开始
浏览器渲染一个页面需要下载并解析编译Html、js、css等文件。如果这些文件很大的话,就会延长首屏加载的时间。而下载的时间往往是其中的大头。
我们第一个想到的是压缩代码,删除注释,CDN加速,或者把大文件分成小文件来并发进行下载,通过这些方法来缩短下载的时间。
还有一个思路:
我们只是想缩短首屏加载的时间,而与首屏渲染相关的代码只是占所有代码的一部分。所以只需要把与首屏渲染相关的代码第一时间给浏览器,其他的代码后面再来传输,这样是不是也可以缩短首屏加载的时间呢?
答案是可以的,关键在于如何实现
这里有一篇关于Suspence的讲解,初学者建议看看:React-解析Suspence-超简单
简单的代码切割
很简单,借助Suspence就可以做到了😁
import { lazy, Suspense, useState } from 'react';
import Title from './Title';
import Title from './Name'
function App() {
const [flag, setFlag] = useState(false);
return (
<div className="App">
<div onClick={() => { setFlag(true) }}>
<Title />
</div>
{flag ? <Name /> : null }
</div>
);
}
上面的代码中,初始化时,Name组件是不渲染的。只有当用户点击Title,Name才会得到渲染。默认情况下,Name代码会和Title代码一起传至浏览器,但Name的代码并不会参与首屏的渲染,这无疑会拖慢渲染的速度。
所以我们可以在用户点击了Title之后,才去下载Name的代码 下面是实现代码
import { lazy, Suspense, useState } from 'react';
import './App.css';
import Title from './Title';
const Name = lazy(() => import('./Name');)
function App() {
const [flag, setFlag] = useState(false);
return (
<div className="App">
<div onClick={() => { setFlag(true) }}>
<Title />
</div>
<Suspense fallback={<div>loading ...</div>}>
{flag ? <Name /> : null}
</Suspense>
</div>
);}
我们将Name组件用React.lazy函数导入了进来,并且在使用的时候,用Suspence包裹住。这样就实现了在用户点击Title后,才去下载Name的代码文件。
下面是效果图👇
这是页面初始化时候下载的文件
当我们点击TItle
Name代码文件被另外下载过来了。效果达成😁
这样似乎就完成了代码的分割,而我们只需要动用两个React的API:lazy、Suspence,就能完成这样的效果。如果文件的大小变得不可忽视的时候,该优化的效果是显而易见的
还有一个问题
如果Name组件代码很多,那么用户在点击Title之后,需要较长的时候再能看到Name渲染的效果,这是不好的。
在首屏加载完成之后,到用户点击Title之前,是有一段时间的。而这时间我们可以利用起来,用来下载Name组件代码。这样既不影响首屏的渲染时间,也能让用户更快地看到Name的渲染。
想法是好的,该怎么做呢?
解决代码切割的问题
import { lazy, Suspense, useState } from 'react';
import './App.css';
import Title from './Title';
const temp = import('./Name');
const Name = lazy(() => temp)
function App() {
const [flag, setFlag] = useState(false);
return (
<div className="App">
<div onClick={() => { setFlag(true) }}>
<Title />
</div>
<Suspense fallback={<div>loading ...</div>}>
{flag ? <Name /> : null}
</Suspense>
</div>
);
}
我们将import()的返回值用temp变量承接,之后的lazy函数会用到temp来做懒加载。
来看看NetWork中的实际效果
Name组件代码依旧被分割,先下载了APP和TItle组件的代码,后下载了Name的代码。即不需要用户点击Title,也会进行下载
不仅Name组件代码会切割延迟下载,Name组件的依赖也同样如此,这样的结果绝对是令人兴奋的
一般性的用法:按照路由切割代码
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
);
这是官方文档里面的例子。其中通过路由对不同的组件进行切割,这样可以做到当渲染哪个页面,哪个页面才被下载,而不是全部下载。
这是个好功能,以前见到了不明所以,现在知道了是干嘛的了,相见恨晚呀
据说webpack在打包的过程中,也有代码切割的功能,它是怎么做的呢,这个上面所讲的代码分割有什么不同呢?
总结:
- 用Suspence实现代码的切割
- 用变量承接import的返回值,来解决代码切割的问题
- 一般性的用法,按照路由做代码切割