开始
上一期《微信小程序实现数据侦听器watch》,在小程序中实现了Vue的watch功能。但小程序没有状态管理,目前的状况下,跨页通讯和数据传递非常的蛋疼,会造成难以维护和调试。这次就来实现store,全局状态管理功能,来解决这个蛋疼的问题,有以下几个目标:
- 在Page和Component中所有生命周期和函数中均可使用store,均可修改state
- 少量几行代码即可引入,不修改之前代码结构,不需要每个页面都引入
- 使用方式和vue一样
方案
store其实就是一个全局变量,所有页面都共享,实现store功能有2个关键点
- Page和Component中怎么引用全局变量state,怎么将数据渲染到页面上
- 修改了全局变量state,怎么将其他Page和Component引用的全局变量state也更新并渲染到页面上
第一个问题,在Page的onLoad函数和Component的attached函数里,在options中增加一个对全局变量state的引用,并调用this.setData方法将变量state的数据渲染到页面上
第二个问题,需要维持一个列表,将当前已加载的Page和Component都储存起来,也就是在Page的onLoad函数和Component的attached函数中将当前对象添加到列表中,在Page的onUnload函数和Component的detached函数中将当前对象从列表中移除。然后当有一处调用setState方法时,遍历这个列表中所有对象,调用this.setData方法将变量state的数据渲染到页面上。
下面来看实现代码,写一个store.js,内容如下:
// 全局变量
let store = {
msg: 'xxxxx',
user: {}
}
// 所有已加载的页面和组件
let pcList = []
function initPage() {
let oldPage = Page
Page = function(obj) {
let oldOnLoad = obj.onLoad || function() {}
let oldOnUnload = obj.onUnload || function() {}
obj.onLoad = function() {
// 实现store功能,onLoad的时候将state增加到Page的data中
loadStore.call(this)
// 实现store的setState功能
this.setState = function(obj) {
setState.call(this, obj)
}
oldOnLoad.call(this, ...arguments)
}
obj.onUnload = function() {
// 把this传进function
unloadStore.call(this)
oldOnUnload.call(this, ...arguments)
}
return oldPage(obj)
}
}
function initComponent() {
let oldComponent = Component
Component = function(obj) {
let oldAttached = obj.attached || function() {}
let oldDetached = obj.detached || function() {}
obj.attached = function() {
// 实现store功能,onLoad的时候将state增加到Component的data中
loadStore.call(this)
// 实现store的setState功能
this.setState = function(obj) {
setState.call(this, obj)
}
oldAttached.call(this, ...arguments)
}
obj.detached = function() {
// 把this传进function
unloadStore.call(this)
oldDetached.call(this, ...arguments)
}
return oldComponent(obj)
}
}
function setState(obj) {
let keys = Object.keys(obj)
let newObj = {}
keys.forEach(key => {
newObj['$state.' + key] = obj[key]
})
// 更新所有的组件和页面
pcList.forEach(item => {
item.setData(newObj)
})
}
function loadStore() {
// 加入pcList,待setState的时候遍历
pcList.push(this)
// 每个页面或组件都在data中储存一份state的引用
this.$state = store
this.setData({
$state: store
})
}
function unloadStore() {
// 从pcList中移除
let index = pcList.findIndex(item => {
return item === this
})
if (index > -1) {
pcList.splice(index, 1)
}
}
initPage()
initComponent()
使用
首先是引入,在app.js引入store.js即可
import './utils/store.js'
然后使用方式,在wxml中使用
<view>{
{$state.msg}}</view>
在js中获取state和更新state
Page({
//...
func(){
// 获取state中的user
let storeUser = this.$state.user
storeUser.name = 'Becky'
// 更新state中的user
this.setState({
user:storeUser
})
}
})
结语
我们实现了store功能,并实现了开头写得三个目标,所有生命周期和函数中均可使用,一行代码即可引入,使用方式和Vue一样。
不过只是实现了最基础的store功能,但能满足目前的项目需要,若以后有其他的需求,再在这个基础上增加。