我有一个带有以下 webpack.config.js 的 chrome 扩展:
module.exports = {
mode,
entry: {
"content/content": [
"./src/js/content/content.js",
"./src/js/store.js",
"./src/js/content/overlay/style.scss",
],
"background/background": [
"./src/js/background/utils.js",
"./src/js/background/background.js",
],
"overlay/overlay": "./src/js/content/overlay/index.js",
"popup/popup": "./src/js/content/popup/index.js",
},
看着
https://github.com/xanf/vuex-shared-mutations
在浏览器本地存储周围添加一个包装器:
browserStore.js
import browser from "@/js/browser";
export function getStorageValue(payload) {
return new Promise((resolve) => {
browser.storage.local.get(payload, (items) => {
if (items) {
resolve(items);
}
});
});
}
export function setStorageValue(payload) {
return new Promise((resolve) => {
browser.storage.local.set(payload, (value) => {
resolve(value);
});
});
}
在"./src/js/content/popup/firstpage/store/index.js"vuex store 中定义为:
import Vue from "vue";
import Vuex from "vuex";
import "es6-promise/auto";
import createMutationsSharer from "vuex-shared-mutations";
import dummyData from "./dummyData";
import { getStorageValue, setStorageValue } from "@/js/store";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
chromePagesState: {
allSections: [],
},
},
getters: {
...
},
mutations: {
setChromePagesState(state, value) {
...
},
// this function is to be called from a content script
addWhiteListedItem(state, item) {
// state not initialized here
state.chromePagesState.allSections[0].itemSectionCategory[0].tasks.splice(
0,
0,
item
);
},
...
}
actions: {
async saveChromePagesState({ state }) {
// Save only needed fields
let data = {
...
};
await setStorageValue({ inventoryData: JSON.stringify(data) });
},
async loadChromePagesState({ commit }) {
const json = await getStorageValue("inventoryData");
// json always an empty object
commit(
"setChromePagesState",
Object.keys(json).length === 0 && json.constructor === Object
? json
: dummyData
);
},
async loadChromePagesStateBrowser({ commit }) {
browser.runtime
.sendMessage({ type: "storeinit", key: "chromePagesState" })
.then(async (chromePagesState) => {
const json = await getStorageValue("inventoryData");
commit(
"setChromePagesState",
Object.keys(json).length === 0 && json.constructor === Object
? json
: dummyData
);
});
},
plugins: [
createMutationsSharer({
predicate: [
"addWhiteListedItem",
"loadChromePagesState",
"loadChromePagesStateBrowser",
],
}),
],
},
后台脚本有一个监听器;src/background/background.js:
browser.runtime.onMessage.addListener((message, sender) => {
if (message.type === "storeinit") {
return Promise.resolve(store.state[message.key]);
}
});
需要使用共享存储的内容脚本有一个入口点content.js:
import { initOverlay } from '@/js/content/overlay';
import browser from '@/js/browser';
browser.runtime.onMessage.addListener(function (request, _sender, _callback) {
// vue component gets created here:
if (request && request.action === 'show_overlay') {
initOverlay();
}
return true; // async response
});
initOverlay()在以下位置创建一个 vue 组件./src/js/content/overlay/index.js:
import Vue from "vue";
import Overlay from "@/js/content/overlay/Overlay.vue";
import browser from "@/js/browser";
import { getStorageValue } from "@/js/store";
import store from "../popup/firstpage/store";
Vue.prototype.$browser = browser;
export async function initOverlay(lockScreen = defaultScreen, isPopUp = false) {
...
setVueOverlay(overlayContainer, cover);
...
}
function setVueOverlay(overlayContainer, elem) {
if (!elem.querySelector("button")) {
elem.appendChild(overlayContainer);
elem.classList.add("locked");
new Vue({
el: overlayContainer,
store,
render: (h) => h(Overlay, { props: { isPopUp: isPopUp } }),
});
}
}
Overlay.vue只需addWhiteListedItem要从 store 中调用一个 mutation ():
<template>
<button
@click="addToWhiteList()"
>White list!</button
>
</template>
<script>
import { mapState, mapMutations } from "vuex";
export default {
data() {
return {
};
},
computed: mapState(["chromePagesState"]),
methods: {
...mapMutations(["addWhiteListedItem"]),
addToWhiteList() {
console.log("addToWhiteList()");
let newItem = {
...
};
// store not defined fails with:
Uncaught TypeError: Cannot read property 'itemSectionCategory' of undefined
at Store.addWhiteListedItem (index.js:79)
at wrappedMutationHandler (vuex.esm.js:853)
at commitIterator (vuex.esm.js:475)
at Array.forEach (<anonymous>)
at eval (vuex.esm.js:474)
at Store._withCommit (vuex.esm.js:633)
at Store.commit (vuex.esm.js:473)
at Store.boundCommit [as commit] (vuex.esm.js:418)
at VueComponent.mappedMutation (vuex.esm.js:1004)
at eval (Overlay.vue?./node_modules/vue-loader/lib??vue-loader-options:95)
this.addWhiteListedItem(newItem);
}, 1500);
},
},
};
</script>
为什么Overlay.vue看不到 store 的状态?
流动:
- 启用扩展将内容脚本注入页面
- 内容脚本导入存储对象(尚未初始化)
- 单击弹出窗口(/新选项卡)时,popup.js 会向后台脚本发送一条消息,该脚本还导入存储并调用突变(初始化状态):
background.js
import store from "../content/popup/firstpage/store";
browser.runtime.onMessage.addListener((message, sender) => {
console.log("in background");
if (message.type === "storeinit") {
console.log("got storeinit message. Message key: ", message.key);
store.dispatch("loadChromePagesState");
console.log("current state in store:", JSON.stringify(store.state));
console.log(
"store.state[message.key]:",
JSON.stringify(store.state[message.key])
);
return Promise.resolve(store.state[message.key]);
}
});
- 现在应该初始化商店的状态并且可以从内容脚本调用突变(vue-shared-mutations 保证它)
是否export default new Vuex.Store意味着导入商店的每个脚本都会获得一个具有与其他导入不同步的默认状态的新实例?