简单实现 vuex 数据存储到本地

449 阅读2分钟

为了满足业务需求,需要在刷新页面的时候不丢失已加载过的数据,所以编写了这个插件来满足业务需求(vuex版本:3.6.2)。

使用很简单,只需要在模块文件中定义一个meta数据,就实现了存储功能,会在初始化的时候使用存储的数据,在更新state的时候存储数据。

  // 如果 storage = 'local' 则表示使用 localStorage 存储,否则使用 sessionStorage 存储
  meta: {
    storage: "session",
  },

一个简单的例子

  1. 编写模块文件 store/modules/user.js

业务功能:登录后加载用户信息,登出后清空用户信息

import * as userApi from "@/apis/user";

export default {
  meta: {
    storage: "session",
  },
  namespaced: true,
  state: {
    token: null,
    userInfo: null,
  },
  getters: {
    isLogin(state) {
      return !!state.token;
    },
  },
  mutations: {
    SET_TOKEN(state, data) {
      state.token = data;
    },
    SET_USER_INFO(state, data) {
      state.userInfo = data;
    },
  },
  actions: {
    async getUserInfo(context) {
      const result = await userApi.getUserInfo();
      context.commit("SET_USER_INFO", result.data);
    },
    cleanUserInfo(context) {
      context.commit("SET_USER_INFO", null);
    },
    async login(context, data) {
      const result = await userApi.login(data);
      context.commit("SET_TOKEN", result.data.token);
      context.dispatch("getUserInfo");
    },
    async logout(context) {
      await userApi.logout();
      context.commit("SET_TOKEN", null);
      context.dispatch("cleanUserInfo");
    },
  },
};

  1. 编写store/plugins/storage.js插件

实现原理:根据模块对象中的元数据来判断是否需要做持久化操作,需要则修改模块中的state属性为一个函数,函数的作用就是加载本地数据或者使用默认值,然后再监听模块的state,一旦修改则对数据序列化后进行存储。

// 存储的key
const getStorageKey = (name) => {
  return "__recruit__" + name;
};

// 储存的对象
const getStorage = (storage) => {
  return storage == "local" ? localStorage : sessionStorage;
};

// 初始化的state
export const getState = (name, storage, initialState) => {
  return () => {
    const data = getStorage(storage).getItem(getStorageKey(name));
    return data ? JSON.parse(data) : initialState;
  };
};

// 定义一个创建插件的函数
export default (modules) => {
  // 获取需要存储的模块列表
  const storageList = Object.entries(modules)
    ?.filter(([, module]) => module.meta?.storage)
    ?.map(([name, module]) => ({ name, storage: module.meta.storage }));
  
  // 修改需要存储模块的state值
  storageList?.forEach(({ name, storage }) => {
    modules[name].state = getState(name, storage, modules[name].state);
  });

  return (store) => {
    storageList?.forEach(({ name, storage }) => {
      store.watch(
        (state) => state[name],
        (newValue) => {
          getStorage(storage).setItem(getStorageKey(name), JSON.stringify(newValue));
        },
        {
          immediate: true,
          deep: true,
        }
      );
    });
  };
};

编写 store/index.js 文件

import Vue from "vue";
import vuex from "vuex";
import createStoragePlugin from "./plugins/storage";

Vue.use(vuex);

// 根据modules文件夹生成modules对象
const modulesFiles = require.context("./modules", true, /\.js$/);
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
  const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, "$1");
  const value = modulesFiles(modulePath);
  modules[moduleName] = value.default;
  return modules;
}, {});

// 创建一个存储插件
const storagePlugin = createStoragePlugin(modules);

const store = new vuex.Store({
  modules,
  plugins: [storagePlugin],
});

export default store;

来看看效果吧

iShot_2022-06-30_21.23.48.gif