一、了解页面加载过程
1. 打开页面
这个时候页面是完全空白的。
2. 首屏渲染
Html 和引用的 Css 加载完毕,浏览器进行首次渲染,有可见的内容出现。
我们把首次渲染需要加载的资源体积称为“首屏体积”。
3. 首次内容渲染
react、 react-dom、业务代码加载完毕,应用第一次渲染,页面主要内容出现。
4. 可交互
然后应用的代码开始执行,拉取数据、进行动态 import、响应事件等,完毕后页面进入可交互状态。
5. 内容加载完毕
接下来 lazyload 的图片等多媒体内容开始逐渐加载完毕。
6. 页面加载完毕
然后直到页面的其他资源加载完毕。
接下来,我们分别讨论这些步骤中,有哪些优化的点。
二、首屏渲染优化
React 项目中的 Html 都会提供一个 root 节点
<div id='root'></div>
我们可以在这个 root 节点中加点内容,使第一步打开页面时的空白时间尽可能减少(页面崩溃也会显示这些内容)。
我们可以使用 html-webpack-plugin 自动给 root 节点插入内容。
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const fs = require('fs');
// 读取要自动插入root节点的 html 和 css
var loading = {
html: fs.readFileSync(path.join(__dirname, './src/components/loading/index.html')),
css: '<style>' + fs.readFileSync(path.join(__dirname, './src/components/loading/index.css')) + '</style>'
}
module.exports = {
...
plugins: [
new HtmlWebpackPlugin({
favicon: path.resolve(config.srcPath, 'favicon.ico'),
inject: 'body',
template: path.join(config.srcPath, 'index.tmpl.html'),
loading
}),
]
}
然后在模板中引用即可:
// html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<%= htmlWebpackPlugin.options.loading.css %>
</head>
<body>
<div id="root">
<%= htmlWebpackPlugin.options.loading.html %>
</div>
</body>
</html>
三、首次内容渲染优化
使用 react-loadable 动态 import React 组件,让首次加载时只加载当前路由匹配的组件。
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Loadable from 'react-loadable'
import Loading from './components/loading'
let Home = Loadable({
loader: () => import('@/pages/home'),
loading: Loading
})
let User = Loadable({
loader: () => import('@/pages/user'),
loading: Loading
})
let Others = Loadable({
loader: () => import('@/pages/others'),
loading: Loading
})
export default class RootRouter extends React.Component {
render() {
return (
<Router>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/user" component={User}/>
<Route path="/others" component={Others}/>
<Route component={Home}/>
</Switch>
</Router>
)
}
}
上面代码在首次加载时,会先展示一个 Loading,等到页面组件代码加载完毕后,便会替换掉 Loading。
四、编译到 ES2015+
把 ES2015+ 的代码编译成 ES5,体积会增大好几倍,运行速率也会减慢。在当下2018年,大部分现代浏览器已经支持 ES6 语法。我们要做的,就是把代码编译到 ES2015+,然后给少数的老旧浏览器留一份 ES5 的代码即可。
具体的做法就是使用 <script type="module"> 标签。
支持这个标签的浏览器必然支持 async/await, Promise, Class, 箭头函数, Map/Set, fetch 等等。
<script type="module" src="main.js"></script>
<script type="nomodule" src="main.es5.js"></script>
现代主流浏览器能识别 type="module",就加载第一条 ES2015+ 的代码。老旧浏览器不能识别 type="module" 和 type="nomodule",就会加载第二条 ES5 的代码。
五、Placeholder
我们在加载文字、图片的时候,经常出现“闪屏”的情况,比如文字或者图片还没有加载完毕,此时页面上对应的位置还是完全空着的,然后加载完毕,内容会突然撑开页面,导致“闪屏”的出现,造成不好的用户体验。
为了避免这种“闪屏”的情况,我们要做的就是提前设置占位元素,也就是 placeholder。已经有一些现成的第三方组件可以用了。