Webpack 代碼分離
:pushpin: 提示:
版本問題
本文基于
webpack 2.x
版本孽查。webpack 2.x
相比webpack 1.x
有重大改變。所以缔恳,如果你的項目中已使用了 webpack 1.x ,本教程的示例將不適用,請慎重典勇。如果鐵了心要升級 webpack ,請參考 webpack 官方文檔 - 從 v1 遷移到 v2
閱讀建議
閱讀本文前叮趴,建議先閱讀 Webpack 概念 割笙。
代碼分離是 webpack 中最引人注目的特性之一。
你可以把你的代碼分離到不同的 bundle 中,然后你就可以去按需加載這些文件伤溉。
總的來說般码, webpack
分離可以分為兩類:
- 資源分離
- 代碼分離
資源分離(Resource Splitting)
對第三方庫(vendor) 和 CSS 進(jìn)行代碼分離,這些方式有助于實現(xiàn)緩存和并行加載乱顾。
分離 CSS(CSS Splitting)
你可能也想將你的樣式代碼分離到單獨(dú)的 bundle 中板祝,以此使其獨(dú)立于應(yīng)用程序邏輯。這加強(qiáng)了樣式的可緩存性走净,并且使得瀏覽器能夠并行加載應(yīng)用程序代碼中的樣式文件券时,避免 FOUC 問題 (無樣式內(nèi)容造成的閃爍)。
安裝 ExtractTextWebpackPlugin
如下
$ npm install --save-dev extract-text-webpack-plugin
webpack.config.js 中需要按下面進(jìn)行配置:
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// 關(guān)于模塊配置
module: {
// 模塊規(guī)則(配置 loader伏伯、解析器等選項)
rules: [
{
// css 加載
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: "css-loader"
})
}
]
},
// 附加插件列表
plugins: [
// 將樣式文件獨(dú)立打包
new ExtractTextPlugin("styles.css")
]
};
分離第三方庫(Vendor Code Splitting)
一個典型的應(yīng)用程序橘洞,由于框架/功能性需求,會依賴于許多第三方庫的代碼舵鳞。不同于應(yīng)用程序代碼震檩,這些第三方庫代碼不會頻繁修改。
如果我們將這些庫(library)中的代碼蜓堕,保留在與應(yīng)用程序代碼相獨(dú)立的 bundle 中抛虏,我們就可以利用瀏覽器緩存機(jī)制,把這些文件長時間地緩存在用戶機(jī)器上套才。
為了完成這個目標(biāo)迂猴,不管應(yīng)用程序代碼如何變化,vendor 文件名中的 hash
部分必須保持不變背伴。學(xué)習(xí)如何使用 CommonsChunkPlugin
分離 vendor/library 代碼沸毁。
webpack.config.js
const webpack = require('webpack');
module.exports = {
// 這里應(yīng)用程序開始執(zhí)行
// webpack 開始打包
// 本例中 entry 為多入口
entry: {
main: "./app/index",
vendor: "react"
},
// webpack 如何輸出結(jié)果的相關(guān)選項
output: {
// 所有輸出文件的目標(biāo)路徑
// 必須是絕對路徑(使用 Node.js 的 path 模塊)
path: path.resolve(__dirname, "dist"),
// 「入口分塊(entry chunk)」的文件名模板(出口分塊?)
// filename: "bundle.min.js",
// filename: "[name].js", // 用于多個入口點(diǎn)(entry point)(出口點(diǎn)傻寂?)
// filename: "[chunkhash].js", // 用于長效緩存
filename: "[name].[chunkhash:8].js", // 用于長效緩存
},
// 附加插件列表
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: "vendor" // 指定公共 bundle 的名字息尺。
})
]
};
在上面的配置中,
- 在
entry
屬性中疾掰,將 react 指定為一個獨(dú)立的入口 vendor搂誉; - 然后,在
output
屬性中静檬,將 filename 指定為 [name].[chunkhash:8].js炭懊,這表示輸出文件的文件名樣式。 - 最后在
plugins
列表中引入 CommonsChunkPlugin 插件拂檩,用來指定 bundle 侮腹。
執(zhí)行 webpack 命令后,webpack 會生成 2 個 bundle 文件稻励,形式如:
main.bef8f974.js
vendor.2f1bd4c8.js
代碼按需分離(On Demand Code Splitting)
雖然前面幾類資源分離父阻,需要用戶預(yù)先在配置中指定分離模塊,但也可以在應(yīng)用程序代碼中創(chuàng)建動態(tài)分離模塊。
這可以用于更細(xì)粒度的代碼塊加矛,例如钠署,根據(jù)我們的應(yīng)用程序路由,或根據(jù)用戶行為預(yù)測荒椭。這可以使用戶按照實際需要加載非必要資源谐鼎。
前一節(jié),我們了解了 webpack 可以將資源拆分為bundle趣惠。接下來狸棍,我們要學(xué)習(xí)如何異步加載。例如味悄,這允許首先提供最低限度的引導(dǎo) bundle草戈,并在稍后再異步地加載其他功能。
webpack 支持兩種相似的技術(shù)實現(xiàn)此目的:使用 import()
(推薦侍瑟,ECMAScript 提案) 和 require.ensure()
(遺留唐片,webpack 特定)。本文只介紹官方推薦的 import()
方式涨颜。
ES2015 loader 規(guī)范定義了 import()
作為一種在運(yùn)行時(runtime)動態(tài)載入 ES2015 模塊的方法费韭。
webpack 把 import()
作為一個分離點(diǎn)(split-point),并把引入的模塊作為一個單獨(dú)的 chunk庭瑰。 import()
將模塊名字作為參數(shù)并返回一個 Promoise
對象星持,即 import(name) -> Promise
配合 Babel 使用
如果你想要在 Babel 中使用 import
,但是由于 import() 還是屬于 Stage 3 的特性弹灭,所以你需要安裝/添加 syntax-dynamic-import
插件來避免 parser 報錯督暂。在草案正式成為規(guī)范后,就不再需要這個插件了穷吮。
例:
我們來定義一個 Clock 組件逻翁,動態(tài)引入 moment 庫。
首先捡鱼,安裝 moment 庫八回。
$ npm install --save-dev moment
JavaScript 代碼:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date().toLocaleDateString() };
this.click = this.click.bind(this);
}
click() {
// 動態(tài)引入import()
import('moment')
.then(moment => moment().format("MMMM Do YYYY, h:mm:ss a"))
.then(str => this.setState({date:str}))
.catch(err => console.log("Failed to load moment", err));
}
render() {
return (
<div>
<h2>It is {this.state.date}.</h2>
<p onClick={this.click}>Click here to changing the time.</p>
</div>
);
}
}
webpack.config.js
// 關(guān)于模塊配置
module: {
// 模塊規(guī)則(配置 loader、解析器等選項)
rules: [
{
// 語義解釋器堰汉,將 js/jsx 文件中的 es2015/react 語法自動轉(zhuǎn)為瀏覽器可識別的 Javascript 語法
test: /\.jsx?$/,
include: path.resolve(__dirname, "app"),
// 應(yīng)該應(yīng)用的 loader辽社,它相對上下文解析
// 為了更清晰伟墙,`-loader` 后綴在 webpack 2 中不再是可選的
// 查看 webpack 1 升級指南翘鸭。
loader: "babel-loader",
// loader 的可選項
options: {
presets: ["es2015", "react"],
plugins: ['syntax-dynamic-import']
},
},
]
},
Webpack 系列教程
歡迎閱讀其它內(nèi)容: