chrome 扩展中的 vuex 共享状态 [英] vuex shared state in chrome extension

查看:84
本文介绍了chrome 扩展中的 vuex 共享状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有以下 webpack.config.js 的 chrome 扩展:

I have a chrome extension with the following webpack.config.js:

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",
  },

看着

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

https://github.com/xanf/vuex-shared-mutations

围绕浏览器本地存储添加包装器:

Adding a wrapper around browser local storage:

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定义为:

In "./src/js/content/popup/firstpage/store/index.js" vuex store is defined as:

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:

the background script has a listener; src/background/background.js:

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

需要使用共享存储的内容脚本在content.js中有一个入口点:

The content script that needs to make use of the shared store has an entry point in 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()./src/js/content/overlay/index.js 中创建一个 vue 组件:

initOverlay() creates a vue component in ./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 只需要从 store 调用一个mutation(addWhiteListedItem):

Overlay.vue only needs to call a mutation (addWhiteListedItem) from store:

<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 不能看到"?商店的状态?

Why doesn't Overlay.vue "see" the state of 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 是否意味着每个导入 store 的脚本都会获得一个新实例,其默认状态与其他导入不同步?

    Does export default new Vuex.Store mean that every script that imports the store gets a new instance with a default state that is not in sync with other imports?

    推荐答案

    正如错误消息所暗示的那样 itemSectionCategory 无法找到,因为它应该是 allSections[0] 的一个元素.但是,在调用 allSections 之前,您永远不会定义它的索引 0.

    As the error message suggests itemSectionCategory can not be found as it is expected to be an element of allSections[0]. However you never define index 0 of allSections before calling it.

    所以简而言之,您需要在使用之前定义 allSections 索引 0 ,或者将索引部分设为可选并在未找到时创建它.

    So in short you need to either define allSections index 0 before using it, or make the index part optional and create it if it's not found.

    否则,您可以尝试以下解决方案之一:

    Otherwise you could try one of the following solutions:

    1. 如果您需要依赖索引 0 可用,请在调用您的函数之前检查它是否已设置!state.chromePagesState.allSections[0] ?[...插入初始化函数调用...]

    也许可选链可能是另一种解决方案,具体取决于您之后使用它的用途,例如 如何对数组或函数使用可选链接?

    Maybe optional chaining could be another solution depending on what you use it for afterwards, for an example How to use optional chaining with array or functions?

    这篇关于chrome 扩展中的 vuex 共享状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