原生微信小程序开发中 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 FLASH幻灯片字符串中有连接符&的处理方法
Mar 01 Javascript
jQuery中clone()方法用法实例
Jan 16 Javascript
jQuery及JS实现循环中暂停的方法
Feb 02 Javascript
javascript实时获取鼠标坐标值并显示的方法
Apr 30 Javascript
第六章之辅组类与响应式工具
Apr 25 Javascript
jquery实现刷新随机变化样式特效(tag标签样式)
Feb 03 Javascript
原生js轮播特效
May 18 Javascript
js模拟百度模糊搜索的实例
Aug 04 Javascript
使用store来优化React组件的方法
Oct 23 Javascript
JS点击动态添加标签、删除指定标签的代码
Apr 18 Javascript
vue数据初始化initState的实例详解
Apr 11 Javascript
Element-ui树形控件el-tree自定义增删改和局部刷新及懒加载操作
Aug 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
PHPMyadmin 配置文件详解(配置)
2009/12/03 PHP
PHP错误WARNING: SESSION_START() [FUNCTION.SESSION-START]解决方法
2014/05/04 PHP
php格式化时间戳显示友好的时间实现思路及代码
2014/10/23 PHP
PHP获取当前完整URL地址的函数
2014/12/21 PHP
thinkPHP实现表单自动验证
2014/12/24 PHP
php include类文件超时问题处理
2015/02/06 PHP
PHP的new static和new self的区别与使用
2019/11/27 PHP
PHP实现图片防盗链破解操作示例【解决图片防盗链问题/反向代理】
2020/05/29 PHP
清华大学出版的事半功倍系列 javascript全部源代码
2007/05/04 Javascript
讨论javascript(一)工厂方式 js面象对象的定义方法
2009/12/15 Javascript
jquerymobile局部渲染的各种刷新方法小结
2014/03/05 Javascript
node.js中的fs.chownSync方法使用说明
2014/12/16 Javascript
深入浅析JavaScript中prototype和proto的关系
2015/11/15 Javascript
如何解决ligerUI布局时Center中的Tab高度大小
2015/11/24 Javascript
jQuery插件HighCharts绘制2D圆环图效果示例【附demo源码下载】
2017/03/09 Javascript
bootstrap table动态加载数据示例代码
2017/03/25 Javascript
Node.js+ES6+dropload.js实现移动端下拉加载实例
2017/06/01 Javascript
关于JS与jQuery中的文档加载问题
2017/08/22 jQuery
angularJS的radio实现单项二选一的使用方法
2018/02/28 Javascript
jQuery实现获取当前鼠标位置并输出功能示例
2019/01/05 jQuery
详解jquery和vue对比
2019/04/16 jQuery
用Vue编写抽象组件的方法
2019/05/06 Javascript
js实现随机点名
2021/01/19 Javascript
[04:19]DOTA2完美大师赛第四天精彩集锦
2017/11/26 DOTA
Python中类的继承代码实例
2014/10/28 Python
Python编程把二叉树打印成多行代码
2018/01/04 Python
Python针对给定列表中元素进行翻转操作的方法分析
2018/04/27 Python
python爬虫之urllib3的使用示例
2018/07/09 Python
Python装饰器简单用法实例小结
2018/12/03 Python
Python批量生成特定尺寸图片及图画任意文字的实例
2019/01/30 Python
Ann Taylor官方网站:美国最大的女性产品制造商之一
2016/09/14 全球购物
通用自荐信范文
2014/03/14 职场文书
乡镇纠风工作实施方案
2014/03/22 职场文书
安全生产管理责任书
2014/04/16 职场文书
公司经营目标责任书
2015/01/29 职场文书
如何写好一份优秀的工作总结?
2019/06/21 职场文书