软件的设计是为了服务理念。只有懂了设计理念,才能明白为了实现这样的理念需要如何架构。所以,在我们深入源码架构之前,先来聊聊React理念。

# React理念

我们可以从官网看到React的理念:

我们认为,React 是用 JavaScript 构建快速响应的大型 Web 应用程序的首选方式。它在 Facebook 和 Instagram 上表现优秀。

那么该如何理解快速响应?可以从两个角度来看:

  • 速度快
  • 响应自然

React是如何实现这两点的呢?

# 理解“速度快”

每当聊到一款前端框架,拉出来比比渲染速度成了老生常谈。

这里提供了各种框架渲染速度的对比

我们经常用“前端三大框架”指ReactVueAngular。相比于使用模版语言的VueAngular,使用原生js(JSX仅仅是js的语法糖)开发UI的React在语法层面有更多灵活性。

然而,高灵活性意味着高不确定性。考虑如下Vue模版语句:

<template>
    <ul>
        <li>0</li>
        <li>{{ name }}</li>
        <li>2</li>
        <li>3</li>
    </ul>
</template>

当编译时,由于模版语法的约束,Vue可以明确知道在li中,只有name是变量,这可以提供一些优化线索。

而在React中,以上代码可以写成如下JSX

function App({name}) {
    const children = [];
    for (let i = 0; i < 4; i++) {
        children.push(<li>{i === 1 ? name : i}</li>)
    }
    return <ul>{children}</ul>
}

由于语法的灵活,在编译时无法区分可能变化的部分。所以在运行时,React需要遍历每个li,判断其数据是否更新。

基于以上原因,相比于VueAngular,缺少编译时优化手段的React为了速度快需要在运行时做出更多努力。

比如

  • 使用PureComponentReact.memo构建组件
  • 使用shouldComponentUpdate生命周期钩子
  • 渲染列表时使用key
  • 使用useCallbackuseMemo缓存函数和变量

由开发者来显式的告诉React哪些组件不需要重复计算、可以复用。

在后面源码的学习中,我们会看到这些优化手段是如何起作用的。比如经过优化后,React会通过bailoutOnAlreadyFinishedWork方法跳过一些本次更新不需要处理的任务。

# 理解“响应自然”

该如何理解“响应自然”?React给出的答案是将人机交互研究的结果整合到真实的 UI 中

设想以下场景:

搜索框

有一个地址搜索框,在输入字符时会实时显示地址匹配结果。

当用户输入过快时可能输入变得不是那么流畅。这是由于下拉列表的更新会阻塞线程。我们一般是通过debouncethrottle来减少输入内容时触发回调的次数来解决这个问题。

但这只是治标不治本。只要组件的更新操作是同步的,那么当更新开始直到渲染完毕前,组件中总会有一定数量的工作占用线程,浏览器没有空闲时间绘制UI,造成卡顿。

React核心团队成员Dan在介绍React为什么会异步(Concurrent Mode)更新组件时说: Dan关于用户体验的思考

让我们从“响应自然”的角度考虑:当输入字符时,用户是否在意下拉框能在一瞬间就更新?

事实是:并不在意。

如果我们能稍稍延迟下拉框更新的时间,为浏览器留出时间渲染UI,让输入不卡顿。这样的体验是更自然的。

为了实现这个目标,需要将同步的更新变为可中断的异步更新

在浏览器每一帧的时间中,预留一些时间给JS线程,React利用这部分时间更新组件(可以看到,在源码中,预留的初始时间是5ms)。

当预留的时间不够用时,React将线程控制权交还给浏览器使其有时间渲染UI,React则等待下一帧时间到来继续被中断的工作。

同步更新 vs 异步更新 Demo

我们有个更新很耗时的大列表,让我们看看同步更新异步更新时,输入框的响应速度

同步更新

异步更新

可以从Demo看到,当牺牲了列表的更新速度,React大幅提高了输入响应速度,使交互更自然。

# 总结

通过以上内容,我们可以看到,React为了践行“构建快速响应的大型 Web 应用程序”理念做出的努力。

这其中有些优化手段可以在现有架构上增加,而有些(如:异步可中断更新)只能重构整个架构实现。

最后再让我们看看,Dan回答网友关于React发展方向的提问:

用户向Dan提问 Dan回答

相比于新增feature,React更在意底层抽象的表现力。结合理念,相信你已经明白这意味着什么了。

# 参考资料

「英文 计划翻译」尤雨溪论JavaScript框架设计哲学:平衡