原生微信小程序开发中 redux 的使用详解


Posted in Javascript onFebruary 18, 2021

前提

复杂场景中有不少数据需要在多个不同页面间来回使用和修改。但是小程序页面直接的数据通信方式十分的简单。通常情况需要自己维护一个全局的对象来存放共有数据。但是,简单的维护一个共有数据实体,会随着业务逻辑的不断复杂化而变的过分庞大,并且数据的修改往往无法很好的溯源。加之公共数据实体中数据的修改和页面的UI之间没有太好的同步手段,往往需要在页面和对应的数据实体中同时都维护一份相同的数据,操作十分的不方便。

之前使用过Taro以react+redux的结构来开发微信小程序,依托redux整体上可以解决上述的问题。但是Taro本身也有着一些让人无法接受的潜在问题。本着能用原生就绝不使用第三方二次封装的库的原则。一直想尝试一下在原生微信小程序开发中接入redux。

需要解决的问题

1、redux库的接入
2、页面UI与redux数据的绑定

redux库的引入

1、redux的安装,使用 npm与yarn 都可以。
具体到redux中文官网如下:https://www.reduxjs.cn/introduction/getting-started/
2、微信小程序引入外部npm包。
使用微信小程序IDEA,tools 中的 Build npm,生成miniprogram_npm。

原生微信小程序开发中 redux 的使用详解

3、redux库ReferenceError: process is not defined报错的解决。

原生微信小程序开发中 redux 的使用详解

因为微信小程序Build npm工具,构建时不会引入nodeprocess环境变量,但是redux对不同env做了对应的优化。所以导致构建出来的包缺失process变量。最便捷的解决方法是在构建完成的包中自己注入需要的process。

原生微信小程序开发中 redux 的使用详解

这样基本可以解决所有第三方库遇到的process参数缺失的问题。如果每次运行Build npm工具后都需要手动修改。如果有多个第三方库需要手动修改,那就很麻烦。所以很有必要通过脚本,使用ast树等工具完整动态修改,节省人力成本(这个后续介绍)

综上,redux的引入就完成了。

在项目中添加redux

1、store的创建

使用combineReducers合并不同的实体,使用createStore创建store实体,并导出。为了数据的统一性,redux的原则是一个项目只初始化一个store,所以后续任何的操作都是在当前生成的store中进行。

合并数据实体:

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
}

导出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、全局维护store

这里和react中的使用方法不同。微信小程序没有对应的控件来全局维护store,所以我的做法是直接在,app.js的globalData中维护,这样每个页面都可以直接获取到store
app.js:

const { store } = require('./redux/index');

//app.js
App({
 globalData: {
  $store: store,
  getState: ()=> store.getState(),
 }
})

模拟connect方法

在react中,connect方法是通过高阶组件的方式实现的,但是这个方法并不适用微信小程序。好在redux有提供subscribe方法来监听store中数据的变化。所以初步设计:
1、每当页面计入或显示的时候,添加监听,页面隐藏或销毁时销毁监听
2、添加完监听后,模拟 mapState 方法,把对应 redux 中的数据注入到页面的data中
3、当监听到redux中数据变化时,更新页面data,从而实现页面UI刷新
4、模拟mapDispatch方法,为页面提供修改store数据的方法

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);
  },
  // ----------------
  // 清空监听
  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;
  },
  // 监听 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 中主要考虑和不足 如下问题:
1、为了保持微信小程序原有生命周名称不变,所以事先劫持了传入页面的生命周期,然后用bind重新在对应生命周期完成后触发。
2、因为redux更新数据,都会生成一个新的数据对象,所以每当监听到数据变化,新数据和老数据会进行对比,每次setData,只放入确实发生变化的数据
3、页面中的data,既维护了默认页面创建的data数据,又加入了redux connect 后的数据,但是目前没有对这个两个数据的命名进行安全的区分,所以页面原生data中的数据名称必须与 connect 注入的数据不同。

测试页面:

导入了testItem, testItem2两个数据,导入了add2一个方法

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);

到此这篇关于原生微信小程序开发中 redux 的使用详解的文章就介绍到这了,更多相关小程序 redux使用内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木! 

