My Little World

如何提升应用打开速度

使用 import 语句,定义按需加载的起始模块

对于这个需求,ECMA Script 标准有一个提案,专门用于动态加载模块,语法是 import(someModule)。
注意,这里的 import 和我们一般用于引入模块的静态声明方式不同,比如 import something from ‘somemodule’ 。
但这里的 import 是作为一个函数动态运行的,这个 import() 函数会返回一个 Promise。
这样,在模块加载成功后,就可以在 Promise 的 then 回调函数中去使用这个模块了。

虽然这只是一个提案,并没有成为标准,但是 Webpack 等打包工具利用了这样的语法去定义代码的分包。

也就是说,Webpack 实现了这样的语法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function ProfilePage() {
// 定义一个 state 用于存放需要加载的组件
const [RealPage, setRealPage] = useState(null);

// 根据路径动态加载真正的组件实现
import('./RealProfilePage').then((comp) => {
setRealPage(Comp);
});
// 如果组件未加载则显示 Loading 状态
if (!RealPage) return 'Loading....';

// 组件加载成功后则将其渲染到界面
return <RealPage />
}

import() 这个语句完全是由 Webpack 进行处理的。

按需加载的实现原理:Webpack 利用了动态 import 语句,自动实现了整个应用的拆包。
而在实际开发中,其实并不需要关心 Webpack 是如何做到的,
而只需要考虑:该在哪个位置使用 import 语句去定义动态加载的拆分点。

总体需要采用的策略是:按业务模块为目标去做隔离,尽量在每个模块的起始页面去定义这个拆分点。

react-loadable,专门用于 React 组件的按需加载。

1
2
3
4
5
6
7
8
9
10
11
12
import Loadable from "react-loadable";


// 创建一个显示加载状态的组件
function Loading({ error }) {
return error ? 'Failed' : 'Loading';
}
// 创建加载器组件
const HelloLazyLoad = Loadable({
loader: () => import("./RealHelloLazyLoad"),
loading: Loading,
});

可以看到 Loadable 这个高阶组件主要就是两个 API。

loader:用于传入一个加载器回调,在组件渲染到页面时被执行。
在这个回调函数中,我们只需要直接使用 import 语句去加载需要的模块就可以了。

loading:表示用于显示加载状态的组件。在模块加载完成之前,加载器就会渲染这个组件。
如果模块加载失败,那么 react-loadable 会将 errors 属性传递给 Loading 组件,方便你根据错误状态来显示不同的信息给用户。

按需加载可以说是减少首屏加载时间最为有效的手段,它可以让用户在打开应用时,无需加载所有代码就能开始使用,从而提升用户体验。

使用 service worker 缓存前端资源

和浏览器自动的资源缓存机制相比,Service Worker 加上 Cache Storage 这个缓存机制,具有更高的准确性和可靠性。

因为它可以确保两点:
缓存永远不过期。你只要下载过一次,就永远不需要再重新下载,除非主动删除。
永远不会访问过期的资源。换句话说,如果发布了一个新版本,那么你可以通过版本化的一些机制,来确保用户访问到的一定是最新的资源。