前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深入Flux

深入Flux

原创
作者头像
BLUSE
修改于 2022-11-24 01:36:07
修改于 2022-11-24 01:36:07
75000
代码可运行
举报
文章被收录于专栏:前端web技术前端web技术
运行总次数:0
代码可运行

前言: 本来想学习总结下Redux、Mobx, 可是说到这两个, 那就不得不提一下 Flux, 他们都是使用单向数据流来集中管理应用的状态变化, 以及触发页面的数据更新. 所以这篇文章先简单介绍一下Flux.

一、介绍

Flux 的出现背景和具体细节不做详细介绍, 感兴趣的可以参考官网. Flux中有四个角色, 分别是Action、Dispatcher、Store、View

1、Dispatcher

作为 Flux 的中心, 负责管理数据流的工作, 所有的 Store 将会共用一个 Dispatcher. 可以认为 Dispathcer 管理着一张注册表 callback list, 每当定义一个 Store 都会向注册表里添加上自己的回调函数 func, 当 Action Creator 触发一个 Action, Dispatcher 会按注册表的顺序逐个执行callback list中所有的函数, 回调函数会根据实际情况去选择是否要更新 state 状态.

2、Stores

负责统一管理 Flux 中的状态和逻辑, 这里可以认为是特定域的状态, 他包括了逻辑域的单例模型 FluxStore 和 逻辑域的模型集合 FluxStoreGroup. 每个 Store 通常定义时会向 Dispatcher 注册一个回调函数, 这个回调函数会接收一个 action , 然后会根据 action 的类型检查是否需要执行或执行哪一种状态更新操作, 等待所有 Store 状态检查更新完后会广播一个 change 事件, 通知 Views 进行自动更新.

3、Views

基于React的视图层逻辑, 我们可以根据 state 的变化去更新视图, 例如通过 setState 方法触发 React 视图的更新. 而在 Flux 中, Views 负责监听其所依赖 Store 的广播事件, 它从 Store 中获取到变化的 state, 并控制页面的更新. 同时 Views 也能够接收到视图层事件的触发, 通过 Actions 去改变 Store 的状态.

4、Actions

其利用 Dispatcher 暴露的一个方法 dispatch, 该方法可以传入一些有效的参数去触发 Store 的状态改变, 而不同的 action 就是针对与不同参数的 dispatch 方法的封装调用. 我们可以将不同的 action 绑定到视图层的不同事件中, 通过视图事件的触发, 从而调用 dispatch 去更改 Store.

Actions 通常包括具体的 action 和 action creator, action 是一个对操作的方法描述, 可以理解为一个 payload 对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
    type: TodoActionTypes.ADD_TODO, // action 的类型, 用于 disatcher 触发时的执行筛选
    text: '', // 需要更新的 state 值
}

action creator 是和 action 相关的事件函数, 为了便于统一的管理, 会把所有的 creator 放在一起

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const Actions = {
  // 单个creator, 执行 dispatch 事件
  addTodo(text) {
    TodoDispatcher.dispatch({
      type: TodoActionTypes.ADD_TODO,
      text,
    });
  },
}

二、深入源码

Dsipatcher

既然是中心枢纽, 那我们先来看一下Dispatcher的使用和实现. 在使用上很简单, 直接实例化即可, 实例化的对象会用在 Actions 和 Stores 中.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { Dispatcher } from 'flux';
export default new Dispatcher();

其实现如下:

关键内容说明

1、 _callbacks

以 key-value 的形式维护一个回调函数的注册表, FluxStore 和 FluxStoreGroup 在实例化的时候会调用register 函数向注册表里添加各自的 callback, register 通过自增的 _lastID 计算出唯一的 DispatchToken, 作为 Store 的注册标识, 该标识可以用于 disptach.waitFor 中调用, 后面详细说明

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
register(callback: (payload: TPayload) => void): DispatchToken {
    var id = _prefix + this._lastID++; // 唯一标识id
    this._callbacks[id] = callback; // 向注册表中添加回调函数
    return id; // 返回标识
}

2、 dispatch

当某个 action 想要更新 Store 时, 会通过 dispatch 来进行, dispatch 会逐一执行注册表中的所有回调函数, 各回调函数会根据 payload 来决定是否需要更新自己的 state. 大致流程如下:

