[TOC]
# plugin
plugin是用于擴展 webpack 的功能,各種各樣的 plugin 幾乎可以讓 webpack 做任何與構建相關的事情。
plugin 的配置很簡單,plugins 配置項接收一個數組,數組里的每一項都是一個要使用的plugin的實例,plugin 需要的參數通過構造函數傳入。
舉個栗子
```
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new HtmlWebpackPlugin({ // 打包輸出HTML
title: 'Hello World app',
minify: { // 壓縮HTML文件
removeComments: true, // 移除HTML中的注釋
collapseWhitespace: true, // 刪除空白符與換行符
minifyCSS: true// 壓縮內聯css
},
filename: 'index.html',
template: 'index.html'
}),
]
```
使用plugin 的難點在于 plugin 本身的配置項,而不是如何在 webpack 中引入 plugin,幾乎所有webpack 無法直接實現的功能,都能找到開源的 plugin 去解決,我們要做的就是去找更據自己的需要找出相應 的plugin。
# 編寫 plugin
# html-webpack-plugin
這個plugin曝光率很高,他主要有兩個作用
* 為html文件中引入的外部資源如script、link動態添加每次compile后的hash,防止引用緩存的外部文件問題
* 可以生成創建html入口文件,比如單頁面可以生成一個html文件入口,配置N個html-webpack-plugin可以生成N個頁面入口
github上有些關于 [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) 的屬性介紹
## title
生成html文件的標題
## filename
輸出的html的文件名稱
## template
html模板所在的文件路徑
根據自己的指定的模板文件來生成特定的 html 文件。這里的模板類型可以是任意你喜歡的模板,可以是 html, jade, ejs, hbs, 等等,但是要注意的是,使用自定義的模板文件時,需要提前安裝對應的 loader, 否則webpack不能正確解析。
如果你設置的 title 和 filename于模板中發生了沖突,那么以你的 title 和 filename 的配置值為準。
## inject
注入選項。有四個選項值 true, body, head, false.
1. true:默認值,script標簽位于html文件的 body 底部
2. body:script標簽位于html文件的 body 底部(同 true)
3. head:script 標簽位于 head 標簽內
4. false:不插入生成的 js 文件,只是單純的生成一個 html 文件
## favicon
給生成的 html 文件生成一個 favicon。屬性值為 favicon 文件所在的路徑名
## minify
minify 的作用是對 html 文件進行壓縮,minify 的屬性值是一個壓縮選項或者 false 。默認值為false, 不對生成的 html 文件進行壓縮。
下面羅列了一些常用的配置:
~~~javascript
plugins:[
new HtmlWebpackPlugin({
//部分省略,具體看minify的配置
minify: {
//是否對大小寫敏感,默認false
caseSensitive: true,
//是否簡寫boolean格式的屬性如:disabled="disabled" 簡寫為disabled 默認false
collapseBooleanAttributes: true,
//是否去除空格,默認false
collapseWhitespace: true,
//是否壓縮html里的css(使用clean-css進行的壓縮) 默認值false;
minifyCSS: true,
//是否壓縮html里的js(使用uglify-js進行的壓縮)
minifyJS: true,
//Prevents the escaping of the values of attributes
preventAttributesEscaping: true,
//是否移除屬性的引號 默認false
removeAttributeQuotes: true,
//是否移除注釋 默認false
removeComments: true,
//從腳本和樣式刪除的注釋 默認false
removeCommentsFromCDATA: true,
//是否刪除空屬性,默認false
removeEmptyAttributes: true,
// 若開啟此項,生成的html中沒有 body 和 head,html也未閉合
removeOptionalTags: false,
//刪除多余的屬性
removeRedundantAttributes: true,
//刪除script的類型屬性,在h5下面script的type默認值:text/javascript 默認值false
removeScriptTypeAttributes: true,
//刪除style的類型屬性, type="text/css" 同上
removeStyleLinkTypeAttributes: true,
//使用短的文檔類型,默認false
useShortDoctype: true,
}
}),
]
~~~
## hash
hash選項的作用是 給生成的 js 文件一個獨特的 hash 值,該 hash 值是該次 webpack 編譯的 hash 值。默認值為 false 。同樣看一個例子。
~~~javascript
plugins: [
new HtmlWebpackPlugin({
hash: true
})
]
~~~
編譯打包后
~~~html
<script type=text/javascript src=bundle.js?22b9692e22e7be37b57e></script>
~~~
執行 webpack 命令后,你會看到你的生成的 html 文件的 script 標簽內引用的 js 文件,是不是有點變化了。
bundle.js 文件后跟的一串 hash 值就是此次 webpack 編譯對應的 hash 值。
## cache
默認是true的,表示內容變化的時候生成一個新的文件。
## showErrors
這個我們自運行項目的時候經常會用到,showErrors 的作用是,如果 webpack 編譯出現錯誤,webpack會將錯誤信息包裹在一個 pre 標簽內,屬性的默認值為 true ,也就是顯示錯誤信息。
開啟這個,方便定位錯誤
## chunks
主要是針對多入口(entry)文件。當你有多個入口文件的時候,對應就會生成多個編譯后的 js 文件。
那么 chunks 選項就可以決定是否都使用這些生成的 js 文件。
chunks 默認會在生成的 html 文件中引用所有的 js 文件,當然你也可以指定引入哪些特定的文件。
~~~
entry: {
index: path.resolve(__dirname, './src/index.js'),
devor: path.resolve(__dirname, './src/devor.js'),
main: path.resolve(__dirname, './src/main.js')
}
plugins: [
new httpWebpackPlugin({
chunks: ['index','main']
})
]
~~~
那么編譯后:
~~~
<script type=text/javascript src="index.js"></script>
<script type=text/javascript src="main.js"></script>
~~~
而如果沒有指定 chunks 選項,默認會全部引用。
## excludeChunks
排除掉一些js,
~~~
entry: {
index: path.resolve(__dirname, './src/index.js'),
devor: path.resolve(__dirname, './src/devor.js'),
main: path.resolve(__dirname, './src/main.js')
}
plugins: [
new httpWebpackPlugin({
excludeChunks: ['devor.js']//和的等等效
})
]
~~~
那么編譯后:
~~~
<script type=text/javascript src="index.js"></script>
<script type=text/javascript src="main.js"></script>
~~~
## webpack4 與 html-webpack-plugin
用最新版本的的 html-webpack-plugin你可能還會遇到如下的錯誤:
```
throw new Error('Cyclic dependency' + nodeRep)
```
產生這個 bug 的原因是循環引用依賴,如果你沒有這個問題可以忽略。
目前解決方案可以使用 Alpha 版本,n`pm i --save-dev html-webpack-plugin@next `
或者加入 `chunksSortMode: 'none' ` 就可以了。
但仔細查看文檔發現設置成 `chunksSortMode: 'none'`這樣是會有問題的。
這屬性會決定你 chunks 的加載順序,如果設置為 none,你的 chunk 加載在頁面中加載的順序就不能夠保證了,可能會出現樣式被覆蓋的情況。比如我在 `app.css` 里面修改了一個第三方庫 element-ui的樣式,通過加載順序的先后來覆蓋它,但由于設置為了 `none`,打包出來的結果變成了這樣:
~~~
<link href="/app.8945fbfc.css" rel="stylesheet">
<link href="/chunk-elementUI.2db88087.css" rel="stylesheet">
~~~
app.css 被先加載了,之前寫的樣式覆蓋就失效了,除非你使用important或者其它 css 權重的方式覆蓋它,但這明顯是不太合理的。
vue-cli 正好也有這個相關 issue,尤雨溪也在不使用@next版本的基礎上 hack 了它,有興趣的可以自己研究一下,
其它 html-webpack-plugin 的配置和之前使用沒有什么區別。
參考鏈接
[html-webpack-plugin用法全解](https://links.jianshu.com/go?to=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000007294861)
[插件 html-webpack-plugin 的詳解](https://links.jianshu.com/go?to=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000013883242)
[手摸手,帶你用合理的姿勢使用webpack4(上)](https://www.jianshu.com/p/1fcc2f8ed8bb)
> https://www.jianshu.com/p/08a60756ffda
# DllPlugin 生產第三方 dll
> [https://webpack.docschina.org/plugins/dll-plugin/](https://webpack.docschina.org/plugins/dll-plugin/)
DllPlugin & DllReferencePlugin 這一方案,實際上也是屬于代碼分割的范疇,但與 [SplitChunksPlugin](http://webpack.docschina.org/plugins/split-chunks-plugin/) 不一樣的是,它不僅僅是把公用代碼提取出來放到一個獨立的文件供不同的頁面來使用,它更重要的一點是:**只需要編譯一次,在之后的構建過程中被動態鏈接庫包含的模塊(公用代碼)將不會在重新編譯,而是直接使用動態鏈接庫中的代碼**。這有什么好處呢?很簡單,業務代碼常改,而公用代碼不常改,那么,我們在日常修改業務代碼的過程中,就可以**省出編譯公用代碼那一部分所耗費的時間**了。
整個過程大概是這樣的:
1. 利用 DllPlugin 把公用代碼打包成一個**Dll文件**(其實本質上還是 js,只是套用概念而已);除了 Dll 文件外,DllPlugin 還會生成一個 `manifest.json` 文件作為公用代碼的索引供`DllReferencePlugin`使用。
?2. 在業務代碼的 webpack 配置文件中配置好`DllReferencePlugin`并進行編譯,達到利用`DllReferencePlugin`讓業務代碼和 Dll 文件實現關聯的目的。
3. 在各個頁面中,先加載 Dll 文件,再加載業務代碼文件。
## 適用范圍
由于 Dll 文件(動態鏈接庫)中大多數包含的是不常改動的第三方模塊,例如 react、react-dom,尤其是本身就龐大或者依賴眾多的庫,只要不升級這些模塊的版本,動態鏈接庫就不用重新編譯。
如果你自己整理了一套成熟的框架,開發項目時只需要在上面添磚加瓦的,那么也可以把這套框架也打包進Dll文件里,甚至可以做到多個項目共用這一份 Dll 文件。
## 具體配置
構建輸出的以下這四個文件
~~~
├── polyfill.dll.js
├── polyfill.manifest.json
├── react.dll.js
└── react.manifest.json
~~~
和以下這一個文件
~~~
├── main.js
~~~
動態鏈接庫文件相關的文件需要由一份獨立的構建輸出,用于給主構建使用。
新建一個 Webpack 配置文件`webpack_dll.config.js`專門用于構建它們,如下:
~~~
const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
// JS 執行入口文件
entry: {
// 把 React 相關模塊的放到一個單獨的動態鏈接庫
react: ['react', 'react-dom'],
// 把項目需要所有的 polyfill 放到一個單獨的動態鏈接庫
polyfill: ['core-js/fn/object/assign', 'core-js/fn/promise', 'whatwg-fetch'],
},
output: {
// 輸出的動態鏈接庫的文件名稱,[name] 代表當前動態鏈接庫的名稱,
// 也就是 entry 中配置的 react 和 polyfill
filename: '[name].dll.js',
// 輸出的文件都放到 dll 目錄下
path: path.resolve(__dirname, 'vendors'),
// 存放動態鏈接庫的全局變量名稱,例如對應 react 來說就是 _dll_react
// 之所以在前面加上 _dll_ 是為了防止全局變量沖突
library: '_dll_[name]',
},
plugins: [
// 接入 DllPlugin
new DllPlugin({
// 動態鏈接庫的全局變量名稱,需要和 output.library 中保持一致
// 該字段的值也就是輸出的 manifest.json 文件 中 name 字段的值
// 例如 react.manifest.json 中就有 "name": "_dll_react"
name: '_dll_[name]',
// 描述動態鏈接庫的 manifest.json 文件輸出時的文件名稱
path: path.join(__dirname, 'dist', '[name].manifest.json'),
}),
],
};
~~~
主 Webpack 配置文件如下:
~~~
...
entry: {
// 定義入口 Chunk
main: './main.js'
},
output: {
// 輸出文件的名稱
filename: '[name].js',
// 輸出文件都放到 dist 目錄下
path: path.resolve(__dirname, 'dist'),
},
...
plugins: [
// 告訴 Webpack 使用了哪些動態鏈接庫
new DllReferencePlugin({
// 描述 react 動態鏈接庫的文件內容
manifest: require('./vendors/react.manifest.json'),
}),
new DllReferencePlugin({
// 描述 polyfill 動態鏈接庫的文件內容
manifest: require('./vendors/polyfill.manifest.json'),
}),
],
devtool: 'source-map'
...
~~~
以 DllPlugin 生成出 動態鏈接庫文件 `react.dll.js`文件為例,其文件內容大致如下:
```
var _dll_react = (function(modules) {
// ... 此處省略 webpackBootstrap 函數代碼
}([
function(module, exports, __webpack_require__) {
// 模塊 ID 為 0 的模塊對應的代碼
},
function(module, exports, __webpack_require__) {
// 模塊 ID 為 1 的模塊對應的代碼
},
// ... 此處省略剩下的模塊對應的代碼
]));
```
可見一個動態鏈接庫文件中包含了大量模塊的代碼,這些模塊存放在一個數組里,用數組的索引號作為 ID。 并且還通過`_dll_react`變量把自己暴露在了全局中,也就是可以通過`window._dll_react`可以訪問到它里面包含的模塊。
## 執行構建
在修改好以上兩個 Webpack 配置文件后,需要重新執行構建。 重新執行構建時要注意的是需要先把動態鏈接庫相關的文件編譯出來,因為主 Webpack 配置文件中定義的 DllReferencePlugin 依賴這些文件。
執行構建時流程如下:
1. 如果動態鏈接庫相關的文件還沒有編譯出來,就需要先把它們編譯出來。方法是執行`webpack --progress --colors --config ./webpack-dll.config.js`命令。
2. 在確保動態鏈接庫存在時,才能正常的編譯出入口執行文件。方法是執行`webpack`命令。這時你會發現構建速度有了非常大的提升。
## 后續操作
1. 如何在業務代碼里使用Dll文件打包的module/資源?
不需要刻意做些什么,該怎么 import 就怎么 import,webpack 都會幫你處理好的了。
2. 如何整合 Dll?
?在每個頁面里,都要按這個順序來加載js文件:Dll 文件 => SplitChunksPlugin生成的公用chunk文件(如果有) => 頁面本身的入口文件。
有兩個注意事項:
1. 如果你是像我一樣利用 HtmlWebpackPlugin 來生成 HTML 并自動加載 chunk 的話,還需要在`html`中手寫插入`react.dll.js`。
2. 為了完全分離源文件和編譯后生成的文件,也為了方便在編譯前可以清空 build 目錄,不應直接把 Dll 文件編譯生成到build 目錄里,我建議可以先生成到源文件 src 目錄里,再用 file-loader 給原封不動搬運過去。
3. 自動動注入`DLL`到`html`中
另外可以通過 [html-webpack-tags-plugin](https://www.npmjs.com/package/html-webpack-tags-plugin) 自動注入`DLL`到`html`中
# DefinePlugin
[DefinePlugin](https://webpack.js.org/plugins/define-plugin/#usage) 允許我們**創建全局變量**,可以在編譯時進行設置,因此我們可以使用該屬性來設置全局變量來區分開發環境和正式環境。這就是 DefinePlugin 的基本功能。
因此我們可以在 `webpack.config.js` 中添加如下代碼配置全局變量信息了,因為當 webpack 進行編譯的時候會全局設置變量;如下代碼:
```
...
module.exports = {
plugins: [
// 設置環境變量信息
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify('5fa3b9'),
BROWSER_SUPPORTS_HTML5: true,
TWO: '1+1',
'typeof window': JSON.stringify('object'),
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
}
})
]
}
```
在 打包命令上,前面加上了 `NODE_ENV=development` 命令,在 build 打包命令上前面加上了 `NODE_ENV=production`,因此繼續查看代碼結果變為如下:
~~~
console.log('Running App version ' + VERSION); // 打印 Running App version 5fa3b9
console.log(PRODUCTION); // 打印 true
console.log(process.env); // 打印 { NODE_ENV: 'development' }
~~~
可以看到這個時候 `process.env.NODE_ENV` 才有值,因此在項目打包中,為了區分開發環境和正式環境像如上配置即可,然后在 `webpack.config.js` 中通過 `process.env.NODE_ENV` 這個來區分正式環境還是開發環境即可。
> [一個被忽視的webpack插件](https://github.com/akira-cn/FE_You_dont_know/issues/14)
- 講解 Markdown
- 示例
- SVN
- Git筆記
- github 相關
- DESIGNER'S GUIDE TO DPI
- JS 模塊化
- CommonJS、AMD、CMD、UMD、ES6
- AMD
- RequrieJS
- r.js
- 模塊化打包
- 學習Chrome DevTools
- chrome://inspect
- Chrome DevTools 之 Elements
- Chrome DevTools 之 Console
- Chrome DevTools 之 Sources
- Chrome DevTools 之 Network
- Chrome DevTools 之 Memory
- Chrome DevTools 之 Performance
- Chrome DevTools 之 Resources
- Chrome DevTools 之 Security
- Chrome DevTools 之 Audits
- 技巧
- Node.js
- 基礎知識
- package.json 詳解
- corepack
- npm
- yarn
- pnpm
- yalc
- 庫處理
- Babel
- 相關庫
- 轉譯基礎
- 插件
- AST
- Rollup
- 基礎
- 插件
- Webpack
- 詳解配置
- 實現 loader
- webpack 進階
- plugin 用法
- 輔助工具
- 解答疑惑
- 開發工具集合
- 花樣百出的打包工具
- 紛雜的構建系統
- monorepo
- 前端工作流
- 爬蟲
- 測試篇
- 綜合
- Jest
- playwright
- Puppeteer
- cypress
- webdriverIO
- TestCafe
- 其他
- 工程開發
- gulp篇
- Building With Gulp
- Sass篇
- PostCSS篇
- combo服務
- 編碼規范檢查
- 前端優化
- 優化策略
- 高性能HTML5
- 瀏覽器端性能
- 前后端分離篇
- 分離部署
- API 文檔框架
- 項目開發環境
- 基于 JWT 的 Token 認證
- 扯皮時間
- 持續集成及后續服務
- 靜態服務器搭建
- mock與調試
- browserslist
- Project Starter
- Docker
- 文檔網站生成
- ddd