前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【微信小程序】---- redux 在原生微信小程序的使用实例

【微信小程序】---- redux 在原生微信小程序的使用实例

作者头像
Rattenking
发布2022-01-06 20:13:54
5.6K0
发布2022-01-06 20:13:54
举报
文章被收录于专栏:RattenkingRattenking

weapp-redux 下载

  1. weapp-redux 使用实例下载

预览

在这里插入图片描述
在这里插入图片描述

开发

1. 目标

  1. 学会 redux 在原生微信小程序的使用;
  2. 学习和思考微信小程序中封装 Provider;

2. 引入 redux 文件

  1. 下载 redux
  2. git 克隆
代码语言:javascript
复制
	git clone https://github.com/reduxjs/redux.git
  1. 使用实例【 weapp-redux-demo 】下载
在这里插入图片描述
在这里插入图片描述

3. 修改 redux 源码

代码语言:javascript
复制
import createStore from './redux/createStore';
import combineReducers from './redux/combineReducers';
import compose from './redux/compose';
import applyMiddleware from './redux/applyMiddleware';
import bindActionCreators from './redux/bindActionCreators';

export {
  createStore, 
  combineReducers, 
  applyMiddleware, 
  bindActionCreators,
  compose
};

修改 redux 源码适配微信小程序!

4. 创建 store

在这里插入图片描述
在这里插入图片描述
1. 创建 store
  1. store/index.js
代码语言:javascript
复制
import { createStore, applyMiddleware } from '../weapp-redux/index'

import reducer from './reducers/index';

const store = createStore(reducer)
export default store;
2. 创建 reducers
在这里插入图片描述
在这里插入图片描述
  1. reducers/index.js
代码语言:javascript
复制
import { combineReducers } from '../../weapp-redux/index';

import { commonCart } from './cart';
import { goodsDetail } from './goodsDetail';
import { integralZone } from './integralZone';
import { collect } from './collect';

export default combineReducers({
  commonCart,
  goodsDetail,
  integralZone,
  collect
})
  1. 页面示例数据创建 [reducers/collect.js]
代码语言:javascript
复制
import {
  UPDATE_COLLECT_LIST,
  CHECK_COLLECT_STATUS_GOODS
} from '../constants/actionTypes'

const defaultState = {
  list: [],
  goodsCollectStatus: {}
}

export const collect = (state = defaultState, action) => {
  switch (action.type) {
    case UPDATE_COLLECT_LIST:
      return { ...state, list: action.list };
    case CHECK_COLLECT_STATUS_GOODS:
      return { ...state, goodsCollectStatus: action.status}
    default:
      return state;
  }
}
3. 创建 constants
在这里插入图片描述
在这里插入图片描述
  1. 创建命令常量示例 constants/actionTypes.js
代码语言:javascript
复制
// 收藏商品
// 收藏列表更新
export const UPDATE_COLLECT_LIST = 'UPDATE_COLLECT_LIST_GOODS';
// 删除收藏商品
export const DELETE_COLLECT_GOODS = 'DELETE_COLLECT_GOODS';
// 添加收藏商品
export const ADD_COLLECT_GOODS = 'ADD_COLLECT_GOODS';
// 收藏状态查询
export const CHECK_COLLECT_STATUS_GOODS = 'CHECK_COLLECT_STATUS_GOODS';
4. 创建 actions
在这里插入图片描述
在这里插入图片描述
  1. actions/index.js
代码语言:javascript
复制
import * as integral from './integralZone';
import * as goodsDetail from './goodsDetail';
import * as collect from './collect';

export default {
  integral,
  collect,
  goodsDetail
}

注意此处暴露接口命名最好和reducers中保持一直,这样不易名字混淆!

  1. actions中更新页面示例[actions/collect.js]
代码语言:javascript
复制
import store from '../index'
import axios from '../../request/axios'
import { 
  UPDATE_COLLECT_LIST,
  CHECK_COLLECT_STATUS_GOODS
} from '../constants/actionTypes';

