Day06:响应式系统与React | 青训营笔记

99 阅读4分钟

这是我参与「第五届青训营」伴学笔记创作活动的第 6 天。

React的应用

  • 前端应用开发
  • 移动原生应用开发
  • 桌面应用开发(结合Electron)
  • 3D图形应用开发(结合react-three-fiber)

React的发展历史

  1. 2010年,Facebook在其PHP生态中引入了XHP框架,首次引入了“组合式组件”的思想,启发了后来React的设计
  2. 2011年,Facebook的工程师Jordan Walke创造了React的原型FaxJS
    • 同时支持客户端/服务端渲染(无痛切换)
    • 响应式设计:界面随状态变化自动更新
    • 高性能
    • 结构化:组件封装、函数式定义、声明式视图
  3. 2012年,在Facebook收购Instagram后,FaxJS项目在内部得到使用,Jordan Walke基于FaxJS的经验创造了React
  4. 2013年,Reac正式开源,在2013 JSConf上Jordan Walke介绍了这款全新的框架
    • React被定义为绑定用户界面的JS库(而不是前端框架)
  5. 2014年,React生态大爆发,各种围绕React的新工具、框架开始涌现
    • React Native:原生应用开发
    • Redux:状态管理库
    • GraphQL:继Restful后的新接口标准
    • React AR & VR
    • React组件库

字节内部大部分前端项目都是基于React~

React相对传统编程方式的改进

UI的传统编程方式的痛点

传统前端采用绑定onclick等事件利用DOM API手动修改网页内容,是过程式思想:

  • 状态更新,UI不会自动更新,需要手动地调用DOM进行更新
  • 欠缺基本的代码层面的封装隔离,代码层面没有组件化
  • UI之间的数据依赖关系需要手动维护,如果依赖链路长,则会遇到“Callback Hell”

响应式与转换式

  • 转换式系统:把输入转换为输出(例如编译器、数值计算应用)
  • 响应式系统:监听事件,以消息驱动(例如监控系统、UI界面)
    graph LR
    事件 --> 执行既定的回调函数 --> 状态变更
    
    对于前端而言,需要增加一个“UI更新”的阶段
    graph LR
    事件 --> 执行既定的回调函数 --> 状态变更 --> UI更新
    

前端显然更适合采用响应式系统的方式编写。针对UI的传统编程方式的痛点,合理的前端响应式系统应当做到如下几点:

  • 状态更新,UI自动更新
  • 前端代码组件化,可复用,可封装
  • 状态之间的互相依赖关系,只需声明即可

    例如,变量A在逻辑上始终满足A=B-C的关系:

    • 传统编程过程中需要在把每次修改B或者C的时候收到加上修改A来满足这一关系
    • 合理的前端响应式系统应当只需要说明A=B-C的关系,在修改B或者C的时候A应当自动更新

React 设计与实现

组件化

  • 组件是组件的组合/原子组件
  • 组件内拥有状态,外部不可见(状态的局部性)
  • 父组件可将状态传入子组件(使父组件对子组件具有一定的控制权)

具体而言:

  • 组件声明了状态和UI的映射。
  • 组件有Props(外部传进的状态)/State(内部私有的状态)两种状态。
  • 组件可由其他组件拼装而成

状态归属问题

两个组件共享的状态,其存放位置应位于这两个节点最近的公共祖宗节点。这也意味着共享的状态会一定程度减损状态的局部性,这种现象称为“状态上升”。

不在组件内部的状态不能直接修改。为了解决共享状态的修改需求时,需要在这个公共祖宗节点给出修改状态的接口,并将这个接口逐层传递给需要修改状态的组件。

image.png

  • React 是单向数据流(而不是双向数据流),永远是父组件传递给子组件,只不过通过传递回调函数可以实现子组件修改父组件的状态
  • 如何解决状态不合理上升的问题?见第五节
  • 组件的状态改变后,如何更新 DOM?见第四节

生命周期

image.png

关键时间点:挂载(Mount)、卸载(Unmount)、更新(Update)