<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>有道技术沙龙博客 &#187; react</title>
	<atom:link href="https://techblog.youdao.com/?feed=rss2&#038;tag=react" rel="self" type="application/rss+xml" />
	<link>https://techblog.youdao.com</link>
	<description>分享有道人的技术思考</description>
	<lastBuildDate>Thu, 05 Mar 2026 10:02:57 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>从源码中来，到业务中去，React性能优化终极指南</title>
		<link>https://techblog.youdao.com/?p=1929</link>
		<comments>https://techblog.youdao.com/?p=1929#comments</comments>
		<pubDate>Thu, 27 May 2021 03:03:15 +0000</pubDate>
		<dc:creator><![CDATA[youdao]]></dc:creator>
				<category><![CDATA[技术分享]]></category>
		<category><![CDATA[大前端]]></category>
		<category><![CDATA[react]]></category>

		<guid isPermaLink="false">http://techblog.youdao.com/?p=1929</guid>
		<description><![CDATA[前言：我们从React源码入手，结合有道精品课大前端的具体业务，运用三大原则对系统进行外科手术式的优化。同时介 [&#8230;]]]></description>
				<content:encoded><![CDATA[<blockquote>
<p>前言：我们从React源码入手，结合有道精品课大前端的具体业务，运用三大原则对系统进行外科手术式的优化。同时介绍React Profiler这款工具如何帮我们定位性能瓶颈前言：我们从React源码入手，结合有道精品课大前端的具体业务，运用三大原则对系统进行外科手术式的优化。同时介绍React Profiler这款工具如何帮我们定位性能瓶颈</p>
<p>作者/ 安增平</p>
<p>编辑/ Ein</p>
</blockquote>
<p>React性能优化是在业务迭代过程中不得不考虑的问题，大部分情况是由于项目启动之初，没有充分考虑到项目的复杂度，定位该产品的用户体量及技术场景并不复杂，那么我们在业务前期可能并不需要考虑性能优化。但是随着业务场景的复杂化，性能优化就变得格外重要。</p>
<p>我们从React源码入手，结合有道精品课大前端的具体业务，运用优化技巧对系统进行外科手术式的优化。同时介绍一下React Profiler这款性能优化的利器是如何帮我们定位性能瓶颈的。</p>
<p>本文中的项目代码全部是在有道大前端组开发项目中的工作记录，如有不足欢迎在留言区讨论交流，笔芯❤<br />
<span id="more-1929"></span></p>
<h1>页面加载流程</h1>
<p><img src="https://oscimg.oschina.net/oscnet/up-052ec483a7d51c31efd59949610500b6fc0.JPEG" alt="" /></p>
<ol>
<li>假设用户首次打开页面（无缓存），这个时候页面是完全空白的；</li>
<li>html 和引用的 css 加载完毕，浏览器进行<strong>首次渲染</strong>；</li>
<li>react、react-dom、业务代码加载完毕，应用第一次渲染，或者说<strong>首次内容渲染</strong>；</li>
<li>应用的代码开始执行，拉取数据、进行动态import、响应事件等等，完毕后页面进入<strong>可交互</strong>状态；</li>
<li>接下来 lazyload 的图片等多媒体内容开始逐渐加载完毕；</li>
<li>直到页面的其它资源（如错误上报组件、打点上报组件等）加载完毕，整个页面加载完成。</li>
</ol>
<p><strong>我们主要来针对React进行剖析</strong>：</p>
<p>React 针对渲染性能优化的三个方向，也适用于其他软件开发领域，这三个方向分别是:</p>
<ol>
<li><strong>减少计算的量</strong>：React 中就是减少渲染的节点或通过索引减少渲染复杂度；</li>
<li><strong>利用缓存</strong>：React 中就是避免重新渲染（利用 memo 方式来避免组件重新渲染）；</li>
<li><strong>精确重新计算的范围</strong>：React 中就是绑定组件和状态关系, 精确判断更新的&#8217;时机&#8217;和&#8217;范围&#8217;. 只重新渲染变更的组件（减少渲染范围）。</li>
</ol>
<p>如何做到这三点呢？我们从React本身的特性入手分析。</p>
<h1>React 工作流</h1>
<p>React 是声明式 UI 库，负责将 State 转换为页面结构（虚拟 DOM 结构）后，再转换成真实 DOM 结构，交给浏览器渲染。State 发生改变时，React 会先进行Reconciliation，结束后立刻进入Commit阶段，Commit结束后，新 State 对应的页面才被展示出来。</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-72128c7e5014784753ac28496bbdcfac480.JPEG" alt="" /></p>
<p>React 的<strong>Reconciliation</strong>需要做两件事：</p>
<ol>
<li>计算出目标 State 对应的虚拟 DOM 结构。</li>
<li>寻找「将虚拟 DOM 结构修改为目标虚拟 DOM 结构」的最优方案。</li>
</ol>
<p>React 按照深度优先遍历虚拟 DOM 树的方式，在一个虚拟 DOM 上完成Render和Diff的计算后，再计算下一个虚拟 DOM。Diff 算法会记录虚拟 DOM 的更新方式（如：Update、Mount、Unmount），为Commit做准备。</p>
<p>React 的<strong>Commit</strong>也需要做两件事：</p>
<ol>
<li>将Reconciliation结果应用到 DOM 中。</li>
<li>调用暴露的hooks如：componentDidUpdate、useLayoutEffect 等。</li>
</ol>
<p><strong>下面我们将针对三个优化方向进行精准分析。</strong></p>
<h1>减少计算的量</h1>
<p>关于以上<strong>Reconciliation</strong>与<strong>Commit</strong>两个阶段的优化办法，我在实现的过程中遵循<strong>减少计算量</strong>的方法进行优化（<strong>列表项使用 key 属性</strong>)该过程是优化的重点，React 内部的 Fiber 结构和并发模式也是在减少该过程的耗时阻塞。对于<strong>Commit</strong>在执行hooks时，开发者应保证hooks中的代码尽量轻量，避免耗时阻塞，同时应避免在 <strong>CDM、CDU</strong>周期中更新组件。</p>
<p><strong>列表项使用 key 属性</strong></p>
<blockquote>
<p>特定框架中，提示也做的十分友好。假如你没有在列表中添加key属性，控制台会为你展示一片大红</p>
</blockquote>
<p><img src="https://oscimg.oschina.net/oscnet/up-4fe69e5a342647e8cc122c015bafec0c5cd.JPEG" alt="" /></p>
<p>系统会时刻提醒你记得加Key哦~~</p>
<h2>优化Render 过程</h2>
<p>Render 过程：即Reconciliation中计算出目标 State 对应的虚拟 DOM 结构这一阶段 。</p>
<p>触发 React 组件的 Render 过程目前有三种方式：</p>
<ol>
<li>forceUpdate、</li>
<li>State 更新、</li>
<li>父组件 Render 触发子组件 Render 过程。</li>
</ol>
<h3>优化技巧</h3>
<h4>PureComponent、React.memo</h4>
<p>在 React 工作流中，如果只有父组件发生状态更新，即使父组件传给子组件的所有 Props 都没有修改，也会引起子组件的 Render 过程。</p>
<p>从 React 的声明式设计理念来看，如果子组件的 Props 和 State 都没有改变，那么其生成的 DOM 结构和副作用也不应该发生改变。当子组件符合声明式设计理念时，就可以忽略子组件本次的 Render 过程。</p>
<p>PureComponent 和 React.memo 就是应对这种场景的，PureComponent 是对类组件的 Props 和 State 进行浅比较，React.memo 是对函数组件的 Props 进行浅比较。</p>
<h5>useMemo、useCallback 实现稳定的 Props 值</h5>
<p>如果传给子组件的派生状态或函数，每次都是新的引用，那么 PureComponent 和 React.memo 优化就会失效。所以需要使用 useMemo 和 useCallback 来生成稳定值，并结合 PureComponent 或 React.memo 避免子组件重新 Render。</p>
<h5>useMemo 减少组件 Render 过程耗时</h5>
<p>useMemo 是一种缓存机制提速，当它的依赖未发生改变时，就不会触发重新计算。一般用在「计算派生状态的代码」非常耗时的场景中，如：遍历大列表做统计信息。</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-10becf1cb1b4ad17a47f5b5a9155a30e6cf.png" width="700" align="middle" /></p>
<p>显然useMemo的作用是缓存昂贵的计算(避免在每次渲染时都进行高开销的计算)，在业务中使用它去控制变量来更新表格</p>
<h4>shouldComponentUpdate</h4>
<p>在类组件中，例如要往数组中添加一项数据时，当时的代码很可能是 state.push(item)，而不是 const newState = [...state, item]。</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-57bfb127368f9b484905e5f2401af06211e.png" width="700" align="middle" /></p>
<p>在此背景下，当时的开发者经常使用</p>
<p>shouldComponentUpdate 来深比较 Props，只在 Props 有修改才执行组件的 Render 过程。如今由于数据不可变性和函数组件的流行，这样的优化场景已经不会再出现了。</p>
<p>为了贴合shouldComponentUpdate的思想：给子组件传props的时候一定只传其需要的而并非一股脑全部传入：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-32d99c3457639dfd3d0b611e54205f86381.png" width="700" align="middle" /></p>
<p>传入到子组件的参数一定保证其在自组件中被使用到。</p>
<h2>批量更新，减少 Render 次数</h2>
<p>在 React 管理的事件回调和生命周期中，setState 是异步的，而其他时候 setState 都是同步的。这个问题根本原因就是 React 在自己管理的事件回调和生命周期中，对于 setState 是批量更新的，而在其他时候是立即更新的。</p>
<p>批量更新 setState 时，多次执行 setState 只会触发一次 Render 过程。相反在立即更新 setState 时，每次 setState 都会触发一次 Render 过程，就存在性能影响。</p>
<p>假设有如下组件代码，该组件在 getData() 的 API 请求结果返回后，分别更新了两个 State 。</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-6c86a84cf309cd9b1dc8ef8e9a4d33c5cb5.png" width="700" align="middle" /></p>
<p>该组件会在 setList(data.list) 后触发组件的 Render 过程，然后在 setInfo(data.info) 后再次触发 Render 过程，造成性能损失。那我们该如何解决呢：</p>
<ol>
<li>将多个 State 合并为单个 State。例如 useState({ list: null, info: null }) 替代 list 和 info 两个 State。</li>
<li>使用 React 官方提供的 unstable_batchedUpdates 方法，将多次 setState 封装到 unstable_batchedUpdates 回调中。</li>
</ol>
<p>修改后代码如下：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-71947c1183764c4da5d49847ea4027b36a7.png" width="700" align="middle" /></p>
<h1>精细化渲染阶段</h1>
<h2>按优先级更新，及时响应用户</h2>
<p>优先级更新是批量更新的逆向操作，其思想是：优先响应用户行为，再完成耗时操作。常见的场景是：页面弹出一个 Modal，当用户点击 Modal 中的确定按钮后，代码将执行两个操作：</p>
<ol>
<li>关闭 Modal。</li>
<li>页面处理 Modal 传回的数据并展示给用户。</li>
</ol>
<p>当操作2需要执行500ms时，用户会明显感觉到从点击按钮到 Modal 被关闭之间的延迟。</p>
<p>以下为一般的实现方式，将 slowHandle 函数作为用户点击按钮的回调函数。</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-5c3584e2db57a51a496e970376b68f72c1b.png" width="700" align="middle" /></p>
<p>slowHandle() 执行过程耗时长，用户点击按钮后会明显感觉到页面卡顿。</p>
<p>如果让页面优先隐藏输入框，用户便能立刻感知到页面更新，不会有卡顿感。</p>
<p>实现优先级更新的要点是将耗时任务移动到下一个宏任务中执行，优先响应用户行为。</p>
<p>例如在该例中，将 setNumbers 移动到 setTimeout 的回调中，用户点击按钮后便能立即看到输入框被隐藏，不会感知到页面卡顿。mhd项目中优化后的代码如下：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-c85c495b7a0468465a41a3eace75eb7267f.png" width="700" align="middle" /></p>
<h2>发布者订阅者跳过中间组件 Render 过程</h2>
<p>React 推荐将公共数据放在所有「需要该状态的组件」的公共祖先上，但将状态放在公共祖先上后，该状态就需要层层向下传递，直到传递给使用该状态的组件为止。</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-faf8fcc068d87181e8586507ed0ba649927.JPEG" width="700" align="middle" /></p>
<p>每次状态的更新都会涉及中间组件的 Render 过程，但中间组件并不关心该状态，它的 Render 过程只负责将该状态再传给子组件。在这种场景下可以将状态用发布者订阅者模式维护，只有关心该状态的组件才去订阅该状态，不再需要中间组件传递该状态。</p>
<p>当状态更新时，发布者发布数据更新消息，只有订阅者组件才会触发 Render 过程，中间组件不再执行 Render 过程。</p>
<p>只要是发布者订阅者模式的库，都可以使用useContext进行该优化。比如：redux、use-global-state、React.createContext 等。</p>
<p>业务代码中的使用如下：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-b5ab9990cdc525f6fbac21f0b0517a4c9ea.png" width="700" align="middle" /></p>
<p>从图中可看出，优化后只有使用了公共状态的组件renderTable才会发生更新，由此可见这样做可以大大减少父组件和 其他renderSon&#8230; 组件的 Render 次数(减少叶子节点的重渲染)。</p>
<h2>useMemo 返回虚拟 DOM 可跳过该组件 Render 过程</h2>
<p>利用 useMemo 可以缓存计算结果的特点，如果 useMemo 返回的是组件的虚拟 DOM，则将在 useMemo 依赖不变时，跳过组件的 Render 阶段。</p>
<p>该方式与 React.memo 类似，但与 React.memo 相比有以下优势：</p>
<ol>
<li>更方便。React.memo 需要对组件进行一次包装，生成新的组件。而 useMemo 只需在存在性能瓶颈的地方使用，不用修改组件。</li>
<li>更灵活。useMemo 不用考虑组件的所有 Props，而只需考虑当前场景中用到的值，也可使用 useDeepCompareMemo 对用到的值进行深比较。</li>
</ol>
<p>该例子中，父组件状态更新后，不使用 useMemo 的子组件会执行 Render 过程，而使用 useMemo 的子组件会按需执行更新。业务代码中的使用方法：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-9bcf696026f50a3af10f9a707e5c0bf458f.png" width="700" align="middle" /></p>
<h1>精确判断更新的&#8217;时机&#8217;和&#8217;范围&#8217;</h1>
<h2>debounce、throttle 优化频繁触发的回调</h2>
<p>在搜索组件中，当 input 中内容修改时就触发搜索回调。当组件能很快处理搜索结果时，用户不会感觉到输入延迟。</p>
<p>但实际场景中，中后台应用的列表页非常复杂，组件对搜索结果的 Render 会造成页面卡顿，明显影响到用户的输入体验。</p>
<p>在搜索场景中一般使用 useDebounce+ useEffect 的方式获取数据。</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-b588a3994697509bb54ddc62e040b861d87.png" width="700" align="middle" /></p>
<p>在搜索场景中，只需响应用户最后一次输入，无需响应用户的中间输入值，debounce 更适合。而 throttle 更适合需要实时响应用户的场景中更适合，如通过拖拽调整尺寸或通过拖拽进行放大缩小（如：window 的 resize 事件）。</p>
<h2>懒加载</h2>
<p>在 SPA 中，懒加载优化一般用于从一个路由跳转到另一个路由。</p>
<p>还可用于用户操作后才展示的复杂组件，比如点击按钮后展示的弹窗模块（大数据量弹窗）。</p>
<p>在这些场景下，结合 Code Split 收益较高。懒加载的实现是通过 Webpack 的动态导入和 React.lazy 方法。</p>
<p>实现懒加载优化时，不仅要考虑加载态，还需要对加载失败进行容错处理。</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-70d53594254239dd81003a13b4b68f6991f.png" width="700" align="middle" /></p>
<h2>懒渲染</h2>
<p>懒渲染指当组件进入或即将进入可视区域时才渲染组件。常见的组件 Modal/Drawer 等，当 visible 属性为 true 时才渲染组件内容，也可以认为是懒渲染的一种实现。懒渲染的使用场景有：</p>
<ol>
<li>页面中出现多次的组件，且组件渲染费时、或者组件中含有接口请求。如果渲染多个带有请求的组件，由于浏览器限制了同域名下并发请求的数量，就可能会阻塞可见区域内的其他组件中的请求，导致可见区域的内容被延迟展示。</li>
<li>需用户操作后才展示的组件。这点和懒加载一样，但懒渲染不用动态加载模块，不用考虑加载态和加载失败的兜底处理，实现上更简单。</li>
</ol>
<p>懒渲染的实现中判断组件是否出现在可视区域内借助react-visibility-observer依赖：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-b49f8195abd6c725f32285c1b3a2b0f833b.png" width="700" align="middle" /></p>
<h2>虚拟列表</h2>
<p>虚拟列表是懒渲染的一种特殊场景。虚拟列表的组件有 react-window和 react-virtualized，它们都是同一个作者开发的。</p>
<p>react-window 是 react-virtualized 的轻量版本，其 API 和文档更加友好。推荐使用 react-window，只需要计算每项的高度即可：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-67ce95b55519173c426d648f4bda5ca4c3a.png" width="700" align="middle" /></p>
<p>如果每项的高度是变化的，可给 itemSize 参数传一个函数。</p>
<p>所以在开发过程中，遇到接口返回的是所有数据时，需提前预防这类会有展示的性能瓶颈的需求时，推荐使用虚拟列表优化。使用示例：<a href="https://react-window.vercel.app/#/examples/list/fixed-size" title="react-window​react-window.vercel.app">react-window​react-window.vercel.app</a></p>
<h2>动画库直接修改 DOM 属性，跳过组件 Render 阶段</h2>
<p>这个优化在业务中应该用不上，但还是非常值得学习的，将来可以应用到组件库中。</p>
<p>参考 react-spring 的动画实现，当一个动画启动后，每次动画属性改变不会引起组件重新 Render ，而是直接修改了 dom 上相关属性值：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-3154268122929bd891f2679b2015ae8690b.png" width="700" align="middle" /></p>
<h2>避免在 didMount、didUpdate 中更新组件 State</h2>
<p>这个技巧不仅仅适用于 didMount、didUpdate，还包括 willUnmount、useLayoutEffect 和特殊场景下的 useEffect（当父组件的 cDU/cDM 触发时，子组件的 useEffect 会同步调用），本文为叙述方便将他们统称为「提交阶段钩子」。</p>
<p><strong>React 工作流commit</strong>阶段的第二步就是执行提交阶段钩子，它们的执行会阻塞浏览器更新页面。</p>
<p>如果在提交阶段钩子函数中更新组件 State，会再次触发组件的更新流程，造成两倍耗时。一般在提交阶段的钩子中更新组件状态的场景有：</p>
<ol>
<li>计算并更新组件的派生状态（Derived State）。在该场景中，类组件应使用 <strong>getDerivedStateFromProps</strong> 钩子方法代替，函数组件应使用函数调用时执行 <strong>setState</strong> 的方式代替。使用上面两种方式后，React 会将新状态和派生状态在一次更新内完成。</li>
<li>根据 DOM 信息，修改组件状态。在该场景中，除非想办法不依赖 DOM 信息，否则两次更新过程是少不了的，就只能用其他优化技巧了。</li>
</ol>
<p>use-swr 的源码就使用了该优化技巧。当某个接口存在缓存数据时，use-swr 会先使用该接口的缓存数据，并在 requestIdleCallback 时再重新发起请求，获取最新数据。模拟一个swr：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-e3d6a721a15a3c031398737fa555c5dbe70.png" width="700" align="middle" /></p>
<ol>
<li>它的第二个参数 deps，是为了在请求带有参数时，如果参数改变了就重新发起请求。</li>
<li>暴露给调用方的 fetch 函数，可以应对主动刷新的场景，比如页面上的刷新按钮。</li>
</ol>
<p>如果 use-swr 不做该优化的话，就会在 useLayoutEffect 中触发重新验证并设置 isValidating 状态为 true·，引起组件的更新流程，造成性能损失。</p>
<h1>工具介绍——React Profiler</h1>
<h2>React Profiler 定位 Render 过程瓶颈</h2>
<p>React Profiler 是 React 官方提供的性能审查工具，本文只介绍笔者的使用心得，详细的使用手册请移步官网文档。</p>
<p>Note：react-dom 16.5+ 在 DEV 模式下才支持 Profiling，同时生产环境下也可以通过一个 profiling bundle react-dom/profiling 来支持。请在 fb.me/react-profi… 上查看如何使用这个 bundle。</p>
<p>“Profiler” 的面板在刚开始的时候是空的。你可以点击 record 按钮来启动 profile：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-24112e88844bd37cbf0bfbbe42391c142c4.png" width="700" align="middle" /></p>
<h2>Profiler 只记录了 Render 过程耗时</h2>
<p>不要通过 Profiler 定位非 Render 过程的性能瓶颈问题</p>
<p>通过 React Profiler，开发者可以查看组件 Render 过程耗时，但无法知晓提交阶段的耗时。</p>
<p>尽管 Profiler 面板中有 Committed at 字段，但这个字段是相对于录制开始时间，根本没有意义。</p>
<p>通过在 React v16 版本上进行实验，同时开启 Chrome 的 Performance 和 React Profiler 统计。</p>
<p>如下图，在 Performance 面板中，Reconciliation和Commit阶段耗时分别为 642ms 和 300ms，而 Profiler 面板中只显示了 642ms：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-ae3c815e9b3e80c2b42ed83a2bdfd99e38b.png" width="700" align="middle" /></p>
<h2>开启「记录组件更新原因」</h2>
<p>点击面板上的齿轮，然后勾选「Record why each component rendered while profiling.」，如下图：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-a9442d9e79b59acc37aac4f1b7f0a3e72a6.JPEG" width="700" align="middle" /></p>
<p>然后点击面板中的虚拟 DOM 节点，右侧便会展示该组件重新 Render 的原因。</p>
<h2>定位产生本次 Render 过程原因</h2>
<p>由于 React 的批量更新（Batch Update）机制，产生一次 Render 过程可能涉及到很多个组件的状态更新。那么如何定位是哪些组件状态更新导致的呢？</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-db44264ec4a18285bad1fa100f1928f1dc8.JPEG" width="700" align="middle" /></p>
<p>在 Profiler 面板左侧的虚拟 DOM 树结构中，从上到下审查每个发生了渲染的（不会灰色的）组件。</p>
<p>如果组件是由于 State 或 Hook 改变触发了 Render 过程，那它就是我们要找的组件，如下图：</p>
<p><img src="https://oscimg.oschina.net/oscnet/up-10e0a2826e6a558259e77a6ab6bbfa54570.png" width="700" align="middle" /></p>
<h1>站在巨人的肩膀上</h1>
<p><a href="https://react.docschina.org/docs/optimizing-performance.html" title="Optimizing Performance React">Optimizing Performance React</a> 官方文档，最好的教程, 利用好 React 的性能分析工具。</p>
<p><a href="https://medium.com/@paularmstrong/twitter-lite-and-high-performance-react-progressive-web-apps-at-scale-d28a00e780a3" title="Twitter Lite and High Performance React Progressive Web Apps at Scale ">Twitter Lite and High Performance React Progressive Web Apps at Scale </a>看看 Twitter 如何优化的。</p>
<p><strong>-END-</strong></p>
]]></content:encoded>
			<wfw:commentRss>https://techblog.youdao.com/?feed=rss2&#038;p=1929</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
