小程序全局状态管理缘由使用vue的朋友可能用过vuex,使用react的朋友可能用redux,好处自然不用多说,用过的都说好。微信小程序,我去年就曾接触过,那时候小程序连组件的概念都没有,现在小程序似乎更火了,也增加了很多新概念,包括自定义组件。小程序的入口文件app.js里会调用App()方法创建一个应用程序实例,有一些公共的、全局的数据可以保存在app.globalData属性里,但是globalData并不具备响应式功能,数据变化时,不会自动更新视图。多个页面或者组件共享同一状态时,处理起来也是相当麻烦的。所以,我花了一点时间,简单实现了一个适用于小程序的状态管理。demoapp.js//app.jsimport store from ‘./store/index’;// 创建appApp(store.createApp({ globalData: { userInfo: {}, todos: [{ name: ‘刷牙’, done: true }, { name: ‘吃饭’, done: false }] } // …其他}))复制代码pages/index/index.wxml<view class=”container”> <view> {{title}} </view> <view class=”info”> <view> 姓名:{{userInfo.name}} </view> <view> 年龄:{{userInfo.age}} </view> </view> <button type=”button” bindtap=”addTodo”>增加todo</button> <!– todos组件 –> <todos /></view>复制代码pages/index/index.js//index.jsimport store from ‘../../store/index’;// 创建页面Page(store.createPage({ data: { title: ‘用户信息页’ }, // 依赖的全局状态属性 这些状态会被绑定到data上 globalData: [‘userInfo’, ‘todos’], // 这里可以定义需要监听的状态,做一些额外的操作,this指向了当前页面实例 watch: { userInfo(val) { console.log(‘userInfo更新了’, val, this); } }, onLoad() { this.getUserInfo().then(userInfo => { // 通过dispatch更新globalData数据 store.dispatch(‘userInfo’, userInfo); }) }, // 模拟从服务端获取用户信息 getUserInfo() { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ name: ‘小明’, age: 20 }) }, 100) }) }, // 增加todo addTodo() { // 注意:这里从this.data上获取todos而不是globalData const todos = […this.data.todos, { name: ‘学习’, donw: false }]; store.dispatch(‘todos’, todos); } // 其他…}))复制代码components/todos/index.wxml<view class=”container”> <view class=”todos”> <view wx:for=”{{todos}}” wx:key=”{{index}}”> <view>{{item.name}}</view> <view>{{item.done?’已完成’:’未完成’}}</view> </view> </view></view>复制代码components/todos/index.jsimport store from ‘../../store/index’;// 创建组件Component(store.createComponent({ // 依赖的全局状态属性 globalData: [‘todos’], // 这里可以定义需要监听的状态,做一些额外的操作,this指向了当前组件实例 watch: { todos(val) { console.log(‘todos更新了’, val, this); // 做其他事。。。 } } // 其他…}))复制代码点击”增加todo”之前点击”增加todo”之后实现原理是不是很神奇,全是代码中那个store的功劳。原理其实非常简单,实现起来也就100+行代码。主要就是使用观察者模式(或者说是订阅/分发模式),通过dispatch方法更新数据时,执行回调,内部调用this.setData方法更新视图。不多说,直接贴一下源码。codestore/observer.jsconst events = Symbol(‘events’);class Observer { constructor() { this[events] = {}; } on(eventName, callback) { this[events][eventName] = this[events][eventName] || []; this[events][eventName].push(callback); } emit(eventName, param) { if (this[events][eventName]) { this[events][eventName].forEach((value, index) => { value(param); }) } } clear(eventName) { this[events][eventName] = []; } off(eventName, callback) { this[events][eventName] = this[events][eventName] || []; this[events][eventName].forEach((item, index) => { if (item === callback) { this[events][eventName].splice(index, 1); } }) } one(eventName, callback) { this[events][eventName] = [callback]; }}const observer = new Observer();export { Observer, observer}复制代码store/index.jsimport { Observer} from ‘./observer’const bindWatcher = Symbol(‘bindWatcher’);const unbindWatcher = Symbol(‘unbindWatcher’);class Store extends Observer { constructor() { super(); this.app = null; } // 创建app createApp(options) { const { onLaunch } = options; const store = this; options.onLaunch = function (…params) { store.app = this; if (typeof onLaunch === ‘function’) { onLaunch.apply(this, params); } } return options; } // 创建页面 createPage(options) { const { globalData = [], watch = {}, onLoad, onUnload } = options; const store = this; // 保存globalData更新回调的引用 const globalDataWatcher = {}; // 保存watch监听回调的引用 const watcher = {}; // 劫持onLoad options.onLoad = function (…params) { store[bindWatcher](globalData, watch, globalDataWatcher, watcher, this); if (typeof onLoad === ‘function’) { onLoad.apply(this, params); } } // 劫持onUnload options.onUnload = function () { store[unbindWatcher](watcher, globalDataWatcher); if (typeof onUnload === ‘function’) { onUnload.apply(this); } } delete options.globalData; delete options.watch; return options; } // 创建组件 createComponent(options) { const { globalData = [], watch = {}, attached, detached } = options; const store = this; // 保存globalData更新回调的引用 const globalDataWatcher = {}; // 保存watch监听回调的引用 const watcher = {}; // 劫持attached options.attached = function (…params) { store[bindWatcher](globalData, watch, globalDataWatcher, watcher, this); if (typeof attached === ‘function’) { attached.apply(this, params); } } // 劫持detached options.detached = function () { store[unbindWatcher](watcher, globalDataWatcher); if (typeof detached === ‘function’) { detached.apply(this); } } delete options.globalData; delete options.watch; return options; } // 派发一个action更新状态 dispatch(action, payload) { this.app.globalData[action] = payload; this.emit(action, payload); } /** * 1. 初始化页面关联的globalData并且监听更新 * 2. 绑定watcher * @param {Array} globalData * @param {Object} watch * @param {Object} globalDataWatcher * @param {Object} watcher * @param {Object} instance 页面实例 */ [bindWatcher](globalData, watch, globalDataWatcher, watcher, instance) { const instanceData = {}; for (let prop of globalData) { instanceData[prop] = this.app.globalData[prop]; globalDataWatcher[prop] = payload => { instance.setData({ [prop]: payload }) } this.on(prop, globalDataWatcher[prop]); } for (let prop in watch) { watcher[prop] = payload => { watch[prop].call(instance, payload); } this.on(prop, watcher[prop]) } instance.setData(instanceData); } /** * 解绑watcher与globalDataWatcher * @param {Object} watcher * @param {Object} globalDataWatcher */ [unbindWatcher](watcher, globalDataWatcher) { // 页面卸载前 解绑对应的回调 释放内存 for (let prop in watcher) { this.off(prop, watcher[prop]); } for (let prop in globalDataWatcher) { this.off(prop, globalDataWatcher[prop]) } }}export default new Store()复制代码具体的代码就不解释了,源码里也有基本的注释。目前实现的功能不算多,基本上能用了,如果业务上需求更高了,再进行拓展。以上,希望能给一些朋友一点点启发,顺便点个赞哦,嘻嘻!
小程序全局状态管理
未经允许不得转载:E企盈小程序开发-热线:4006-838-530 » 小程序全局状态管理
最新评论
不错的小程序案例
优秀的团队,不错的服务!
讲的很好
主播长的帅气
好系统好服务
优秀的团队
好服务,值得信赖
不错的服务