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

前提

复杂场景中有不少数据需要在多个不同页面间来回使用和修改。但是小程序页面直接的数据通信方式十分的简单。通常情况需要自己维护一个全局的对象来存放共有数据。但是,简单的维护一个共有数据实体,会随着业务逻辑的不断复杂化而变的过分庞大,并且数据的修改往往无法很好的溯源。加之公共数据实体中数据的修改和页面的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。

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

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

这样基本可以解决所有第三方库遇到的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使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 微信小程序Redux绑定实例详解

    微信小程序Redux绑定实例详解 安装 clone或者下载代码库到本地: git clone https://github.com/charleyw/wechat-weapp-redux 将dist/wechat-weapp-redux.js(或者拷贝minify的也可以)文件直接拷贝到小程序的工程中,例如(下面假设我们把第三方包都安装在libs目录下): cd wechat-weapp-redux cp -r dist/wechat-weapp-redux.js <小程序根目录>/libs

  • 在小程序中集成redux/immutable/thunk第三方库的方法

    一.前言 小程序给我们暴露了两个参数 require 和 module , require 用来在模块中加载其他模块, module 用来将模块中的方法暴露出去 module.exports = function(){} 所以只要需要让第三方库的代码使用这种形式的 export 就可以了 二.构建Redux的微信小程序包 打一个 Redux 包,让它可以兼容微信小城的加载方式 git clone https://github.com/reactjs/redux.git npm install #

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

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

  • 微信小程序开发图片拖拽实例详解

    微信小程序开发图片拖拽实例详解 1.编写页面结构:moveimg.wxml <view class="container"> <view class="cnt"> <image class="image-style" src="../uploads/foods.jpg" style="left:{{ballleft}}px;width:{{screenWidth}}px" bi

  • Echarts在Taro微信小程序开发中的踩坑记录

    背景 近期笔者在使用Taro进行微信小程序开发,当引入Echarts图表库时,微信检测单包超限2M的一系列优化措施的踩坑记录,期望能指导读者少走一些弯路. 为什么选择Echarts? 微信小程序目录市面上使用最多的两款图表库,如下: echarts-for-weixin--echarts微信小程序版本 wx-charts--基于微信小程序的图表库 对比两款图表库优缺点刚好相反. echarts-for-weixin:功能强大,但体积非常大 wx-charts:功能相对简单,但体积小 由于笔者对e

  • 微信小程序 开发中遇到问题总结

    微信小程序 开发中遇到问题总结 1.由于小程序wx.request()方法是异步的,在app.js执行ajax后,各分页加载app.js的全局数据时,无法按顺序加载.例: //app.js App({ ajax:function(){ let that = this; wx.request({ url: 'https://a.com/url.php', method: 'GET', success: function(e){ that.data = 123; } }) }; }) //conte

  • 微信小程序开发中var that =this的用法详解

    在微信小程序开发中,var that =this的声明很常见.举个例子,代码如下! 示例代码1 //index.js Page({ data: { toastHidden: true, }, loadData: function () { var that = this//这里声明了that:将this存在that里面 wx.request({ url: 'test.php', data: {a: 'a', b: 'b'}, header: { 'content-type': 'applicat

  • 深入解析微信小程序开发中遇到的几个小问题

    本地图片不显示,开发工具运行是没问题的,但真机调试却显示不了 item.img = '/goods/img/图片.png' <image src=" { { item.img } } " class="image"> </image> 经过仔细观察发现,路径是没问题的,问题在于图片名不能是中文的,把它改成字母+数字就好了. 使用FileSystemManager.readdir(Object object)方法读取本地文件夹中的所有图片,报错

  • 微信小程序 获取二维码实例详解

     微信小程序 获取二维码实例详解 理论: 接口A: 适用于需要的码数量较少的业务场景 接口地址:(永久有效,数量有限,进入path对应的页面) https://api.weixin.qq.com/wxa/getwxacode?access_token=ACCESS_TOKEN path String 不能为空,最大长度 128 字节 width Int 430(默认) 二维码的宽度 auto_color .. line_color .. 接口B:适用于需要的码数量极多,或仅临时使用的业务场景(永

  • 微信小程序数据存储与取值详解

    在小程序开发的过程,经常要需要这个页面输入的数据,在下一个页面中进行取值赋值. 比如: 在A页面input输入框,输入电话号码,点击添加.需要在B页面电话区域中,显示刚刚输入的电话号码. 因为这是两个页面,就需要先存储,再取值.微信小程序提供了数据存储的API,wx.setStorage(OBJECT) 可以将数据存储在本地缓存中指定的 key 中,如果重复会覆盖掉原来该 key 对应的内容. 思路是,在A页面,使用bindinput获取input输入的值,赋值给一个变量(自定义),点击添加按钮

  • 微信小程序和百度的语音识别接口详解

    介绍 因为项目需要,使用到了微信小程序和百度的语音接口 现在将项目中的一个小模块拿出来单独分享. 技术关键字 微微信小程序 百度语音接口 nodejs,express fluent-ffmegp 环境 windows 10 vs code 1.20.1 微信小程序开发工具 1.02.1802270 花生壳-提供域名和内容穿透-用于方便本地远程调试微信小程序 考虑到业务并不复杂,所以就将所有的代码都放在一个页面就可以了(wxml,wxss,js统称为一个页面) 文件目录 页面 index.wxml

  • 微信小程序全局配置以及页面配置详解

    目录 全局配置 全局配置文件及常用配置项 全局配置—window 小程序窗口组成部分 了解window节点常用的配置项 设置导航栏的标题 设置导航栏的背景色 设置导航栏的标题颜色 全局开启下拉刷新功能 设置下拉刷新时窗口的背景色 设置下拉刷新时loading的样式 设置上拉触底的距离 全局配置—tabbar 什么是tabbar tabbar的6个组成部分 tabbar节点的配置项 每个tab项的配置选项 页面配置 页面配置文件的作用 页面配置和全局配置的关系 页面配置中常用的配置项 小结 全局配

随机推荐