React Step by Step

react官方指引反复强调 props与 state 区别。基本上 props 类似于其他语言中的形参,state则相当于内部临时变量。一个是对外沟通的桥梁,而另一个则为交互私有变量。state 基于与 Dom 元素互动考量,这是因为通过setState 方法对 state 的变更,会加入异步队列,在合适的时机进行页面渲染。

ES6

  1. 不使用ES6
    • class关键字继承 React.Component 类
    • 使用 create-react-class 模块来创建
  2. 声明默认属性
    • 自定义属性对象写到类的 defaultProps 属性
    • createReactClass 方法创建组件,那就需要在参数对象中定义 getDefaultProps 方法,并且在这个方法中返回包含自定义属性的对象
  3. 设置初始状态
    • class 关键字创建组件,在 constructor 中给 this.state 赋值
    • 使用 createReactClass 方法创建组件,要写一个 getInitialState 方法,并让这个方法返回你要定义的初始属性
  4. 自动绑定
    • 通过 ES6 class 生成的实例,实例上的方法不会绑定 this
    • 使用 class 关键字创建的 React 组件,组件中的方法是不会自动绑定 this
      • 因此,需要在 constructor 中为方法手动添加 .bind(this) [建议]
    • 使用 createReactClass 方法创建组件,组件中的方法会自动绑定至实例
    • 目前还处于实验性阶段的 Babel 插件 Class Properties(注通常意义上的箭头函数属性)
  5. Mixin 混入
    • ES6本身是不包含混入支持
    • 相似功能-> 横切关注点
    • createReactClass 创建React组件时,可引入混入
      • 配置类似mixins:[SetIntervalMixin]选项
    • 多个混入定义相同生命周期方法,执行顺序与定义顺序一致

此项织入同vue的功能一致,所不同的是在最新版react,官方推荐使用高阶组件来解决数据共享问题

JSX

  1. 不使用jsx
    • 每一个JSX元素都只是 React.createElement(component, props, ...children) 的语法糖
    • react组件类型
      • 可以是一个字符串
      • 亦可以是React.Component的子类
      • 当组件是无关的时候,它可以是一个普通的函数
  2. React.createElement
  3. jsx语法

        class Hello extends React.Component {
            render() {
                return <div>Hello {this.props.toWhat}</div>;
            }
        }
    
        ReactDOM.render(
            <Hello toWhat="World" />,
            document.getElementById('root')
        );
    

协调(Reconciliation)

  1. 目的
    • 在单一时间点你可以考虑render()函数作为创建React元素的树
    • React需要算出如何高效更新UI以匹配最新的树,React基于两点假设
      • 两个不同类型的元素将产生不同的树
      • 通过过渲染器附带key属性,开发者可以示意哪些子元素可能是稳定的
  2. 对比算法
    • 不同类型的元素
      • 每当根元素有不同类型,React将卸载旧树并重新构建新树
      • 组件实例会调用componentWillUnmount() -> componentWillMount() -> componentDidMount()
      • 任何与旧树有关的状态都将丢弃
    • 相同类型的DOM元素
      • React则会观察二者的属性[即树的状态],保持相同的底层DOM节点,并仅更新变化的属性。
      • 在处理完DOM元素后,React递归其子元素。
    • 相同类型的组件元素
      • 当组件更新时,实例仍保持一致,以让状态能够在渲染之间保留
      • React通过更新底层组件实例的props来产生新元素
      • 并在底层实例上依次调用componentWillReceiveProps() -> componentWillUpdate() -> render()
    • 递归子节点
      • 默认时,React仅在同一时间点递归两个子节点列表,并在有不同时产生一个变更
    • Keys
      • React使用key来匹配原本树的子节点和新树的子节点
      • key必须在其兄弟节点中是唯一,但当索引用作key时,组件状态在重新排序时会有问题。
      • 万不得已,你可以传递他们在数组中的索引作为key,建议元素在没有重排情况下使用
  3. 协调
    • 当前实现,子树在其兄弟节点中移动,是无法告知其移动位置
    • React依赖于该启发式算法(即合理假设),若其背后的假设没得到满足,则其性能将会受到影响
      • 算法无法尝试匹配不同组件类型的子元素。
      • Keys应该是稳定的,可预测的,且唯一的。
      • 非稳定的key会使得大量组件实例和DOM节点进行不必要的重建,且丢失子组件的状态,性能下降。

Context

  1. 应用场景(数据共享)
    • React.createContext
    • Context 提供了一种在组件之间共享props值的方式,而不必通过组件树的每个层级显式地传递 props
    • Context 设计目的是为共享那些被认为对于一个组件树而言是“全局”的数据,当前认证的用户、主题或首选语言
    • React应用数据是通过 props 属性由上向下(由父及子)的进行传递,使用context可以避免通过中间元素传递
    • 应用于多个层级的多个组件需要访问相同数据的情景。
  2. 相关API
    • const {Provider, Consumer} = React.createContext(defaultValue);
    • <Provider value={/* some value */}>
    • Comsumer 一个可以订阅 context 变化的 React 组件。
  3. 父子耦合
    • 可以通过 context 向下传递一个函数,以允许 Consumer 更新 contex

Fragment

  1. 存在的意义
    • html元素在某些情况下,是不允许添加无意义标签,比如 trtd 之间,但 React 组件又必须存在一个根节点,Fragment 的出现解决了这一问题
    • Fragments 聚合一个子元素列表,并且不在DOM中增加额外节点
    • 常见模式是为一个组件返回一个子元素列表
  2. React.Fragment 组件
    • <></> 是 的语法糖
    • <></>语法不接受键值或属性

Portals(弹窗对话框)

  1. 目的
    • Portals 提供一种将子节点渲染到父组件以外的DOM节点的方式
    • ReactDOM.createPortal(child, container)
      • 第一个参数(child)是任何可渲染的 React 子元素, 如 一个元素,字符串或碎片
      • 数(container)则是一个 DOM 元素。
  2. 应用
    • 通常从组件的 render 方法返回一个元素,该元素仅能装配 DOM 节点中离其最近的父元素
    • 典型用例
      • 当父组件有 overflow: hidden 或 z-index 样式
      • 需要子组件能够在视觉上“跳出(break out)”其容器,如 对话框、hovercards以及提示框
  3. 通过 Portals 进行事件冒泡
    • portal 仍存在于 React 树中,而不用考虑其在 DOM 树中的位置
    • 一个从 portal 内部会触发的事件会一直冒泡至包含 React 树 的祖先
  4. 错误边界仅可以捕获其子组件的错误

Web Components

  1. React vs web
    • Web组件为可重用组件提供了强大的封装能力
    • React则是提供了保持DOM和数据同步的声明式库
    • React组件与web组件内都可使用对方
    • 由web组件触发的事件可能无法通过React渲染树来正确冒泡,可能需要手动捕获

高阶组件(hoc)

  1. 定义
    • const EnhancedComponent = higherOrderComponent(WrappedComponent)
    • 高阶组件就是一个没有副作用的纯函数,且该函数接受一个组件作为参数,并返回一个新的组件
    • 对比组件将props属性转变成UI,高阶组件则是将一个组件转换成另一个新组件
  2. 应用场景
    • 使用高阶组件(HOC)解决交叉问题
    • 移除混入,mixins)技术产生的问题要比带来的价值大
    • 与混入向组件内部注入,高阶组件更像是变更组件外部生存环境
    • 比如Redux的connect方法和Relay的createContainer.
  3. 注意点
    • 不要改变原始组件,使用组合技术
    • 高阶组件就是参数化的容器组件定义
    • 容器组件是专注于在高层次和低层次关注点之间进行责任划分的策略的一部分
    • 容器组件处理诸如数据订阅和状态管理等事情,并传递props属性给展示组件
    • 展示组件负责处理渲染UI等事情
    • 将不相关的props属性传递给包裹组件
    • 高阶组件给组件添加新特性,不大幅修改原组件的接口(props属性)
    • 最大化使用组合,高阶组件会接收额外的参数
  4. 高阶组件签名
    • 函数签名如下
    • const ConnectedComment = connect(commentSelector, commentActions)(Comment)
    • connect是一个返回函数的函数(高阶函数)
    • React中组件可以是函数
  5. 约定俗成
    • 包装显示名字以便于调试
    • 不要在render函数中使用高阶组件
    • 必须将静态方法做拷贝
      • 理由:当使用高阶组件包装组件,原始组件被容器组件包裹,也就意味着新组件会丢失原始组件的所有静态方法
    • Refs属性不能传递(理由refs是一个伪属性,React对它进行了特殊处理)