a) 在执行前会先清理当前的执行状态 (startDispatching), 将注册表中所有回调的当前执行状态 (_isPending) 清除、缓存当前的 payload (_pendingPayload)、将正在执行dispatch的状态标识置位 (_isDispatching)

b) 按注册表顺序执行回调函数, 并将回调函数的执行状态置位 (_isPending), 避免重复执行

c) 恢复执行状态 (_stopDispatching), 将缓存payload清掉 (pendingPayload), 并将执行状态清除 (_isDispatching)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
dispatch(payload: TPayload): void {
    this._startDispatching(payload); // 清理状态
    try {
      for (var id in this._callbacks) {
        if (this._isPending[id]) { // 执行标识位置位
          continue;
        }
        this._invokeCallback(id); // 执行注册表中回调函数
      }
    } finally {
      this._stopDispatching(); // 恢复状态
    }
}

3、 waitFor

可以用来管理各 Stores 的一些依赖, 比如等待其他 Stores 的回调函数执行完后, 再执行当前 Store 的回调. 他也用于 FluxStoreGroup 中, 等待所有状态更新后, 再去触发 React 的 state 变化, 这个后面介绍 FluxContainer 时再详细说明

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 传入DispatchToken数组, DispatchToken在register时生成
waitFor(ids: Array<DispatchToken>): void { 
    for (var ii = 0; ii < ids.length; ii++) {
      var id = ids[ii];
      if (this._isPending[id]) { // 等待ids中的回调执行完毕
         continue;
      }
      this._invokeCallback(id); // 执行当前的回调函数
    }
}

Stores

源码中分为FluxStore、FluxReduceStore, 具体调用链关系如下

用户定义的 Store 类继承于 FluxReduceStore, 而 FluxReduceStore 继承于 FluxStore. FluxStore 作为抽象类使用, 不直接用于继承和实现, FluxReduceStore 也不直接用于实例化, 所有的实例化都需要调用方自定义继承于 FluxReduceStore 的 Store 类.

关键内容说明

1、dispatcher

Store 实例化时必须要传入 dispathcer (由 Dsipatcher 实现) 用于向注册表中注册回调函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class FluxStore {
  ...
  constructor(dispatcher: Dispatcher<any>): void {
    this.__changeEvent = 'change'; // 定义状态变更事件名
    this.__dispatcher = dispatcher; // 初始化dispatcher
    this.__emitter = new EventEmitter(); // 初始化事件发生器
    this._dispatchToken = dispatcher.register((payload) => {
      this.__invokeOnDispatch(payload);
    }); // 向dispatch注册表中注册回调函数
  }
}

2、 reduce

用于改变当前状态的函数, 一般是 switch...case 的实现, 根据传入的 state 和 action 来判断是否需要更新当前的状态

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// state: 当前状态, action: Action触发的payload
reduce(state: number, action: Object): number {
  switch(action.type) { // 根据触发的类型, 选择性的更新state
    case: 'add':
      return state + action.value;
    case: 'double':
      return state * 2;
    default:
      return state;
  }
}

3、 addListener

允许 Store 实例监听 change 状态改变事件, 并对监听回调函数进行处理. 一般会在 React 组件中对此事件进行订阅

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
addListener(callback: (eventType?: string) => void): {remove: () => void} {
    // 当状态改变时, 会触发chagne事件
    return this.__emitter.addListener(this.__changeEvent, callback);
}

4、 __invokeOnDispatch

该方法用于监听 Action 的事件触发, 并且根据用户定义的 reduce 去更新状态, 当有状态变更后会广播一个 change 事件通知 Views.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
__invokeOnDispatch(action: Object): void {
    this.__changed = false; // 清除更新状态
    const startingState = this._state; // 缓存当前state
    // 获取更新后的state
    const endingState = this.reduce(startingState, action); 
    // 如果更新前后state的值不一样
    if (!this.areEqual(startingState, endingState)) {
      this._state = endingState; // 将当前state更新到最新值
      this.__emitChange(); // 将this.__changed置为true
    }

    if (this.__changed) {
      // 触发'change'事件, 通知组件有状态更新
      this.__emitter.emit(this.__changeEvent);
    }
}

Views

这一层是通过 FluxContainer、FluxContainerSubscriptions 来实现对 React Components的封装, 将 React 组件与相应的 Stores 关联起来, 并订阅监听 Stores 的变化, 对变化做出响应, 从而更新视图

