最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 原生微信小程序开发中 redux 的使用

    正文概述 掘金(王唯佳)   2021-02-17   868

    前提

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

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

    需要解决的问题

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

    redux库的引入

    1、redux的安装,使用 npm与yarn 都可以。

    具体到redux中文官网如下:www.reduxjs.cn/introductio…

    2、微信小程序引入外部npm包。

    使用微信小程序IDEA,tools 中的 Build npm,生成miniprogram_npm

    原生微信小程序开发中 redux 的使用 原生微信小程序开发中 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.jsglobalData中维护,这样每个页面都可以直接获取到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 的使用

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元