Redux

  1. 动机
    • 不断变化的state,厘清变化和异步
    • state 一个对象树
      • 基本数据类型,数组,对象
      • 或Imutable.js生成的数据结构
      • 当state变化时需要返回全新的对象,而不是修改传入的参数
    • action 普通对象
      • action只是描述了有事情发生什么的普通对象,必须拥有一个type域,指示器
    • reducer 函数
      • 描述 action 如何改变 state 树, 是一个纯函数(相同的输入得到相同的输出)
    • Redux 所有的state都以一个对象树的形式储存在一个单一的store中
    • 可以用action追溯应用的每一次修改,Redux试图让state的变化变得可预测
  2. 三大原则
    • 单一数据源
      • 整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中
    • state 是只读的
      • 唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象
    • 使用纯函数来执行修改
      • 描述 action 如何改变 state tree ,编写 reducers(纯函数)
      • reducer只是函数,可以控制它被调用的顺序,传入附加数据,或复用reducer处理通用任务
  3. 溯源其它
    • Redux规定将模型的更新逻辑集中于一个特定层(flux里的store,Redux里的reducer)
    • 不允许直接修改数据,而是用普通对象来对变更数据进行描述
    • Redux没有dispatcher概念,并假定永不变动数据,通常在reducer中返回一个新对象来更新state
    • Elm 函数式编程语言
    • Immutable 不可变数据
      • 可实现持久数据结构的 JavaScript 库
      • 提供了许多永久不可变数据结构
        • List,Stack,Map,OrderedMap,Set,OrderedSet和Record
    • 可变数据(引用类型)
      • 可变的好处是节约内存
      • 在 JS 中,objects, arrays,functions, classes, sets, maps 都是可变数据。
    • 不可变数据
      • js中数字和字符串皆属于不可变数据,即不会影响之前的数据
      • 源于函数式编程,不可变每次操作产生新的对象,新的数据结构(数据拷贝)
      • 由于可变在程序越大,代码的可读性,复杂度越来越高(因为其每一变动,可能导致另一变动,会产生副作用)
  4. 数据拷贝
    • 赋值 与const定义数据,后者避免了赋值引用
    • Object.assign({},x) 浅拷贝
    • deconstruction 解构 浅拷贝(只拷贝一层)
    • 深拷贝方式
      • 原生
      • json 序列化再反序列化 const y = JSON.parse(JSON.stringify(x))
      • 第三方库
        • lodash
        • $.extend(true,...)
        • immutable.js 库
      • 不可变涉及到数据拷贝算法问题
    • 数据持久化
      • 数据不可变的时候,当每次操作,都不会引起初始数据的改变
      • 对函数编程而言,在一定的时期内,数据是永久存在, 故可通过读取实现类似"回退/切换快照"操作
      • 问题:
        • 每次对整个数据结构进行完整的深拷贝,效率会很低
        • immutable部分持久化(即共用没变化的地方)
    • 函数编程最重要原则之一不可变数据
    • 在使用构造函数创建数组的时候可以省略掉new操作符
  5. js数组操作方法
    • concat() 创建当前数组副本,将接受到的数据添加到个副本末尾
    • slice() 返回起止位置之间的数组项(不含结束位置),此方法不会影响原数组
    • splice() 对数组进行删除,插入,替换操作,会改变原始数组
    • indexOf() lastIndex() 位置查找
    • 迭代方法(数组每一项,当前项的下标,数组对象本身(影响this))
      • every() 皆true则返回true
      • filer() 返回为true的项
      • forEach() 无返回值
      • map() 返回函数每次调用结果组成的数组
      • some() 有一项为真,则为真
    • 归并方法(累计计算)
      • reduce()
      • reduceRight() 从右项开始累计

Reducer

  1. reducer(纯函数,只执行计算)
    • 定义 (previousState, action) => newState
    • 永远不要在 reducer 里做以下操作
      • 修改传入的参数
      • 执行有副作用的操作,如API请求和路由跳转
      • 调用非纯函数,如Date.now()或Math.random()
  2. reducer合成
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!