利用Dectorator分模块存储Vuex状态的实现(2)

完成了setState修饰器功能以后,就可以装饰action方法了,这样等action返回的promise状态修改为fulfilled后调用storage的存储功能,及时保存数据状态以便在新开Weex页面加载最新数据。

store.js

import setState from './decorator'; const module = { state: () => ({}), actions: { @setState someAction() {/** 业务代码 **/ }, }, };

3.2、读取module数据

完成了存储数据到storage以后,我们还需要在新开的Weex页面实例能自动读取数据并初始化Vuex的状态。在这里,我们使用Vuex的plugins设置来完成这个功能。

首先我们先编写Vuex的plugin:

plugin.js

import storage from './storage'; import {rootKey} from './constant'; const parseJSON = (str) => { try { return str ? JSON.parse(str) : undefined; } catch(e) {} return undefined; }; const getState = (store) => { const getStateData = async function getModuleState(module, path = []) { const {_children} = module; // 根据path读取当前module下存储在storage里的数据 const data = parseJSON(await storage.getItem(`${path.join('https://www.jb51.net/')}/`)) || {}; const children = Object.entries(_children); if (!children.length) { return data; } // 剔除childModule的数据,递归读取 const childModules = await Promise.all( children.map(async ([childKey, child]) => { return [childKey, await getModuleState(child, path.concat(childKey))]; }) ); return { ...data, ...Object.fromEntries(childModules), } }; // 读取本地数据,merge到Vuex的state const init = getStateData(store._modules.root, [rootKey]).then(savedState => { store.replaceState(merge(store.state, savedState, { arrayMerge: function (store, saved) { return saved }, clone: false, })); }); }; export default getState;

以上就完成了Vuex的数据按照module读取,但Weex的IOS/Andriod中的storage存储是异步的,为防止组件挂载以后发送请求返回的数据被本地数据覆盖,需要在本地数据读取并merge到state以后再调用new Vue,这里我们使用一个简易的interceptor来拦截:

interceptor.js

const interceptors = {}; export const registerInterceptor = (type, fn) => { const interceptor = interceptors[type] || (interceptors[type] = []); interceptor.push(fn); }; export const runInterceptor = async (type) => { const task = interceptors[type] || []; return Promise.all(task); };

这样plugin.js中的getState就修改为:

import {registerInterceptor} from './interceptor'; const getState = (store) => { /** other code **/ const init = getStateData(store._modules.root, []).then(savedState => { store.replaceState(merge(store.state, savedState, { arrayMerge: function (store, saved) { return saved }, clone: false, })); }); // 将promise放入拦截器 registerInterceptor('start', init); };

store.js

import getState from './plugin'; import setState from './decorator'; const rootModule = { state: {}, actions: { @setState someAction() {/** 业务代码 **/ }, }, plugins: [getState], modules: { /** children module**/ } };

app.js

import {runInterceptor} from './interceptor'; // 待拦截器内所有promise返回resolved后再实例化Vue根组件 // 也可以用Vue-Router的全局守卫来完成 runInterceptor('start').then(() => { new Vue({/** other code **/}); });

这样就实现了Weex页面实例化后,先读取storage数据到Vuex的state,再实例化各个Vue的组件,更新各自的module状态。

4、TODO

通过Decorator实现了Vuex的数据分模块存储到storage,并在Store实例化时通过plugin分模块读取数据再merge到state,提高数据存储效率的同时实现与业务逻辑代码的解耦。但还存在一些可优化的点:

1、触发action会将所有module中的所有state全部,只需保存所需状态,避免存储无用数据。

2、对于通过registerModule注册的module,需支持自动读取本地数据。

3、无法通过_modulesNamespaceMap获取namespaced为false的module,需改为遍历_children。

在此不再展开,将在后续版本中实现。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/948b97cfe485d7a8dcd6593d85811cce.html