当前位置:网站首页>Flutter 3.0升级内容,该如何与小程序结合
Flutter 3.0升级内容,该如何与小程序结合
2022-08-05 05:14:00 【汪鸿的好朋友】
背景介绍
前端时间在写内部用的脚手架,写到数据管理这块儿,调研了很多数据管理相关的库。比如zustand、redux toolkit、unstated-next等等。后来仔细的研究了下redux toolkit觉得它实在是太全乎了,于是又趁热打铁看了看相关的源码实现。里面几乎没有任何新的东西,全都是基于之前redux的一些封装,不得不让我竖起了大拇指。于是乎便有了这个关于redux向redux toolkit转变的系列文章(上、下两篇),通过简单的对比结合源码的实现来分享下redux toolkit(以下简称rtk)到底做了些什么。
构建目的
构建rtk的目的就是为了要标准化书写redux的逻辑。它最初创建的目的是为了解决以下三个问题:
- 构建一个redux store过于繁杂(创建reducer,action,actionCreator等等)
- 为了使得redux能变得更有用,使用者这必须得引入各种类库(比如redux-thunk, redux-sagger等等)
- redux需要很多样板代码
所以为了简化整个使用流程,rtx应运而生。同时,rtk也提供非常有用的获取和缓存数据的工具rtk query。这样,一整个完成的体系就构建出来了。
逐步解析
下面我们通过一个简单的案例,来对比下redux和rtk在使用方法上的不同。这里,就采用rtx官网的简单数字加减的案例(如下图所示)来进行对比分析。
页面结构
公用js
const KaTeX parse error: Expected '}', got 'EOF' at end of input: …tion render() { value.innerHTML = store.getState()?.num
}
function eventListener() {const $add = document.querySelector(‘.add’);const $minuse = document.querySelector(‘.minuse’);const a d d A s y n c = d o c u m e n t . q u e r y S e l e c t o r ( ′ . a d d − a s y n c ′ ) ; addAsync = document.querySelector('.add-async'); addAsync=document.querySelector(′.add−async′);add.addEventListener(‘click’, function() {store.dispatch(increment(2));}) m i n u s e . a d d E v e n t L i s t e n e r ( ′ c l i c k ′ , f u n c t i o n ( ) s t o r e . d i s p a t c h ( d e c r e m e n t ( 3 ) ) ; ) minuse.addEventListener('click', function() {store.dispatch(decrement(3));}) minuse.addEventListener(′click′,function()store.dispatch(decrement(3));)addAsync.addEventListener(‘click’, function() {store.dispatch((dispatch) => {setTimeout(() => {dispatch(increment(3))}, 1000)})})
}
//这里的store是由redux或者redux-toolkit创建出来的store
store.subscribe(render);
render();
eventListener();
* ### redux实现按照之前我们写redux的逻辑,简单的代码如下:
/*
- @Author: ryyyyy
- @Date: 2022-07-03 08:22:26
- @LastEditors: ryyyyy
- @LastEditTime: 2022-07-04 16:31:43
- @FilePath: /toolkit-without-react/src/redux-index.js
- @Description:
*/
import {createStore, applyMiddleware} from ‘redux’;
import thunk from ‘redux-thunk’;
import logger from ‘redux-logger’;
const incrementType = “counter/increment”;
const decrementType = “counter/decrement”;
const increment = (count=2) => {return {type: incrementType,payload: count};
};
const decrement = (count=2) => {return {type: decrementType,payload: count};
};
const counterReducer = (state, action) => {switch (action.type) {case incrementType:return { …state, num: state.num + (action?.payload || 1) };case decrementType:return { …state, num: state.num - (action.payload || 1) };default:return state;}
};const store = createStore(counterReducer, {num: 0}, applyMiddleware(logger, thunk));
export default store;
export {increment,decrement
}
里面我们还是想往常一样,定义了actionCreator,定义了reducer处理action,然后通过引入了logger支持打印log,引入thunk支持dispatch一个异步的antion,用过redux的同学肯定都知道这些个逻辑,就不再过多描述。
* ### redux toolkit实现这部分,我们用rtk来重写上面的逻辑,为了方便对比,我们一步一步的来做重新改写。
1.action和reducer替换
import {createAction,createReducer } from ‘@reduxjs/toolkit’;
const increment = createAction(‘counter/increment’);
const decrement = createAction(‘counter/decrement’);
//写法一
const counterReducer = createReducer({num: 0}, (builder) => {builder.addCase(increment, (state, action) => {state.num += action.payload}).addCase(decrement, (state, action) => {state.num -= action.payload})
})
//写法二
const counterReducer = createReducer({num: 0}, {[increment]: (state, action) => {state.num += action.payload},[decrement]: (state, action) => {state.num -= action.payload},
})
非常简单,这里通过createAction传入type就生成了increment和decrement两个actionCreator,createAction的第二个参数prepareAction?,用于对传入的action进行增强(后面源码分析会讲到)。然后利用createReducer简单的传入initialState和对应的描述各个reducer分支的逻辑,就能直接生成reducer。这里支持Builder Callback和Map Object两种写法,前者可以通过builder链式的调用,配置不同的reducer分支逻辑;后者,则通过map的形式,更为直观的给出各个reducer分支的配置。细心的读者还可以观察到,在各个reducer分支的实现里面,我们是直接操作state,是的,createReducer里面内置了immer的逻辑,简直棒呆! 2. store的生成
const store = configureStore({reducer: counterReducer,middleware: [logger]
})
其实这里跟原本的redux的createStore差不太多,只不过这里形参采用map的形式,更让你明白各个字段都是用作什么的。当然,这里不止这么些个参数配置,想要了解详情的小伙伴,请移步[rtk官网](https://link.juejin.cn/?target=https%3A%2F%2Fredux-toolkit.js.org%2F "https://redux-toolkit.js.org/")。细心的读者会发现,这里我们并没有引入redux-thunk,哈哈哈,因为在其内部实现中已经帮我们内置了thunk的功能,突出一个方便。想不想更方便一点呢,当然有办法,rtx提供了一个createSlice的方法,讲上面的action,reducer等都融合在了一起,参看下面的代码:
const counterSlice = createSlice({name: ‘counter’, //用作命名空间,和别的slice区分开initialState: {num: 0},reducers: {increment(state, action) {state.num += action.payload;},decrement(state, action) {state.num -= action.payload;}}
})
const {reducer, actions} = counterSlice;
const {increment, decrement} = actions;
通过createSlice返回了actions和reducer,真的不能更简单了。下面,我们参照源码,来实现下rtk上述几个基本方法。
rtx源码实现
-------
* configureStore
import {createStore, combineReducers, applyMiddleware, compose} from ‘redux’;
import isPlainObject from ‘./utils/isPlainObject’; //工具函数,判断是不是一个对象
import thunk from ‘redux-thunk’;
const configureStore = (options) => {const {reducer,middleware = undefined,devTools = true,preloadedState = undefined} = options;let rootReducer;if (typeof reducer === ‘function’) {rootReducer = reducer} else if (isPlainObject(reducer)) {rootReducer = combineReducers(reducer);} else {throw new Error(‘“reducer” is a required argument, and must be a function or an object of functions that can be passed to combineReducers’)}const composedMiddleware = Array.isArray(middleware) ? middleware.concat(thunk) : [thunk];const composeEnhancers = devTools ? window.REDUX_DEVTOOLS_EXTENSION_COMPOSE||compose : compose;return createStore(reducer, preloadedState, composeEnhancers(applyMiddleware(…composedMiddleware)));
}
export default configureStore;
函数接受四个参数(官网是5个,我简化了)。首先reducer的创建,如果是函数的话,表示传入的是单个reducer,如果是对象,则使用combineReducers进行组合。然后是middleware,根据传入的middleware组合内置的thunk构建新的middleware。接着是enhancer部分,这里会判断是否打开Redux DevTools Extension(就是下面截图这玩意儿)。
<img src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/447a1c70818b4788a5b51e623e3fd388~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image?) 最后调用redux的createStore,齐活儿。从这里我们就能看出,其实并没有什么新的逻辑,全是redux的一些概念" style="margin: auto" />
* createAction
import isPlainObject from “./utils/isPlainObject”;
const createAction = (type, prepareAction) => {const actionCreator = (…args) => {if (prepareAction) {let prepared = prepareAction(…args);if (!isPlainObject(prepared)) {throw new Error(‘prepareAction did not return an object’)}return {type,payload: prepared.payload}}return {type,payload: args[0]}}actionCreator.type = type;actionCreator.toString = () => ${type}
;return actionCreator;
}
export default createAction;
action的创建比较简单,直接返回了一个actionCreator。因为我们可以通过increment.type或者increment.toString()拿到action的type,所以在actionCreator上挂了两个属性。关于第二个参数prepareAction,如果传入了,则根据它生成新的payload,是对之前的payload的一个增强。
/*
- @Author: ryyyyy
- @Date: 2022-07-04 13:53:49
- @LastEditors: ryyyyy
- @LastEditTime: 2022-07-04 15:04:38
- @FilePath: /toolkit-without-react/toolkit/createReducer.js
- @Description:
*/
import produce from “immer”;
export const executeReducerBuilderCallback = (builderCallback) => {const actionsMap = {};const builder = {addCase: (typeOrActionCreator, reducer) => {const type =typeof typeOrActionCreator === “string”? typeOrActionCreator: typeOrActionCreator.type;if (!actionsMap[type]) actionsMap[type] = reducer;return builder;},};builderCallback(builder);return [actionsMap];
};
const createReducer = (initialState, mapOrBuilderCallback) => {function reducer(state = initialState, action) {const type = typeof mapOrBuilderCallback;if (type !== “function” && type !== “object”) {throw new Error(“mapOrBuilderCallback must be a map or a builder function”);}let [actionsMap] =type === “function”? executeReducerBuilderCallback(mapOrBuilderCallback): [mapOrBuilderCallback];let reducer = actionsMap[action.type];if (reducer) {return produce(state, (draft) => {reducer(draft, action);});}return state;}return reducer;
};
export default createReducer;
别看createReducer的代码多,因为是为了兼容上面两种写法,所以显得代码多了些。内部返回了一个reducer。由第二个参数mapOrBuilderCallback,来决定如何获取actionsMap。然后根据action的type来确定最后使用reducer的哪个分支actionsMap\[action.type\]。内部通过immer的produce方法实现了immutable的数据保证。
* createSlice
import createAction from “./createAction”;
import createReducer from “./createReducer”;
const createSlice = (options) => {const {name, initialState, reducers} = options;const actions = {}, newReducers = {};Object.keys(reducers).forEach((key) => {const type = ${name}/${key}
;actions[key] = createAction(type);newReducers[type] = reducers[key];})return {actions,reducer: createReducer(initialState, newReducers)}
}
export default createSlice;
这里内部主要调用了上述createAction和createReducer去生成对应的action和reducer。值得注意的是,传入createReducer的reducer需要重新构建,因为其对应的action是用命名空间加上原来的reducers配置的key生成的新的key。到此,rtk的一些基本函数实现就已经完成了,想要全面了解每个细节,我建议直接去读源码。
写在最后
----
可以看到,rtx的实现并没有什么新的东西,但是其用法逻辑上确是给我们带来了很大的便利。下一篇文章,会接着一些异步逻辑,以及rtk query继续深入研究,敬请期待,谢谢。
边栏推荐
猜你喜欢
随机推荐
Detailed Explanation of Redis Sentinel Mode Configuration File
range函数作用
学习总结week2_4
转正菜鸟前进中的经验(废话)之谈 持续更新中... ...
【After a while 6】Machine vision video 【After a while 2 was squeezed out】
BFC(Block Formatting Context)
2022 Hangzhou Electric Multi-School 1st Session 01
day9-字符串作业
Lecture 5 Using pytorch to implement linear regression
Mysql5.7 二进制 部署
物理层的接口有哪几个方面的特性?各包含些什么内容?
第三讲 Gradient Tutorial梯度下降与随机梯度下降
Matplotlib(三)—— 实践
如何停止flink job
A blog clears the Redis technology stack
基于Flink CDC实现实时数据采集(四)-Sink接口实现
学习总结week2_5
Distributed and Clustered
Spark ML学习相关资料整理
【记一下1】2022年6月29日 哥和弟 双重痛苦