1、 FluxContainer

其包含两个函数 create 和 createFunctional, 他们的唯一区别就是分别用来 React 的处理类组件和函数式组件, createFunctional 会把函数式组件转换成类组件, 然后再使用 create 进行处理. 所以我们重点看下 create 函数.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function create<DefaultProps, Props, State>(
  Base: ReactClass<Props>, // React类组件
  options?: ?Options, // 可选项
): ReactClass<Props>

a) Base: 是 React 的类组件, Flux 要求组件传入的组件内必须实现 getStores (获取 Stores 列表)、calculateState (获取 Store 中的状态) 两个方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class FooContainer extends Component {
  static getStores(props) {
    return [FooStore];
  }

  static calculateState(prevState, props) {
    return {
      foo: FooStore.getState(),
    };
  }

  render() {
    return <FooView {...this.state} />;
  }
}
module.exports = FluxContainer.create(FooContainer);

b) options: 有三个可选项, 分别是 pure、withProps (作用于getStores、calculateState)、withContext (作用于getStores、calculateState)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const DEFAULT_OPTIONS = {
  pure: true, // 是否是纯组件
  withProps: false, // 是否依赖于当前的props
  withContext: false, // 是否依赖于上下文
};

2、 FluxContainerSubscriptions

每一个被 Flux 封装的组件内部都会有一个 FluxContainerSubscriptions 实例, 该实例是用来订阅 Stores 的变化的. 让我们来看一下关键代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class FluxContainerSubscriptions {
  ...
  setStores(stores: Array<FluxStore>): void {
    ...
    // 创建Store的change事件监听函数, 并逐个注入Store中, 当有一个Store状态发证变化, 则属性changed置为true
    const setChanged = () => { changed = true; };
    this._tokens = stores.map((store) => store.addListener(setChanged));
    ...
    // 向FluxStoreGroup中注册回调函数, 当所有的Dispatcher注册表中所有的函数执行完后, 触发此回调
    const callCallbacks = () => {
      if (changed) {
        this._callbacks.forEach((fn) => fn());
        changed = false;
      }
    };
    this._storeGroup = new FluxStoreGroup(stores, callCallbacks);
  }
  // 添加监听函数, 订阅Stores状态的变化
  addListener(fn: () => void): void {
    this._callbacks.push(fn);
  }
  ...
}

3、 ContainerClass

Flux 最终导出的组件, 组件通过订阅 Stores 的状态变化, 将最新的 state 状态通过 React 的 setState 触发视图更新

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class ContainerClass extends Base {
    ...
    constructor(props: Props, context: any) {
      ...
      this._fluxContainerSubscriptions = new FluxContainerSubscriptions();
      // 初始化当前Stores, 并监听Stores变化
    this._fluxContainerSubscriptions.setStores(getStores(props, context));
      // 向订阅列表中添加视图更新回调
      this._fluxContainerSubscriptions.addListener(() => {
        this.setState((prevState, currentProps) =>
          getState(prevState, currentProps, context),
        );
      });
      ...
    }
    ...
}

三、总结

Flux中有好几个监听事件, 容易导致混乱, 这里总结梳理一下

1、 初始化: FluxReduceStore 和 FluxStoreGroup 分别将各自的回调函数注册到 Dispatcher 的 callback 列表中, 用于订阅 Store 的变化

2、 当页面中的事件触发某个 action 时, action 会去轮训 Disptacher 注册表, 并按顺序逐个出发回调函数

3、 Dispatcher 优先触发单个 Store 的 callback, 即根据 action 判断是否需要更新 Store 对应的 state, 从而决定是否需要触发 change 更新事件, 假设状态有变化, 则广播 change 事件

4、 单个 Store 接收到 change 事件, 则将 Store 的变化状态写进自己的 changed 标识位, 并作为 FluxStoreGroup 订阅事件的输入

5、 所有单个 Store 的 callback 调用结束后, Dispatcher 最后会触发FluxStoreGroup 的 callback

6、 此时 FluxStoreGroup 会根据 changed 标识位的情况, 选择是否要触发视图的更新, 即是否调用 setState