export function updateCollectList(list){
  store.dispatch({
    type: UPDATE_COLLECT_LIST,
    list
  })
}
export function checkCollectStatusGoods(status){
  store.dispatch({
    type: CHECK_COLLECT_STATUS_GOODS,
    status
  })
}
// 获取用户收藏列表
export function getCollectList(){
  axios.collectList().then((list = []) => {
    if(Array.isArray(list) && list.length){
      // 所有收藏商品为未选中
      list.forEach(cur => cur.ischeck = false);
      updateCollectList(list);
    }
  }).catch(console.log)
}
// 获取商品收藏状态
export function getGoodsCollectStatus(goodsId, goodsType){
  axios.getGoodsCollectStatus({goodsId, goodsType}).then(checkCollectStatusGoods).catch(console.log);
}
// 添加收藏商品
export function addGoodsCollect(goodsId, skid){
  return new Promise((resolve, reject) => axios.addGoodsCollect({goodsId, skid}).then(resolve).catch(reject));
}
// 删除收藏商品
export function deleteGoodsCollect(id){
  return new Promise((resolve, reject) => axios.delCollect({id}).then(resolve).catch(reject));
}

5. 在 app.js 中引入 store

5.1 直接引入 store 作为 app 的全局变量,页面使用直接 [getApp().store] 进行访问
代码语言:javascript
复制
// app.js
import store from './utils/store/index'
App({
	store
})
5.1.1 优点
  1. 引入少;
  2. 操作不频繁;
  3. 对于每个页面,有一个干净的全局变量空间;
5.1.2 缺点
  1. 更新繁琐,不会自动更新涉及变量的所有位置;
  2. 需要手动在需要的时候获取变量,效果等同于将变量放在app.js;
  3. 操作繁琐,必须手动获取 app.js 中的 store 来获取变量;
5.2 根据 5.1 的缺点思考改进

封装一个类似 react-redux 的中间件;

5.2.1 封装 Provider
  1. 思考如何动态更新?
  2. 如何减少更新通知?
  3. 如何仅更新部分更新的数据,不变的数据不更新?
1. 动态更新
  1. 进行页面 Page 和组件 Component 的拦截;
  2. 在页面和组件加载时,对当前页面 进行订阅 subscribe;
  3. 注意页面和组件卸载时,需要取消订阅;
  4. 不是所有的页面和组件都需要订阅,定义变量,判断当前页面是否需要订阅;
  5. 获取订阅的全局页面变量;
代码语言:javascript
复制
export default function Provider(store){
  const originalPage = Page;
  const originalComponent = Component;
}
2. 页面的订阅和取消订阅
  1. 将 store 设置未页面的
store 变量,方便 this.

store 访问;

  1. storeTypes 存放当前页面需要订阅的全局状态;
  2. 调用 store 的订阅函数 subscribe,同时保存取消订阅方法 unsubscribe;
  3. 在订阅方法中获取当前页面需要订阅的全局状态,收集;
  4. 由于微信小程序的逻辑层和视图层通信需要使用 setData 函数,但是调用太频繁,消耗性能,因此收集需要订阅的全局状态,统一将数据通知视图层。
  5. 注意:必须初始化派送依次数据,否则页面初始化是没有数据的。
  6. 最后在页面卸载函数中监听 unsubscribe 是否存在,存在就在页面卸载的时候执行unsubscribe函数。
代码语言:javascript
复制
Page = (config = {}) => {
    const { onLoad, onUnload,storeTypes = [] } = config;
    config.onLoad = function(opts){
      // 监听全局状态数据变化
      // 设置页面栈变量$store
	  this.$store = store;
	  // 设置监听
	  if(storeTypes && storeTypes.length > 0){
	    this.unsubscribe = store.subscribe(() => {
	      let pageData = {}
	      storeTypes.forEach(storeType => pageData[storeType] = (store.getState()[storeType] || {}))
	      this.setData(pageData)
	    })
	    // 初始化页面数据
	    store.dispatch({type: `@@redux/INIT${randomString()}`});
	  }
      // 绑定页面生命周期
      onLoad && onLoad.call(this, opts);
    }
    config.onUnload = function(){
      onUnload && onUnload.call(this);
      // 页面卸载,取消订阅
      this.unsubscribe && this.unsubscribe();
    }
    originalPage(config);
  }

注意:

  1. 判断 storeTypes,是判断页面是否订阅全局状态的依据,减少订阅的次数,因为每次订阅 listeners,都会收集,执行依次派发, listeners 中的订阅都会执行一次,全部页面和组件都订阅,会消耗性能过大,仅在需要的页面订阅 storeTypes,优化订阅次数。
  2. 订阅生成,但是如果不取消,就会一直存在,在修改全局状态时,会执行 listeners 中所有的订阅。但是页面卸载后下次进入就会生成新的 id,新的页面,所以需要重新订阅。因此需要在卸载页面的时候取消订阅 this.unsubscribe && this.unsubscribe()。

思考:

  1. 由于订阅后,派发时所有收集订阅都会执行,是否可以标记订阅,仅通知当前修改的全局状态存在的订阅,不存在当前修改状态的订阅不派发?
  2. setData 可以只更新部分修改的变量,不修改全部的变量。是否可以通过对比订阅修改前页面当前状态和全局状态进行对比,筛选局部修改变量,进行 setData 修改?
3. 组件的订阅和取消订阅

原理和页面一样,此处不多做解释,直接代码。

代码语言:javascript
复制
Component = (config = {}) => {
    const { lifetimes = {}, attached:conattached, detached:condetached, storeTypes  = [] } = config;
    const { attached, detached } = lifetimes;
    config.lifetimes = {
      attached:function(){
        // 监听全局状态数据变化
        // 设置页面栈变量$store
		  this.$store = store;
		  // 设置监听
		  if(storeTypes && storeTypes.length > 0){
		    this.unsubscribe = store.subscribe(() => {
		      let pageData = {}
		      storeTypes.forEach(storeType => pageData[storeType] = (store.getState()[storeType] || {}))
		      this.setData(pageData)
		    })
		    // 初始化页面数据
		    store.dispatch({type: `@@redux/INIT${randomString()}`});
		  }
        // 绑定页面生命周期
        attached && attached.call(this);
        conattached && conattached.call(this);
      },
      detached:function(){
        detached && detached.call(this);
        condetached && condetached.call(this);
        // 组件卸载,取消订阅
        this.unsubscribe && this.unsubscribe();
      }
    }
    originalComponent(config);
  }
4. 完整 Provider 代码
代码语言:javascript
复制
import isPlainObject from '../utils/isPlainObject';
const randomString = () => Math.random().toString(36).substring(7).split('').join('.');
const subscribeInit = (config, store, page) => {
  let { storeTypes = [] } = config;
  // 设置页面栈变量$store
  page.$store = store;
  // 设置监听
  if(storeTypes && storeTypes.length > 0){
    page.unsubscribe = store.subscribe(() => {
      let pageData = {}
      storeTypes.forEach(storeType => pageData[storeType] = (store.getState()[storeType] || {}))
      page.setData(pageData)
    })
    // 初始化页面数据
    store.dispatch({type: `@@redux/INIT${randomString()}`});
  }
}
export default function Provider(store){
  if(!store && typeof store !== 'object'){
    throw new Error(`Expected the root store to be a object.`)
  }
  const originalPage = Page;
  const originalComponent = Component;
  Page = (config = {}) => {
    const { onLoad, onUnload } = config;
    config.onLoad = function(opts){
      // 监听全局状态数据变化
      subscribeInit(config, store, this);
      // 绑定页面生命周期
      onLoad && onLoad.call(this, opts);
    }
    config.onUnload = function(){
      onUnload && onUnload.call(this);
      // 页面卸载,取消订阅
      this.unsubscribe && this.unsubscribe();
    }
    originalPage(config);
  }
  Component = (config = {}) => {
    const { lifetimes = {}, attached:conattached, detached:condetached } = config;
    const { attached, detached } = lifetimes;
    config.lifetimes = {
      attached:function(){
        // 监听全局状态数据变化
        subscribeInit(config, store, this);
        // 绑定页面生命周期
        attached && attached.call(this);
        conattached && conattached.call(this);
      },
      detached:function(){
        detached && detached.call(this);
        condetached && condetached.call(this);
        // 组件卸载,取消订阅
        this.unsubscribe && this.unsubscribe();
      }
    }
    originalComponent(config);
  }
  return {
    Page,
    Component
  }
}

