谷歌的数据表明,一个有 10 条数据 0.4 秒可以加载完的页面,在变成 30 条数据加载时间为 0.9 秒后,流量和广告收入减少了 20%。当谷歌地图的首页文件大小从 100kb 减少到 70~80kb 时,流量在第一周涨了 10%,接下来的三周涨了 25%。
腾讯的前端工程师根据长期的数据监控也发现页面的一秒钟延迟会造成 9.4% 的 PV 的下降,8.3% 跳出率的增加以及 3.5% 转化率的下降。
可以看出,性能优化商业上来说很重要。
但是,更重要的还是屏幕前我们的用户,让用户在使用产品时有更快更舒适的浏览体验,这算是一种前端工程师的自我修养。
所以今天就分享一下如何去优化我们的 React 项目,进而提升用户体验。
使用React.Fragment 来避免向 DOM 添加额外的节点
我们在写 React 代码时,会经常遇到返回一组元素的情况,代码像这样:
class Parent extends React.Component {
render() {
return (
Hello there!
Hello there again!
)
}
}
如果我们写成这样,控制台会报错误:JSX parent expressions must have one parent element
,告诉我们只能返回一个元素,所以我们通常会在最外层包裹一个 div 元素,如下所示:
class Parent extends React.Component {
render() {
return (
Hello there!
Hello there again!
)
}
}
这样做虽然能正常执行,但是会额外创建不必要的 DOM
节点,这可能会导致创建许多无用的元素,并且在我们的渲染数据来自特定顺序的子组件时,某些情况下也会生成许多无效的节点。请考虑以下代码:
class Table extends React.Component {
render() {
return (
);
}
}
class Columns extends React.Component {
render() {
return (
column one
column two
);
}
}
上面的代码将在我们的组件中呈现以下内容:
column one
column two
这显然不是我们想看到的,React 为我们提供了 Fragments
,Fragments
允许我们将子列表分组,而无需向 DOM 添加额外节点。我们可以将组件重新编写为:
class Columns extends React.Component {
render() {
return (
column one
column two
);
}
}
使用 React.Lazy 延迟加载组件
有时我们只想在请求时加载部分组件,例如,仅在单击购物车图标时加载购物车数据,在用户滚动到该点时在长图像列表的底部加载图像等。
React.Lazy
帮助我们按需加载组件,从而减少我们应用程序的加载时间,因为只加载我们所需的组件。
React.lazy
接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise
,该 Promise
需要 resolve
一个 defalut export
的 React 组件。如下所示:
class MyComponent extends Component{
render() {
return (MyComponent)
}
}
const MyComponent = React.lazy(()=>import('./MyComponent.js'))
function App() {
return ( )
}
在编译时,使用 Webpack
解析到该语法时,它会自动地开始进行代码分割。最终,我们的应用程序将会被分成含有多个 UI 片段的包,这些 UI 片段将在需要时加载,如果你使用 Create React App
,该功能已配置好,你能立刻使用这个特性。Next.js
也已支持该特性而无需再配置。
使用React.Suspense
在交换组件时,会出现一个小的时间延迟,例如在 MyComponent 组件渲染完成后,包含 OtherComponent 的模块还没有被加载完成,这可能就会出现白屏的情况,我们可以使用加载指示器为此组件做优雅降级,这里我们使用 Suspense
组件来解决。
React.Suspense
用于包装延迟组件以在加载组件时显示后备内容。
// MyComponent.js
const Mycomponent = React.lazy(()=>import('./component.js'))
function App() {
return (
loading .. }>