1. JSX 代码是如何成为 DOM 的
JSX 是 JavaScript 的一种语法扩展,它和模板语言很接近,但是它充分具备 JavaScript 的能力。
JSX 会被编译为 React.createElement(), React.createElement() 将返回一个叫作“React Element”的 JS 对象。
ReactElement 把传入的参数按照一定的规范,“组装”进了 element 对象里,这个 ReactElement 对象实例,本质上是以 JavaScript 对象形式存在的对 DOM 的描述,也就是“虚拟 DOM”
通过ReactDOM.render最终渲染到DOM容器中
2. 为什么 React 16 要更改组件的生命周期?
在 React 15 中,大家需要关注以下几个生命周期方法:
constructor()
componentWillReceiveProps()
shouldComponentUpdate()
componentWillMount()
componentWillUpdate()
componentDidUpdate()
componentDidMount()
render()
componentWillUnmount()
进化的生命周期方法:React 16 生命周期工作流详解
主要差异在于,废弃了 componentWillMount, 用 getDerivedStateFromProps 代替 componentWillReceiveProps。
这个变化的原因主要是为了引入Fiber架构
在 React 16 之前,每当我们触发一次组件的更新,React 都会构建一棵新的虚拟 DOM 树,通过与上一次的虚拟 DOM 树进行 diff,实现对 DOM 的定向更新。这个过程,是一个递归的过程。下面这张图形象地展示了这个过程的特征:
同步渲染的递归调用栈是非常深的,只有最底层的调用返回了,整个渲染过程才会开始逐层返回。这个漫长且不可打断的更新过程,将会带来用户体验层面的巨大风险:同步渲染一旦开始,便会牢牢抓住主线程不放,直到递归彻底完成。在这个过程中,浏览器没有办法处理任何渲染之外的事情,会进入一种无法处理用户交互的状态。因此若渲染时间稍微长一点,页面就会面临卡顿甚至卡死的风险。
React 16 引入的 Fiber 架构,恰好能够解决掉这个风险:Fiber 会将一个大的更新任务拆解为许多个小任务。每当执行完一个小任务时,渲染线程都会把主线程交回去,看看有没有优先级更高的工作要处理,确保不会出现其他任务被“饿死”的情况,进而避免同步渲染带来的卡顿。在这个过程中,渲染线程不再“一去不回头”,而是可以被打断的,这就是所谓的“异步渲染”,它的执行过程如下图所示:
Fiber 架构的重要特征就是可以被打断的异步渲染模式。但这个“打断”是有原则的,根据“能否被打断”这一标准,React 16 的生命周期被划分为了 render和 commit两个阶段,而 commit阶段又被细分为了 pre-commit和 commit。每个阶段所涵盖的生命周期如下图所示:
来看下三个阶段各自有哪些特征:
- render 阶段:纯净且没有副作用,可能会被 React 暂停、终止或重新启动。
- pre-commit 阶段:可以读取 DOM。
- commit 阶段:可以使用 DOM,运行副作用,安排更新。
总的来说,render 阶段在执行过程中允许被打断,而 commit 阶段则总是同步执行的。
为什么这样设计呢?简单来说,由于 render阶段的操作对用户来说其实是“不可见”的,所以就算打断再重启,对用户来说也是零感知。而 commit阶段的操作则涉及真实 DOM 的渲染,再狂的框架也不敢在用户眼皮子底下胡乱更改视图,所以这个过程必须用同步渲染来求稳。
细说生命周期“废旧立新”背后的思考 在 Fiber 机制下,render 阶段是允许暂停、终止和重启的。当一个任务执行到一半被打断后,下一次渲染线程抢回主动权时,这个任务被重启的形式是“重复执行一遍整个任务”而非“接着上次执行到的那行代码往下走”。这就导致 render 阶段的生命周期都是有可能被重复执行的。
带着这个结论,再来看看 React 16 打算废弃的是哪些生命周期:
componentWillMount; componentWillUpdate; componentWillReceiveProps。 这些生命周期的共性,就是它们都处于 render 阶段,都可能重复被执行,而且由于这些 API 常年被滥用,它们在重复执行的过程中都存在着不可小觑的风险。
React 16 改造生命周期的主要动机是为了配合 Fiber 架构带来的异步渲染机制。在这个改造的过程中,React 团队精益求精,针对生命周期中长期被滥用的部分推行了具有强制性的最佳实践。这一系列的工作做下来,首先是确保了 Fiber 机制下数据和视图的安全性,同时也确保了生命周期方法的行为更加纯粹、可控、可预测。
3. 数据是如何在React组件之间流动的
基于 props 的单向数据流
组件,从概念上类似于JavaScript函数,他接受任意的入参(即props),并返回用于描述页面展示内容的React元素
父-子组件通信: React 的数据流是单向的,父组件可以直接将 props 传入子组件
子-父组件通信: props的形式也可以是多样的,父组件传递给子组件的是一个绑定了自身上下文的函数,那么子组件在调用该函数时,就可以将想要交给父组件的数据以函数入参的形式给出去,以此来间接地实现数据从子组件到父组件的流动。
利用“发布-订阅”模式驱动数据流: 处理复杂的数据流
全局通信方式“Context API”:
Redux: