一、React 17 中的改变

  • 新的 JSX 转换逻辑
  • 事件系统重构
  • Lane 模型的引入

1. 重构 JSX 转换逻辑

React 17 则允许我们在不引入 React 的情况下直接使用 JSX,不用手动引入React import React fron ‘react’,编译器会自动帮我们引入 JSX 的解析器。

  1. function MyComponent() {
  2. return <p>这是我的组件</p>
  3. }

会被编译器转换成这个样子:

  1. import {jsx as _jsx} from 'react/jsx-runtime';
  2. function MyComponent() {
  3. return _jsx('p', { children: '这是我的组件' });
  4. }

2. 事件系统重构

2.1 卸掉历史包袱:放弃利用 document 来做事件的中心化管控

事件的中心化管控不会再全部依赖 document,管控相关的逻辑被转移到了每个 React 组件自己的容器 DOM 节点中。

  1. const rootElement = document.getElementById("root");
  2. ReactDOM.render(<App />, rootElement);

那么事件管控相关的逻辑就会被安装到 root 节点上去。这样一来, React 组件就能够自己玩自己的,再也无法对全局的事件流构成威胁了。

2.2 拥抱新的潮流:放弃事件池

在 React 17 之前,合成事件对象会被放进一个叫作“事件池”的地方统一管理。这样做的目的是能够实现事件对象的复用,进而提高性能:每当事件处理函数执行完毕后,其对应的合成事件对象内部的所有属性都会被置空,意在为下一次被复用做准备。这也就意味着事件逻辑一旦执行完毕,我们就拿不到事件对象了。

  1. function handleChange(e) {
  2. // This won't work because the event object gets reused.
  3. setTimeout(() => {
  4. console.log(e.target.value); // Too late!
  5. }, 100);
  6. }

异步执行的 setTimeout 回调会在 handleChange 这个事件处理函数执行完毕后执行,因此它拿不到想要的那个事件对象 e。

要想拿到目标事件对象,必须显式地告诉 React——我永远需要它,也就是调用 e.persist() 函数。

  1. function handleChange(e) {
  2. // Prevents React from resetting its properties:
  3. e.persist();
  4. setTimeout(() => {
  5. console.log(e.target.value); // Works
  6. }, 100);
  7. }

React 17 中放弃事件池,为每一个合成事件创建新的对象。我们不需要 e.persist(),也可以随时随地访问我们想要的事件对象。

2.3 Lane 模型的引入

Lane 模型提供了一个新的优先级排序的思路,相对于 expirationTime 来说,它对优先级的处理会更细腻,能够覆盖更多的边界条件。

二、如何深入的了解一个前端框架

1. 官方文档

React 文档在前端框架文档中属于相当优秀的范本。

2. 调用栈就是你的学习地图

比如当你想要了解 Hooks,那么就可以尝试去观察不同 Hooks 调用所触发的函数调用栈,从中找出出镜率最高的那些函数,它们大概率暗示着 Hooks 源码的主流程。事件系统、render 过程之类的也是同理。观察调用栈,寻找共性,然后点对点去阅读关键函数的源码,这将大大降低我们阅读源码的难度。

首先理解框架项目的架构分层,然后阅读源码的姿势就可以多样化一些了:可以尝试分层阅读,一次搞清楚一个大问题,最后再把整个思路按照架构分层的逻辑组合起来;也可以继续借助调用栈,通过观察一个完整的执行流程(比如 React 的首屏渲染过程)中所涉及的函数,自行将每个层次的逻辑对号入座,然后再向下拆分。