6. 实际开发中的应用

  1. 引入页面或组件需要使用的action;
  2. 引入页面或组件需要使用的全局状态storeTypes;
  3. 逻辑层使用action中的方法;
代码语言:javascript
复制
// collect.js
import {
  getCollectList,
  addGoodsCollect,
  deleteGoodsCollect
} from '../../utils/store/actions/collect'
Page({
  storeTypes: ['collect'],
  data: {},
  onLoad(){
    getCollectList();
  }
})
代码语言:javascript
复制
// collect.wxml
<view class="rui-mb15" wx:for="{{ collect.list }}" wx:key="collectList">
 <rui-swipe-cell right-width="{{ right }}" disabled="{{isEdit}}">
   <view class="rui-form-li rui-flex-ac">
     <radio color="#e24c4e" 
     class="rui-mr15" 
     data-item="{{item}}"
     data-index="{{index}}"
     catchtap="chooseGoods" 
     checked="{{item.check}}" 
     wx:if="{{isEdit}}"></radio>
     <view class="rui-fa rui-pr rui-mr40" catchtap="routeTo" data-url="../goodsDetail/goodsDetail?goodsId={{item.tyfogoodsid}}&isFromBc={{item.goodsType}}" data-type="{{item.goodsType}}" data-id="{{item.tyfogoodsid}}">
       <view class="rui-now-btn" wx:if="{{item.isPot == 1}}">现货</view>
       <image lazy-load="true"  src="{{item.mainpic}}" class="rui-goods-img"></image>
     </view>
     <view class="rui-fg" catchtap="routeTo" data-url="../goodsDetail/goodsDetail?goodsId={{item.tyfogoodsid}}&isFromBc={{item.goodsType}}" data-type="{{item.goodsType}}" data-id="{{item.tyfogoodsid}}">
       <view class="rui-fs28 rui-line3 rui-color4 rui-collect-title">{{item.goodsname}}</view>
       <!-- <view class="rui-fs24 rui-color4 rui-mt20 rui-mb20">蓝色</view> -->
       <view class="rui-mt30">
         <text class="rui-fs30 rui-color17 rui-mr20">¥{{item.marketprice}}</text>
         <text class="rui-fs20" wx:if="{{showMoney}}">最高赚<text class="rui-color17">{{item.predictAmt}}</text>元</text>
       </view>
     </view>
   </view>
   <view slot="right" class="rui-cancel-collect" data-item="{{item}}" catchtap="cancelCollect">取消收藏</view>
 </rui-swipe-cell>
</view>

7. 总结

  1. 由于性能的原因,能够不使用,就尽量不使用;
  2. 除非多页面多组件同时使用了该全局状态,同时业务逻辑比较复杂,容易混淆,使用全局状态方便管理,否则不要设置为全局状态;
  3. 在订阅优化尽量只执行更新的订阅;
  4. setData 修改视图层数据尽量只修改局部改变的部分,而不是全部修改。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-11-26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • weapp-redux 下载
  • 预览
  • 开发
    • 1. 目标
      • 2. 引入 redux 文件
        • 3. 修改 redux 源码
          • 4. 创建 store
            • 1. 创建 store
            • 2. 创建 reducers
            • 3. 创建 constants
            • 4. 创建 actions
          • 5. 在 app.js 中引入 store
            • 5.1 直接引入 store 作为 app 的全局变量,页面使用直接 [getApp().store] 进行访问
            • 5.2 根据 5.1 的缺点思考改进
          • 6. 实际开发中的应用
            • 7. 总结
            相关产品与服务
            云开发 CloudBase
            云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档