Web扩展中的共享vuex状态(死对象问题)

mic*_*el9 5 javascript vue.js firefox-addon-webextensions vuex

我正在尝试在Web扩展中使用共享的vue.js状态。

状态存储在后台脚本的DOM中,并在弹出页面中呈现。

第一次尝试

我的第一次尝试是使用没有vuex的简单存储:

background.js

var store = {
  count: 0
};
Run Code Online (Sandbox Code Playgroud)

popup.js

browser.runtime.getBackgroundPage().then(bg => {
    var store = bg.store;

    var vue = new Vue({
        el: '#app',
        data: {
            state: store
        },
    })
})
Run Code Online (Sandbox Code Playgroud)

popup.html

<div id="app">
  <p>{{ state.count }}</p>
  <p>
    <button @click="state.count++">+</button>
  </p>
</div>
<script src="vue.js"></script>
<script src="popup.js"></script>
Run Code Online (Sandbox Code Playgroud)

这在第一次打开弹出窗口时起作用(您可以增加计数器并更新值),但是在第二次打开弹出窗口时,渲染将失败[Vue warn]: Error in render: "TypeError: can't access dead object"。这似乎是由于以下事实造成的:第一个弹出窗口中的vue.js实例store通过设置自己的getter / setter 修改了,自从第一个弹出窗口关闭以来,该实例已被删除,从而使共享状态不可用。这似乎是不可避免的,所以我决定尝试一下vuex。

第二次尝试

background.js

var store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment: state => state.count++,
  }
})
Run Code Online (Sandbox Code Playgroud)

popup.js

browser.runtime.getBackgroundPage().then(bg => {
    var store = bg.store;

    var vue = new Vue({
        el: '#app',
        computed: {
            count () {
                return store.state.count
            }
        },
        methods: {
            increment () {
                store.commit('increment')
            },
        }
    });
})
Run Code Online (Sandbox Code Playgroud)

popup.html

<div id="app">
  <p>{{ count }}</p>
  <p>
    <button @click="increment">+</button>
  </p>
</div>
<script src="vue.js"></script>
<script src="popup.js"></script>
Run Code Online (Sandbox Code Playgroud)

不幸的是,这也不起作用。您可以打开弹出窗口并查看当前的计数器值,但是增加它不会更新视图(您需要重新打开弹出窗口以查看新值)。

当采用相同的代码但在popup.js中声明了存储时,代码按预期工作,因此应该可以工作,但是由于某些原因,它不起作用

我的问题:

  • vue.js根本无法处理此用例吗?
  • 如果是这样,其他框架(角度,反应...)在这里可以工作吗?

Duh*_*uis 2

这不起作用,因为你的 vue 实例在后台和弹出窗口中不一样。因此,当您从后台获取状态时,状态上的观察者会对后台页面内的 Vue 做出反应,而不是弹出窗口中的视图。您可以通过在后台和弹出窗口中使用相同的商店并同步两者之间的状态来实现这一点。要同步状态,您可以使用优秀的插件vuex-shared-mutations,它使用 localStorage 通过存储的不同实例传播突变。在您的商店中,添加

import createMutationsSharer from 'vuex-shared-mutations'
//...
export default new Vuex.Store({
   //...
   plugins: [createMutationsSharer({ predicate: ['increment'] })],
});
Run Code Online (Sandbox Code Playgroud)

现在,您的弹出窗口正在对按钮做出反应,并且您的背景会增加。如果您重新打开弹出窗口,计数将为 0,因为您创建了一个新商店。您现在需要在初始化弹出窗口时加载初始状态:

商店.js:

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment: state => state.count++,
    setCount (state, count) {
      state.count = count
    },
  },
  plugins: [createMutationsSharer({ predicate: ['increment'] })],
  actions: {
    getCount ({ commit }) {
      browser.runtime.sendMessage({type: "storeinit", key: "count"}).then(count => {
        commit('setCount', count)
      })
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

背景.js

import store from './store';
browser.runtime.onMessage.addListener((message, sender) => {
    if (message.type === 'storeinit') {
        return Promise.resolve(store.state[message.key]);
    }
});
Run Code Online (Sandbox Code Playgroud)

弹出窗口.js

import store from '../store';
var vue = new Vue({
  //...
  created () {
    this.$store.dispatch('getCount')
  }
});
Run Code Online (Sandbox Code Playgroud)

这些困难与 vue 无关,react 用户使用代理在浏览器扩展中传播状态:react-chrome-redux