手机版

手把手系列 100行代码获取微信小程序全局状态同步

时间:2021-12-04 来源:互联网 编辑:宝哥软件园 浏览:

最近收到一个小程序项目。对于之前只写过一个小工具的我来说,是时候考察一波小程序的基本功了(认真面对)。

手把手系列,100行代码搞定微信小程序全局状态同步(图1)

首先,我了解了各种大神创建小程序的方式。在此之前,有基于vue语法的MPvue,专门用于生成小程序。REAT的JD.COM团队的taro是多语言的,支持REAT语法来生成小程序,H5,react-native.还有官方的wepy,模仿vue语法,官方支持比较稳定.芥末都是:ox:吗?快点研究每一个。

冉阿让——

手把手系列,100行代码搞定微信小程序全局状态同步(图2)

!!翻开issue页,似乎都有几十到上百条的open isuue未解决,同时还有一些诡异的bug夹杂其中,好怕怕。遂放弃......逃

是原生框架,所以它遇到了原生框架最大的问题之一,全局状态同步management/( o )/~ ~。

小程序框架提供了许多现成的组件,这大大提高了我们的开发效率。但是,作为一个不能直接引用js npm包的语法(支持的模式比较繁琐),小程序本身并没有提供redux、vuex这样的全局状态管理工具,这就完全违背了mvc(mvvm)方的一贯风格。

于是我想到手写一个简单的全球状态管理库,从各方面来看似乎都是可行的。毕竟是一个接近vue的框架。

心路历程如上。。。。。。还是不废话了,上主菜(可直接翻到文末查看代码完整版)。

小程序正式提供和推荐的演示是将全局数据放在应用实例上的3354示例。乍一看,它似乎非常接近我们的全局状态管理要求,但这只是一种数据存储方法,根本无法实现响应状态。

想想我们共同的需求,在个人中心页面点击“登录”,跳转到登录页面,测试一些小技巧。最后成功登录后,回到个人中心,他/她还是被一个大大的“登录”按钮嘲讽,于是测试打了你一顿,让你回去加班。

手把手系列,100行代码搞定微信小程序全局状态同步(图3)

此时,您可以使用这个. setData来刷新onShow中的每个页面扩展.前提是你不怕繁琐的工作,愿意消耗更多的性能(性能力)。

所以开始手写吧。第一步是在项目中生成/store/sotre.js文件。

多装两个轮子的常用方法

const _ toString=object . prototype . toString function is function(obj){ return type of obj==' function ' | | false } function isObject(obj){ return _ toString。call(obj)='[object object]' | | false } Copy代码

createStore

全局状态管理当然需要全局状态存储,可以考虑使用re。

act-redux的connect模式做绑定:

let _state = nullfunction connect(mapStateToData, mapMethodTopPage) {    ...}/** * 创建store对象 * * @param { Object } store * @returns { Object } _Store */function createStore(state) {    if (_state) {        console.warn(            'there are multiple store active. This might lead to unexpected results.'        )    }    _state = Object.assign({}, state)    // 这里返回_Store的原因是因为想通过app实例直接获取    // const { connect, setState, createStore } = getApp().Store    return _Store}const _Store = {    connect,    setState,    createStore}module.exports = _Store复制代码

connect

现在的打算是将_state作为内部存储,以免暴露出去被直接操作,无法做到响应式(单一状态树只读原则)。接下来的重点当然是作为绑定数据和修改数据相互响应了,先来connect:

let _state = nulllet _subjects = [] // 用来存储页面实例对象let _observers = [] // 用来存储状态响应器/** * 仿写react-redux的connect简单工厂 * * @param { Function } mapStateToData * @param { Function } mapMethodTopPage * @returns { Function } pageConnect */function connect(mapStateToData, mapMethodTopPage) {    // mapStateToData接收state参数,且必须返回一个绑定对象,key会被绑定到page实例的data中    const dataMap = mapStateToData ? mapStateToData(_state) : {}    // mapMethodTopPage接收setState和state参数,且必须返回一个绑定对象,key会被绑定到page实例上    const methodMap = mapMethodTopPage ? mapMethodTopPage(setState, _state) : {}    return function(pageObject) {        // 接收page对象        // 遍历绑定data        for (let dataKey in dataMap) {            if (pageObject.data) {                if (pageObject.data[dataKey]) {                    console.warn(                        `page class had data ${dataKey}, connect map will cover this prop.`                    )                }                pageObject.data[dataKey] = dataMap[dataKey]            } else {                pageObject.data = {                    [dataKey]: dataMap[dataKey]                }            }        }        // 遍历绑定method        for (let methodKey in methodMap) {            pageObject[methodKey] = methodMap[methodKey]        }        // 存储onLoad、onUnload周期函数,以便对其做改造        const onLoad = pageObject.onLoad        const onUnload = pageObject.onUnload        pageObject.onLoad = function() {            // 存储page实例和事件响应器,两者保持同步,一个实例对应一个响应器            if (!~_subjects.indexOf(this)) {                // 首次load需要修改data                this.setData(mapStateToData ? mapStateToData(_state) : {})                _subjects.push(this)                _observers.push(() => {                    // mapStateToData生成新的mapData,并使用this.setData更新page状态                    this.setData(mapStateToData ? mapStateToData(_state) : {})                })            }            // 触发原有生命周期函数            onLoad && onLoad.call(this)        }        pageObject.onUnload = function() {            // 注销响应器            const index = _subjects.indexOf(this)            if (!~index) {                _subjects.splice(index, 1)                _observers.splice(index, 1)            }            // 触发原有生命周期函数            onUnload && onUnload.call(this)        }        return pageObject    }}复制代码

setState

状态存储和绑定都有了,现在需要一个修改state的方法:

复制代码/** * 所有的state状态修改必须通过setState方法,以完成正常的响应 * * @param { Object | Function } state */function setState(state) {    // state 接收需要更新的state对象或者一个接收state的方法,该方法必须返回一个state更新对象    let newState = state    if (isFunction(state)) {        newState = state(_state)    }    // 合并新状态    _state = Object.assign(_state, newState)    // 触发响应器    _observers.forEach(function(observer) {        isFunction(observer) && observer()    })}

完整的代码

最后加上一些报错信息:

function isFunction(obj) {    return typeof obj === 'function' || false}function isObject(obj) {    return obj.toString() === '[object Object]' || false}let _state = nullconst _subjects = [] // 用来存储页面实例对象const _observers = [] // 用来存储状态响应器/** * 仿写react-redux的connect简单工厂 * * @param { Function } mapStateToData * @param { Function } mapMethodTopPage * @returns { Function } constructorConnect */function connect(mapStateToData, mapMethodTopPage) {    if (mapStateToData !== undefined && !isFunction(mapStateToData)) {        throw new Error(            `connect first param accept a function, but got a ${typeof mapStateToData}`        )    }    if (mapMethodTopPage !== undefined && !isFunction(mapMethodTopPage)) {        throw new Error(            `connect second param accept a function, but got a ${typeof mapMethodTopPage}`        )    }    // mapStateToData接收state参数,且必须返回一个绑定对象,key会被绑定到page实例的data中    const dataMap = mapStateToData ? mapStateToData(_state) : {}    // mapMethodTopPage接收setState和state参数,且必须返回一个绑定对象,key会被绑定到page实例上    const methodMap = mapMethodTopPage ? mapMethodTopPage(setState, _state) : {}    return function(pageObject) {        // 接收page对象        if (!isObject(pageObject)) {            throw new Error(                `page object connect accept a page object, but got a ${typeof pageObject}`            )        }        // 遍历绑定data        for (const dataKey in dataMap) {            if (pageObject.data) {                if (pageObject.data[dataKey]) {                    console.warn(                        `page object had data ${dataKey}, connect map will cover this prop.`                    )                }                pageObject.data[dataKey] = dataMap[dataKey]            } else {                pageObject.data = {                    [dataKey]: dataMap[dataKey]                }            }        }        // 遍历绑定method        for (const methodKey in methodMap) {            if (pageObject[methodKey]) {                console.warn(                    `page object had method ${methodKey}, connect map will cover this method.`                )            }            pageObject[methodKey] = methodMap[methodKey]        }        // 存储onLoad、onUnload周期函数,以便对其做改造        const onLoad = pageObject.onLoad        const onUnload = pageObject.onUnload        pageObject.onLoad = function() {            // 存储page实例和事件响应器,两者保持同步,一个实例对应一个响应器            if (!~_subjects.indexOf(this)) {                // 首次load需要修改data                this.setData(mapStateToData ? mapStateToData(_state) : {})                _subjects.push(this)                _observers.push(() => {                    // mapStateToData生成新的mapData,并使用this.setData更新page状态                    this.setData(mapStateToData ? mapStateToData(_state) : {})                })            }            // 触发原有生命周期函数            onLoad && onLoad.call(this)        }        pageObject.onUnload = function() {            // 注销响应器            const index = _subjects.indexOf(this)            if (!~index) {                _subjects.splice(index, 1)                _observers.splice(index, 1)            }            // 触发原有生命周期函数            onUnload && onUnload.call(this)        }        return pageObject    }}/** * 所有的state状态修改必须通过setState方法,以完成正常的响应 * * @param { Object | Function } state */function setState(state) {    // state 接收需要更新的state对象或者一个接收state的方法,该方法必须返回一个state更新对象    let newState = state    if (isFunction(state)) {        newState = state(_state)    }    // 合并新状态    _state = Object.assign(_state, newState)    // 触发响应器    _observers.forEach(function(observer) {        isFunction(observer) && observer()    })}/** * 创建store对象 * * @param { Object } store * @returns { Object } _Store */function createStore(state) {    if (_state) {        console.warn(            'there are multiple store active. This might lead to unexpected results.'        )    }    _state = Object.assign({}, state)    // 这里返回_Store的原因是因为想通过app实例直接获取    // const { connect, setState, createStore } = getApp().Store    return _Store}const _Store = {    connect,    setState,    createStore}module.exports = _Store复制代码

确实够简单吧,缺点是不支持模块化和component,也没有实现reducer和action,但是这些,我统统都不要 。 考虑现有需求和性能影响,目前没有支持component和模块化state——“小”程序方向靠拢(其实是懒)。

“小程序是一种不需要下载安装即可使用的应用,它实现了应用‘触手可及’的梦想,用户扫一扫或搜一下即可打开应用;也体现了‘用完即走’的理念,用户不用关心是否安装太多应用的问题。应用将无处不在,随时可用,但又无需安装卸载。”

“微信之父”张小龙的这段话确定了小程序的开发基调。鉴于小程序作为Web端的轻应用,本身的特质就决定了它不适合实现太过复杂的功能(为我的懒找到了官方支持)。

版权声明:手把手系列 100行代码获取微信小程序全局状态同步是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。