原生微信小程序开发中 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 相关文章推荐
JS下高效拼装字符串的几种方法比较与测试代码
Apr 15 Javascript
js 替换功能函数,用正则表达式解决,js的全部替换
Dec 08 Javascript
Javascript图像处理—亮度对比度应用案例
Jan 03 Javascript
JS实现随机数生成算法示例代码
Aug 08 Javascript
解析JavaScript中delete操作符不能删除的对象
Dec 03 Javascript
《JavaScript DOM 编程艺术》读书笔记之JavaScript 简史
Jan 09 Javascript
JavaScript通过function定义对象并给对象添加toString()方法实例分析
Mar 23 Javascript
微信小程序 数据访问实例详解
Oct 08 Javascript
Angular中$state.go页面跳转并传递参数的方法
May 09 Javascript
Node.JS更改Windows注册表Regedit的方法小结
Aug 18 Javascript
原生JS实现列表内容自动向上滚动效果
May 22 Javascript
如何用JavaScript学习算法复杂度
Apr 30 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
php中apc缓存使用示例
2013/12/25 PHP
PHP批量删除、清除UTF-8文件BOM头的代码实例
2014/04/14 PHP
在Mac OS上自行编译安装Apache服务器和PHP解释器
2015/12/24 PHP
PHP  实现等比压缩图片尺寸和大小实例代码
2016/10/08 PHP
PHP大文件分块上传功能实例详解
2019/07/22 PHP
thinkphp框架类库扩展操作示例
2019/11/26 PHP
js中更短的 Array 类型转换
2011/10/30 Javascript
firebug的一个有趣现象介绍
2011/11/30 Javascript
无刷新预览所选择的图片示例代码
2014/04/02 Javascript
express的中间件bodyParser详解
2014/12/04 Javascript
轻松创建nodejs服务器(3):代码模块化
2014/12/18 NodeJs
JS实现点击文字对应DIV层不停闪动效果的方法
2015/03/02 Javascript
js实现消息滚动效果
2017/01/18 Javascript
js 数据存储和DOM编程
2017/02/09 Javascript
vue实现登陆登出的实现示例
2017/09/15 Javascript
实现图片首尾平滑轮播(JS原生方法—节流)
2017/10/17 Javascript
JavaScript折半查找(二分查找)算法原理与实现方法示例
2018/08/06 Javascript
jQuery实现的页面弹幕效果【测试可用】
2018/08/17 jQuery
原生JS实现的简单小钟表功能示例
2018/08/30 Javascript
多个vue子路由文件自动化合并的方法
2019/09/03 Javascript
p5.js临摹动态图形实现方法详解
2019/10/23 Javascript
Python常用正则表达式符号浅析
2014/08/13 Python
修改Python的pyxmpp2中的主循环使其提高性能
2015/04/24 Python
Python实现将一个大文件按段落分隔为多个小文件的简单操作方法
2017/04/17 Python
Python自动化之数据驱动让你的脚本简洁10倍【推荐】
2019/06/04 Python
python实现文件的分割与合并
2019/08/29 Python
Win10里python3创建虚拟环境的步骤
2020/01/31 Python
Python文件读写w+和r+区别解析
2020/03/26 Python
Python pandas 列转行操作详解(类似hive中explode方法)
2020/05/18 Python
Python3利用scapy局域网实现自动多线程arp扫描功能
2021/01/21 Python
html5 worker 实例(一) 为什么测试不到效果
2013/06/24 HTML / CSS
学生自我评价范文
2014/02/02 职场文书
乡镇总工会学雷锋活动总结
2014/03/01 职场文书
广播节目策划方案
2014/05/23 职场文书
公安机关党的群众路线教育实践活动剖析材料
2014/10/10 职场文书
百万英镑观后感
2015/06/09 职场文书