Javascript 相关文章推荐
javascript编程起步(第二课)
Feb 27 Javascript
JQuery魔力之$("tagName")与selector
Mar 05 Javascript
由点击页面其它地方隐藏div所想到的jQuery的delegate
Aug 29 Javascript
jquery(hide方法)隐藏指定元素实例
Nov 11 Javascript
JavaScript onkeydown事件入门实例(键盘某个按键被按下)
Oct 17 Javascript
jQuery Ajax传值到Servlet出现乱码问题的解决方法
Oct 09 Javascript
jQuery中get方法用法分析
Dec 07 Javascript
获取select的value、text值的简单示例(jquery与javascript)
Dec 07 Javascript
Bootstrap源码解读表单(2)
Dec 22 Javascript
BootStrap 导航条实例代码
May 18 Javascript
微信小程序如何再次获取用户授权的方法
May 10 Javascript
vue将后台数据时间戳转换成日期格式
Jul 31 Javascript
vue登录页实现使用cookie记住7天密码功能的方法
Feb 18 #Vue.js
Vue包大小优化的实现(从1.72M到94K)
Feb 18 #Vue.js
Vue如何实现变量表达式选择器
Feb 18 #Vue.js
WebStorm无法正确识别Vue3组合式API的解决方案
Feb 18 #Vue.js
JavaScript实现手风琴效果
Feb 18 #Javascript
代码块高亮可复制显示js插件highlight.js+clipboard.js整合
Feb 15 #Javascript
node.js 基于 STMP 协议和 EWS 协议发送邮件
Feb 14 #Javascript
You might like
使用Linux五年积累的一些经验技巧
2013/06/20 PHP
PHP实现数组递归转义的方法
2014/08/28 PHP
php实现的数字验证码及数字运算验证码
2015/07/30 PHP
php自定义函数实现汉字转换utf8编码的方法
2016/09/29 PHP
javascript StringBuilder类实现
2008/12/22 Javascript
jquery实现网站超链接和图片提示效果
2013/03/21 Javascript
javascript中处理时间戳为日期格式的方法
2014/01/02 Javascript
网站接入QQ登录的两种方法
2014/07/22 Javascript
jQuery元素的隐藏与显示实例
2015/01/20 Javascript
JavaScript+CSS实现的可折叠二级菜单实例
2016/02/29 Javascript
PhotoSwipe异步动态加载图片方法
2016/08/25 Javascript
浅析如何利用JavaScript进行语音识别
2016/10/27 Javascript
微信小程序 css使用技巧总结
2017/01/09 Javascript
原生javascript移动端滑动banner效果
2017/03/10 Javascript
详解基于vue-router的动态权限控制实现方案
2017/09/28 Javascript
Vue实现微信支付功能遇到的坑
2019/06/05 Javascript
JS实现的字符串数组去重功能小结
2019/06/17 Javascript
如何提升vue.js中大型数据的性能
2019/06/21 Javascript
[07:25]DOTA2-DPC中国联赛2月5日Recap集锦
2021/03/11 DOTA
Python实现远程调用MetaSploit的方法
2014/08/22 Python
Python构造自定义方法来美化字典结构输出的示例
2016/06/16 Python
Python安装第三方库及常见问题处理方法汇总
2016/09/13 Python
python使用RNN实现文本分类
2018/05/24 Python
Python基于plotly模块实现的画图操作示例
2019/01/23 Python
对python条件表达式的四种实现方法小结
2019/01/30 Python
查看Python依赖包及其版本号信息的方法
2019/08/13 Python
keras 获取某层输出 获取复用层的多次输出实例
2020/05/23 Python
python中pdb模块实例用法
2021/01/15 Python
税务专业毕业生自荐信
2013/11/10 职场文书
夜大毕业生自我评价分享
2013/11/10 职场文书
英文自荐信格式
2013/11/28 职场文书
大学生的创业计划书就该这么写
2014/01/30 职场文书
主持人大赛开场白
2015/05/29 职场文书
2016年九九重阳节活动总结
2016/04/01 职场文书
Python 详解通过Scrapy框架实现爬取百度新冠疫情数据流程
2021/11/11 Python
win11开机发生死循环重启怎么办?win11开机发生死循环重启解决方法
2022/08/05 数码科技