对于Redux源码的一些理解

本文解析Redux的createStore、applyMiddleware及compose等核心概念。介绍如何利用中间件扩展Redux功能,特别是thunk中间件处理异步action。

Redux很早前看过源码,不得不说Redux是一个有用的架构,接触过Redux对于之后理解React-Redux有很大的帮助。最近学习了一段时间后打算重新学习下,一方面为了总结和归纳,另一方面分享给大家,如果有什么不足之处希望各位大牛的纠错和指正。本文主要包含如下几个分析点:

  • applyMiddleware
  • compose
  • thunk中间件
// 该文件的核心函数部分共传入了三个参数
// reducer, preloadedState, enhancer
function createStore(reducer, preloadedState, enhancer){
    ...
    if (typeof enhancer !== 'undefined') {
        return enhancer(createStore)(reducer, preloadedState)
    }
    ...
}
复制代码

createStore方法的作用是用来创建一个仓库来存放state,subscribe,reducer以及dispatch,state用来存放数据的地方,通过store.getState()来获取;subscribe用来加入监听函数,当页面数据改变的时候会进行触发,dispatch用来派发action,根据不同的类型匹配不同的reducer,继而进行state数据的更新,具体可以参考阮一峰老师的博客 Redux 入门教程

其中enhancer函数的作用顾名思义就是用来扩展加强的,这个函数的存在使得可以随意的加入早就想要的中间件,从而更加方便快捷,这里的加强函数主要是applyMiddleware

applyMiddleware

这里使用连续箭头函数,简单明了更重要的一点是形成了闭包,闭包的存在使得内部的变量可以很自由的访问外部被多个return嵌套的变量, 从而使得每个中间件都获得middlewareAPI参数,引用外部的dispatch变量,这样避免了如果存在一个中间件修改了dispatch导致后面一系列的问题。
export default function applyMiddleware(...middlewares) {
  return (createStore) => (reducer, preloadedState, enhancer) => {
    // 创建仓库
    const store = createStore(reducer, preloadedState, enhancer)
    let dispatch = store.dispatch
    let chain = []

    const middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action) // 通过闭包引用外部的dispatch变量
    }
    // 将middlewareAPI传入中间件,使得每个中间件都获得{getState,dispatch}参数,而由于闭包的原因,我们就可以在中间件当中获取最新的store以及引用外部更新的dispatch变量。
    // @return 返回包含(next) => (action) => {....}的数组
    chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 创建增强功能的dispatch
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}
复制代码

thunk中间件

以thunk中间件为例,当需要派发的action是异步函数而不是对象的时候需要这个中间件,则此时chain即为 [(next)=>(action)=>{...}, ....], 如果action为函数则直接执行该函数,并且传入 dispatchgetSate这两个参数,否则执行next方法直到最后next函数为dispatch进行action派发。
function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
复制代码

compose

理解compose方法首先需要了解reduce,reduce()方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值,所以最终的形式是a(b(c(d...(...args))))

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
复制代码

之前chain返回包含(next) => {...}的thunk,logger中间件数组,从而得到a_middleware方法里的next方法就是b_middleware,b_middleware方法里的next方法是c_middleware,以此类推,根据applyMiddleware.jscompose(...chain)(store.dispatch)以及chain返回值可以知道, 最后的next参数是dispatch,大概步骤如下:

  • 第一步:当我们把store.dispatch传入c_middleware时,这时就把store.dispatch传给了 c_middleware的next变量,返回一个(action)=>{next=store.dispatch}[1]函数;
  • 第二步:接下来就是把[1]传给b_middleware的中间件的next,这时候中间件b_middleware内部的next就变成(action)=>{[1]}[2], 当执行b_middleware的时候,会进入c_middleware;
  • 第三步:接下来就是把[2]的结果传递给a_middleware, (action)=>(action)=>{[1]}传递给a-middleware的next变量,当执行a_middleware的时候会进入b_middleware。

从下面大概例子可以看出disatch: (...args) => dispatch(...args)的好处,dispatch随时随地随着dispatch = compose(...chain)(store.dispatch)更新而更新,从而当传入为方法的时候,也不会忽略其他中间件,再次dispatch的时候会流过所有thunk之后的中间件。

1.
a_middleware(b_middleware){
    ...
    b_middleware = (action) => (action) => {store.dispatch函数}
    return (action) => b_middleware(action)
    ...
}
dispatch = (action) => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    return b_middleware(action)
}
2.
b_middleware(c_middleware){
    ...
    c_middleware = (action) => {store.dispatch函数}
    return (action) => c_middleware(action)
    ...
}
dispatch = (action) => {
    return c_middleware(action)
}
3.
c_middleware(store.dispatch){
    return (action) => {store.dispatch函数}
}
dispatch = (action) => {store.dispatch函数}
复制代码

总结

1.首先将store.getStatestore.dispatch通过闭包的方式使得中间件可以访问;

2.其次,通过compose函数操作,对next进行赋值,使得中间件按顺序依次执行;

3.最后,返回一个dispatch函数,可以通过传入action参数,使得中间件按顺序依次执行,如果action为函数则直接执行该函数,并且传入dispatch和getSate参数。

总之使用redux进行状态管理极大地提高了工作效率,让数据更好地被管理。

转载于:https://juejin.im/post/5c9b2d27e51d4516c14cff6e

一、数据采集层:多源人脸数据获取 该层负责从不同设备 / 渠道采集人脸原始数据,为后续模型训练与识别提供基础样本,核心功能包括: 1. 多设备适配采集 实时摄像头采集: 调用计算机内置摄像头(或外接 USB 摄像头),通过OpenCV的VideoCapture接口实时捕获视频流,支持手动触发 “拍照”(按指定快捷键如Space)或自动定时采集(如每 2 秒采集 1 张),采集时自动框选人脸区域(通过Haar级联分类器初步定位),确保样本聚焦人脸。 支持采集参数配置:可设置采集分辨率(如 640×480、1280×720)、图像格式(JPG/PNG)、单用户采集数量(如默认采集 20 张,确保样本多样性),采集过程中实时显示 “已采集数量 / 目标数量”,避免样本不足。 本地图像 / 视频导入: 支持批量导入本地人脸图像文件(支持 JPG、PNG、BMP 格式),自动过滤非图像文件;导入视频文件(MP4、AVI 格式)时,可按 “固定帧间隔”(如每 10 帧提取 1 张图像)或 “手动选择帧” 提取人脸样本,适用于无实时摄像头场景。 数据集对接: 支持接入公开人脸数据集(如 LFW、ORL),通过预设脚本自动读取数据集目录结构(按 “用户 ID - 样本图像” 分类),快速构建训练样本库,无需手动采集,降低系统开发与测试成本。 2. 采集过程辅助功能 人脸有效性校验:采集时通过OpenCV的Haar级联分类器(或MTCNN轻量级模型)实时检测图像中是否包含人脸,若未检测到人脸(如遮挡、侧脸角度过大),则弹窗提示 “未识别到人脸,请调整姿态”,避免无效样本存入。 样本标签管理:采集时需为每个样本绑定 “用户标签”(如姓名、ID 号),支持手动输入标签或从 Excel 名单批量导入标签(按 “标签 - 采集数量” 对应),采集完成后自动按 “标签 - 序号” 命名文件(如 “张三
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值