React專案載入效能優化
一、瞭解頁面載入過程
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。已經有一些現成的第三方元件可以用了。