原生微信小程序開(kāi)發(fā)中 redux 的使用詳解
前提
復(fù)雜場(chǎng)景中有不少數(shù)據(jù)需要在多個(gè)不同頁(yè)面間來(lái)回使用和修改。但是小程序頁(yè)面直接的數(shù)據(jù)通信方式十分的簡(jiǎn)單。通常情況需要自己維護(hù)一個(gè)全局的對(duì)象來(lái)存放共有數(shù)據(jù)。但是,簡(jiǎn)單的維護(hù)一個(gè)共有數(shù)據(jù)實(shí)體,會(huì)隨著業(yè)務(wù)邏輯的不斷復(fù)雜化而變的過(guò)分龐大,并且數(shù)據(jù)的修改往往無(wú)法很好的溯源。加之公共數(shù)據(jù)實(shí)體中數(shù)據(jù)的修改和頁(yè)面的UI之間沒(méi)有太好的同步手段,往往需要在頁(yè)面和對(duì)應(yīng)的數(shù)據(jù)實(shí)體中同時(shí)都維護(hù)一份相同的數(shù)據(jù),操作十分的不方便。
之前使用過(guò)Taro以react+redux的結(jié)構(gòu)來(lái)開(kāi)發(fā)微信小程序,依托redux整體上可以解決上述的問(wèn)題。但是Taro本身也有著一些讓人無(wú)法接受的潛在問(wèn)題。本著能用原生就絕不使用第三方二次封裝的庫(kù)的原則。一直想嘗試一下在原生微信小程序開(kāi)發(fā)中接入redux。
需要解決的問(wèn)題
1、redux庫(kù)的接入
2、頁(yè)面UI與redux數(shù)據(jù)的綁定
redux庫(kù)的引入
1、redux的安裝,使用 npm與yarn 都可以。
具體到redux中文官網(wǎng)如下:https://www.reduxjs.cn/introduction/getting-started/
2、微信小程序引入外部npm包。
使用微信小程序IDEA,tools 中的 Build npm,生成miniprogram_npm。
3、redux庫(kù)ReferenceError: process is not defined報(bào)錯(cuò)的解決。
因?yàn)槲⑿判〕绦駼uild npm工具,構(gòu)建時(shí)不會(huì)引入nodeprocess環(huán)境變量,但是redux對(duì)不同env做了對(duì)應(yīng)的優(yōu)化。所以導(dǎo)致構(gòu)建出來(lái)的包缺失process變量。最便捷的解決方法是在構(gòu)建完成的包中自己注入需要的process。
這樣基本可以解決所有第三方庫(kù)遇到的process參數(shù)缺失的問(wèn)題。如果每次運(yùn)行Build npm工具后都需要手動(dòng)修改。如果有多個(gè)第三方庫(kù)需要手動(dòng)修改,那就很麻煩。所以很有必要通過(guò)腳本,使用ast樹(shù)等工具完整動(dòng)態(tài)修改,節(jié)省人力成本(這個(gè)后續(xù)介紹)
綜上,redux的引入就完成了。
在項(xiàng)目中添加redux
1、store的創(chuàng)建
使用combineReducers合并不同的實(shí)體,使用createStore創(chuàng)建store實(shí)體,并導(dǎo)出。為了數(shù)據(jù)的統(tǒng)一性,redux的原則是一個(gè)項(xiàng)目只初始化一個(gè)store,所以后續(xù)任何的操作都是在當(dāng)前生成的store中進(jìn)行。
合并數(shù)據(jù)實(shí)體:
const { combineReducers } = require('redux'); const testItem = require('./testItem/index'); const testItem2 = require('./testItem2/index'); const user = require('./user/index'); const reducer = combineReducers({ testItem: testItem.testItem, testItem2, user }); module.exports = { reducer }
導(dǎo)出store:
const { createStore, applyMiddleware } = require('redux'); const { reducer } = require('./reducers'); const { logger } = require('redux-logger'); const store = createStore( reducer, applyMiddleware(logger) ) module.exports = { store }
2、全局維護(hù)store
這里和react中的使用方法不同。微信小程序沒(méi)有對(duì)應(yīng)的控件來(lái)全局維護(hù)store,所以我的做法是直接在,app.js的globalData中維護(hù),這樣每個(gè)頁(yè)面都可以直接獲取到store
app.js:
const { store } = require('./redux/index'); //app.js App({ globalData: { $store: store, getState: ()=> store.getState(), } })
模擬connect方法
在react中,connect方法是通過(guò)高階組件的方式實(shí)現(xiàn)的,但是這個(gè)方法并不適用微信小程序。好在redux有提供subscribe方法來(lái)監(jiān)聽(tīng)store中數(shù)據(jù)的變化。所以初步設(shè)計(jì):
1、每當(dāng)頁(yè)面計(jì)入或顯示的時(shí)候,添加監(jiān)聽(tīng),頁(yè)面隱藏或銷毀時(shí)銷毀監(jiān)聽(tīng)
2、添加完監(jiān)聽(tīng)后,模擬 mapState 方法,把對(duì)應(yīng) redux 中的數(shù)據(jù)注入到頁(yè)面的data中
3、當(dāng)監(jiān)聽(tīng)到redux中數(shù)據(jù)變化時(shí),更新頁(yè)面data,從而實(shí)現(xiàn)頁(yè)面UI刷新
4、模擬mapDispatch方法,為頁(yè)面提供修改store數(shù)據(jù)的方法
pageW.js:
const { store } = require('../redux/index'); const initPage = (params = {}, connect = []) => { const { onLoad = ()=>{}, onShow = ()=>{}, onHide = ()=>{}, onUnload = ()=>{}, data = {} } = params; const newPage = { ...params, // ---------------- OnLoad(...p) { onLoad.bind(this)(...p); }, OnShow(...p) { onShow.bind(this)(...p); }, OnHide(...p) { onHide.bind(this)(...p); }, OnUnload(...p) { onUnload.bind(this)(...p); }, // ---------------- // 清空監(jiān)聽(tīng) clearStoreSubscribe() { if (this.storeSubscribe) { this.storeSubscribe(); this.storeSubscribe = undefined; } }, // 獲取redux 中 data getNewData() { const newItems = {}; const state = this.$store.getState(); if (connect) { if ( Array.isArray(connect) ) { connect.forEach((key) => { const value = state[key]; if (value && this.data[key] !== value) { newItems[key] = value } }) } else if (typeof connect === 'function') { const list = connect(state) || {}; Object.keys(list).forEach((key) => { const value = list[key]; if (value && this.data[key] !== value) { newItems[key] = value } }) } } return newItems; }, // 監(jiān)聽(tīng) redux 變化 handleReduxChange() { this.setData({ ...this.getNewData(), }); }, // ---------------- data: { ...data }, onLoad(...p) { const app = getApp() this.$store = app.globalData.$store; this.setData({ ...this.getNewData(), }); this.OnLoad(...p); this._isOnLoad = true; }, onShow (...p) { if (!this.storeSubscribe) { this.storeSubscribe = this.$store.subscribe(()=>this.handleReduxChange()); } if (!this._isOnLoad) { this.setData({ ...this.getNewData(), }); } this.OnShow(...p); this._isOnLoad = false; }, onHide(...p) { this.OnHide(...p); this.clearStoreSubscribe(); }, onUnload(...p) { this.OnUnload(...p); this.clearStoreSubscribe(); }, // ---------------- dispatch(...p) { if (this.$store) { return this.$store.dispatch(...p); } } } return newPage; } const PageW = (params = {}, mapState = [], mapDispatch = ()=>{}) => { const page = initPage({...params}, mapState); const dispatchList = mapDispatch(store) || {}; page.mapDispatch = { ...dispatchList }; return Page(page); } module.exports = PageW;
PageW 中主要考慮和不足 如下問(wèn)題:
1、為了保持微信小程序原有生命周名稱不變,所以事先劫持了傳入頁(yè)面的生命周期,然后用bind重新在對(duì)應(yīng)生命周期完成后觸發(fā)。
2、因?yàn)閞edux更新數(shù)據(jù),都會(huì)生成一個(gè)新的數(shù)據(jù)對(duì)象,所以每當(dāng)監(jiān)聽(tīng)到數(shù)據(jù)變化,新數(shù)據(jù)和老數(shù)據(jù)會(huì)進(jìn)行對(duì)比,每次setData,只放入確實(shí)發(fā)生變化的數(shù)據(jù)
3、頁(yè)面中的data,既維護(hù)了默認(rèn)頁(yè)面創(chuàng)建的data數(shù)據(jù),又加入了redux connect 后的數(shù)據(jù),但是目前沒(méi)有對(duì)這個(gè)兩個(gè)數(shù)據(jù)的命名進(jìn)行安全的區(qū)分,所以頁(yè)面原生data中的數(shù)據(jù)名稱必須與 connect 注入的數(shù)據(jù)不同。
測(cè)試頁(yè)面:
導(dǎo)入了testItem, testItem2兩個(gè)數(shù)據(jù),導(dǎo)入了add2一個(gè)方法
const PageW = require('../../pageW/index'); const { ActionsFun } = require('../../redux/testItem/actions'); const page = { data: { wwj: 4 }, onLoad() { console.log('sub onLoad'); }, onShow() { }, toTest() { console.log('toTest'); wx.navigateTo({ url: '/pages/test/index' }) }, button1() { console.log('button1'); this.mapDispatch.add2(); }, button2() { const { wwj } = this.data; this.setData({ wwj: wwj + 2 }); }, } const mapState = [ 'testItem', 'testItem2' ]; const mapDispatch = ({dispatch}) => { return { add2: (params) => dispatch(ActionsFun.add(params)) } } PageW(page, mapState, mapDispatch);
到此這篇關(guān)于原生微信小程序開(kāi)發(fā)中 redux 的使用詳解的文章就介紹到這了,更多相關(guān)小程序 redux使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
echarts加載區(qū)域地圖并標(biāo)注點(diǎn)的簡(jiǎn)單步驟
這篇文章提供了南海區(qū)域地圖加載以及氣象站點(diǎn)標(biāo)注的詳細(xì)步驟,首先通過(guò)DataV.GeoAtlas地理小工具下載南海區(qū)域的JSON地圖文件,接著使用Echarts進(jìn)行地圖的展示,可以選擇是否顯示省下所有市的名字,需要的朋友可以參考下2024-09-09javascript實(shí)現(xiàn)blob加密視頻源地址的方法
這篇文章主要介紹了javascript實(shí)現(xiàn)blob加密視頻源地址的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08微信小程序?qū)崿F(xiàn)兩邊小中間大的輪播效果的示例代碼
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)兩邊小中間大的輪播效果的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-12-12js實(shí)現(xiàn)表單及時(shí)驗(yàn)證功能 用戶信息立即驗(yàn)證
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)表單及時(shí)驗(yàn)證功能,在輸入后就可以立即驗(yàn)證,含用戶類型,性別,愛(ài)好等驗(yàn)證,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09用JavaScrpt實(shí)現(xiàn)文件夾簡(jiǎn)單輕松加密的實(shí)現(xiàn)方法圖文
電腦里經(jīng)常會(huì)存儲(chǔ)著重要文件,這些文件需要進(jìn)行加密,有許多方法來(lái)實(shí)現(xiàn)。但如果想對(duì)一個(gè)文件夾里的所有文件都進(jìn)行加密,數(shù)量少還可以,要是數(shù)量多豈不是得把人累死?2008-09-09