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

时间:2018-03-17 20:44:19

标签: javascript vue.js vuex firefox-webextensions

我正在尝试在网络扩展程序中使用共享的vue.js状态。

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

首次尝试

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

background.js

var store = {
  count: 0
};

popup.js

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

    var vue = new Vue({
        el: '#app',
        data: {
            state: store
        },
    })
})

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>

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

第二次尝试

background.js

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

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')
            },
        }
    });
})

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>

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

当使用相同的代码但是在popup.js中声明了商店时,代码按预期工作,因此这应该可行,但由于某种原因它不会

我的问题:

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

2 个答案:

答案 0 :(得分:2)

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

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

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

store.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)
      })
    }
  }
});

background.js

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

popup.js

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

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

答案 1 :(得分:2)

对不起,我为它创建了一个节点模块:

https://github.com/MitsuhaKitsune/vuex-webextensions

该模块使用webextensions消息传递API来同步webextension上的所有商店实例。

安装就像另一个vuex插件,您可以在自述文件中查看。

如果您有任何问题或反馈,请在此处或github问题上告诉我。