以上就是 Flux 的一些介绍解析, 其采用集中式的单向数据流的监听机制, 管理着 React 组件的状态, 使得状态的变化和视图的更新得以收拢, 便于管理. 可以看到 Flux 与 React 是强耦合的, 是特定场景的产物, 只适用于 React 技术框架, 而且官方说还处在维护模式下, 如果希望更加完善或与框架解耦的解决方案, 可以考虑使用 Redux、MobX、Recoil

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
暂无评论
推荐阅读
Uniswap V3 释疑: 集中流动性, 无常损失和滑点
Uniswap 协议是一组原生的 ETH 的智能合约,它可以实现 ERC20 代币与 ERC20 代币的交换, 以及 ERC20 代币与 ETH 之间的的交换。
Tiny熊
2023/01/09
2K0
如何理解Uniswap v3 流动性头寸的价值
请跳到此系列文章的_part 1_[4] 和 _part 2_[5] , 您可以学习到为何 Uniswap v3 流动性代币[译者注:即头寸]为何类似于看涨期权空头和看跌期权空头[的组合,译者注]
Tiny熊
2023/01/09
1K0
如何理解Uniswap v3 流动性头寸的价值
uniswap的工作原理(下)
市场价格=池子里DAI的数量/池子里ETH的数量(P市场=X/Y)。假设市场数量趋近于无穷大,兑换价格无限趋近于X/Y
用户7976544
2020/11/14
2.6K1
uniswap的工作原理(下)
解读Dex中的无常损失:原理,机制,公式推导
本文源于十四正在撰写的《UniSwap从V1到V3的去中心化交易所之路》中用作资料援引,也发现市面文章的推导公式都颇为复杂,且重要逻辑转换缺乏描述,而无常损失又是Dex中AMM的重要部分,故单独成文。
十四君
2023/02/20
7900
解读Dex中的无常损失:原理,机制,公式推导
Raydium 协议
在不到一年的时间里,我们已经看到去中心化金融 (DeFi) 从一个流行词发展成为一个完整的生态系统,为个人财务自主权和信任设定了新的标准。DeFi 体现了去中心化和加密货币的一切。它允许每个人访问传统上属于大型银行、企业、风险基金和贸易公司领域的工具,而不会牺牲自主权。
公众号---人生代码
2021/09/18
7270
Raydium 协议
一文讲清-NFT市场新秀SudoSwap的AMM机制-创新挑战与局限
NFT交易市场的近期颓势频现,整个市场的流动性大幅降低,而此时8月异军突起的SudoSwap则凭借一超多强的增长数据,让基于AMM机制的交易市场映入大众视野。
十四君
2023/02/20
7520
一文讲清-NFT市场新秀SudoSwap的AMM机制-创新挑战与局限
如何在合约中集成 Uniswap v3
如果你还不熟悉Uniswap[4],它是一个去中心化的交易所(DEX),依靠外部流动性提供者将代币添加到流动池配对中,用户可以直接交易这些代币。
Tiny熊
2021/06/10
2.4K0
如何在合约中集成 Uniswap v3
采用延时喂价还被黑?Warp Finance 被黑详解
2020 年 12 月 18 日,据慢雾区情报 DeFi 项目 Warp Finance 遭受闪电贷攻击。以下是慢雾安全团队对整个攻击流程的详细分析。
辞令
2020/12/22
8470
采用延时喂价还被黑?Warp Finance 被黑详解
从ArtexSwap看Dex的另一种架构
ArtexSwap 是一个去中心化交易所,通过使用Artela EVM++和Aspect技术,去解决MEV风险和Rug Pull问题,提升了交易安全性和效率,适用于需要高安全性和灵活性的去中心化交易场景。
十四君
2024/07/12
1740
从ArtexSwap看Dex的另一种架构
Tokenlon协议初步介绍
Tokenlon是基于以太坊的去中心化交易所,实现了0x协议,提供链下订单簿,链上结算的功能;报价模型采用传统的柜台报价模式(RFQ/RFS)。
路之遥
2022/09/26
5710
剖析DeFi交易产品之UniswapV3:概述篇
UniswapV3 于 2021 年 5 月上线,相比 UniswapV2,改动很大,也变得复杂很多,最主要的有以下这几点改动:
Keegan小钢
2023/11/07
7180
剖析DeFi交易产品之UniswapV3:概述篇
defi 去中心化金融
想象一下,有个大学时的朋友联络你说:「嘿,我有个商业设想。我想开发一个做市机器人。不管谁来询价,我随时都能报出一个价格,我的定价算法会用 x * y = k。差不多就这些。想投资吗?」
公众号---人生代码
2020/08/05
6240
defi 去中心化金融
UniswapX研报(上):总结V1-3发展链路,解读下一代 DEX的原理创新与挑战
鲜有一个协议的问世,能彻底改变一个链上生态的格局,uniswapV1是一个而uniswapX又是一个。
十四君
2023/09/01
5200
UniswapX研报(上):总结V1-3发展链路,解读下一代 DEX的原理创新与挑战
如何对接 Uniswap V2 兑换代币
在上一篇文章中,我们通过 大概 100 行代码,了解了 Uniswap 的运行原理。
Tiny熊
2022/11/07
1.5K0
如何对接 Uniswap V2 兑换代币
使用Uniswap V2部署自己的去中心化交易所
Dapp链接:https://www.chainpip.com/dapp-view/6724
fingernft
2022/10/24
1.4K0
使用Uniswap V2部署自己的去中心化交易所
HFT系列:基于限价订单薄(LOB)失衡的量化策略研究与工具
充分了解限价订单薄(LOB)的运行动态是获得交易优势的一种可行的方式。LOB是未完成订单的记录,是大多数现代交易所的核心。在这样一个熙熙攘攘的市场,买家和卖家不断调整他们的出价和报价,以应对市场的涨落。
量化投资与机器学习微信公众号
2023/09/05
1.1K0
HFT系列:基于限价订单薄(LOB)失衡的量化策略研究与工具
Sushiswap 和 SUSHI 指南
Sushiswap最初是去中心化交易所 Uniswap 的社区分支,它使用恒定总和自动做市商 (AMM) 而不是传统的订单簿系统。
公众号---人生代码
2021/10/12
1.3K0
Sushiswap 和 SUSHI 指南
剖析DeFi交易产品之Uniswap:V2上篇
在 DeFi 赛道中,DEX 无疑是最核心的一块,而 Uniswap 又是整个 DEX 领域中的龙头,如 SushiSwap、PancakeSwap 等都是 Fork 了 Uniswap 的。虽然网上关于 Uniswap 的文章已经挺多,但大多都只是从机制上进行介绍,很少谈及具体实现,也存在一些问题没能解答,比如:手续费分配是如何实现的?最优路径是如何得出的?TWAP 怎么用?注入流动性时返回多少 LP Token 是如何计算的?因此,我从代码层面去剖析 Uniswap,搞清楚这些问题,同时也对 Uniswap 从整体到细节都有所理解。
Keegan小钢
2021/08/20
1.7K0
聊聊DeFi应用架构设计之道
DeFi 应用跟传统应用的差异性还是比较大的,商业模式不同,产品模型也不同,就连落地实现的技术栈也有很大不同。一般,传统应用也称为 Web2 应用,而 DeFi 应用则可被归入 Web3 之列。
Keegan小钢
2022/03/04
9380
聊聊DeFi应用架构设计之道
Uniswap v3 DPI-ETH 流动性挖矿计划即将推出!
地点:Uniswap V3 对:DPI — ETH 费用等级(必须使用此费用等级):0.3% 矿池 ID:0x9359c87B38DD25192c5f2b07b351ac91C90E6ca7 持续时间:太平洋标准时间 8 月 20 日下午 2 点, 奖励代币INDEX2ST 0月 2 日下午
公众号---人生代码
2021/10/12
8170
Uniswap v3 DPI-ETH 流动性挖矿计划即将推出!
推荐阅读
相关推荐
Uniswap V3 释疑: 集中流动性, 无常损失和滑点
更多 >
LV.0
珠海市登链科技有限公司CTO
目录
  • 一、介绍
    • 1、Dispatcher
    • 2、Stores
    • 3、Views
    • 4、Actions
  • 二、深入源码
    • Dsipatcher
      • 1、 _callbacks
      • 2、 dispatch
      • 3、 waitFor
    • Stores
      • 1、dispatcher
      • 2、 reduce
      • 3、 addListener
      • 4、 __invokeOnDispatch
    • Views
      • 1、 FluxContainer
      • 2、 FluxContainerSubscriptions
      • 3、 ContainerClass
  • 三、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档