在使用 React怯屉、Vue 等框架時蔚舀,我們可以使用其配套的路由庫如 react-router 和 vue-router 構(gòu)建單頁應(yīng)用饵沧,以發(fā)揮框架本身的最大威力。
單頁應(yīng)用使我們可以無刷新訪問頁面赌躺,擁有較好的性能和用戶體驗(yàn)狼牺,不再像過去那樣,每開一個頁面都要從服務(wù)器重新請求資源礼患,還可能會因?yàn)榫W(wǎng)絡(luò)的問題等待良久是钥。
單頁應(yīng)用在初次進(jìn)入頁面時就加載全部的資源,后續(xù)除了接口調(diào)用缅叠,幾乎不和服務(wù)器進(jìn)行交互了悄泥,頁面切換無煩惱。
但使用單頁應(yīng)用也有一個問題痪署,就是當(dāng)項(xiàng)目很大時码泞,我們常常打包后的文件常常很大,盡管我們可以嘗試使用 webpack 的 CommonsChunkPlugin 來提取公共代碼狼犯,但隨著項(xiàng)目體積的增大余寥,打包后的代碼還是不夠理想。
由于在訪問第一個頁面時需要加載所有的資源悯森,包括體積龐大的 bundle 文件宋舷,會造成首次加載速度變慢,首屏渲染時間增加等一些列的用戶體驗(yàn)問題瓢姻。為了解決這個問題祝蝠,我們希望有這么一種策略:是否可以在打包時不將所有的文件一起打包,而是只打包最必須的一部分(如首頁幻碱、登錄)绎狭,剩下的模塊進(jìn)行分片打包,每訪問到相應(yīng)的頁面時再按需請求呢褥傍?
答案就是 webpack 的分片機(jī)制儡嘶。
webpack 分片機(jī)制
webpack 分片機(jī)制就是將代碼進(jìn)行分片打包,實(shí)現(xiàn)按需加載恍风,使用分片機(jī)制前蹦狂,首先需要滿足兩個配置條件:
1.output 選項(xiàng)中需要有 chunkFileName:
chunkFilename: "static/js/[name].chunk.js",
2.plugins 選項(xiàng)中需要有 CommonsChunkPlugin:
new webpack.optimize.CommonsChunkPlugin({ name:'common', filename:'static/js/common.js'}),
雖然代碼分片打包主要用于生產(chǎn)環(huán)境,但建議在開發(fā)環(huán)境中也加上這樣的配置朋贬,就可以無需構(gòu)建而在開發(fā)環(huán)境(development)時就能看到和生產(chǎn)環(huán)境(production)一樣的效果了凯楔,還可以及早的發(fā)現(xiàn)問題。
幾種引入文件的方式
webpack 支持下面幾種文件的引用方式:
- require
- import
- require.ensure
前兩個想必大家都不陌生锦募,最后一個可能不太常見摆屯,而這種方式正是使 webpack 進(jìn)行分片打包的關(guān)鍵。
使用 require 或 import 進(jìn)行打包時御滩,webpack 在執(zhí)行靜態(tài)編譯檢查時鸥拧,會將 require 或 import 后的代碼打包到同一文件中党远,而如果使用 require.ensure 引入文件,webpack 在檢查時發(fā)現(xiàn)這種引入方式富弦,就知道要對這部分文件進(jìn)行分片打包了沟娱,打包的文件名就是前面在 webpack 配置中定義的 chunkFilename。
使用 require.ensure
使用 require.ensure 較為簡單腕柜,這里以 Home 組件為例:
require.ensure([],(require) => {
const User = require("./User").default
},"user");
下面說一下幾個相關(guān)參數(shù)的含義:
require.ensure 接受兩個參數(shù):
- 第一個參數(shù)是數(shù)組济似,表示該模塊依賴的模塊,該模塊會在被依賴的模塊都被裝載后再引入盏缤,如果沒有依賴性砰蠢,使用空數(shù)組即可
- 第二個參數(shù)是一個回調(diào)函數(shù),相關(guān)的異步模塊就是在這個函數(shù)中被引入的
- 第三個參數(shù)是分片(chunk)文件的文件名唉铜,也就是 chunkFileName 配置中的 [name] 屬性台舱,如果不指定該參數(shù),將默認(rèn)為一個數(shù)字作為唯一的 ID潭流,建議總是定義 chunk 文件名竞惋,方便后期調(diào)試。
在 react-router 中使用代碼分片
同樣灰嫉,配合 require.ensure 也可以實(shí)現(xiàn)路由的按需加載拆宛。在使用路由按需加載時,我們只需將前面的函數(shù)進(jìn)行一下包裝讼撒,再將 component 換成 getComponent 即可浑厚。舉個例子:
...
const getHomeRoute = ( location,cb ) => {
console.log(location)
require.ensure([],(require) => {
cb(null,require("./Home").default)
},"home");
}
const getUserRoute = ( location,cb ) => {
require.ensure([],(require) => {
cb(null,require("./User").default)
},"user");
}
ReactDOM.render(
<Provider store = { store }>
<Router history = { hashHistory }>
<Route path = "/" component = { App }>
<IndexRoute getComponent = { getHomeRoute } />
<Route path = "home" getComponent = { getHomeRoute }></Route>
<Route path = "user" getComponent = { getUserRoute }></Route>
</Route>
</Router>
</Provider>
, document.getElementById("root"));
如果不使用異步路由,Route 組件中使用 component 屬性根盒,如果需要使用異步路由钳幅,Route 組件中使用 getComponent 屬性,屬性值就是前面定義的相應(yīng)獲取函數(shù)炎滞。
關(guān)于獲取函數(shù)的兩個參數(shù):
- 第一個參數(shù) location 是當(dāng)前的路由信息
- 第二個參數(shù)是一個回調(diào)函數(shù)贡这,為 Node 的 Error First 風(fēng)格,該回調(diào)函數(shù)在異步組件被加載成功后再調(diào)用厂榛,然后將加載好的組件傳給 router。
總結(jié)
本文主要講了使用 require.ensure 配合 webpack 進(jìn)行異步加載丽惭,并講到了其和 react-router 的配合實(shí)現(xiàn)異步路由击奶,要實(shí)現(xiàn)異步路由加載,需要滿足以下兩個條件:
- 使用 require.ensure 讓 webpack 對代碼進(jìn)行分片打包
- 在 react-router 中使用 getComponent 而不是 component 加載異步分片文件
webpack 的看家本領(lǐng)就是文件打包责掏,對于異步文件分片的支持自然也不在話下柜砾,使用代碼分片打包可以顯著降低打包后的文件大小,并在生產(chǎn)場合下提高用戶體驗(yàn)换衬。
完痰驱。