本文爲做者第二次專門對 Webpack 的知識點進行深刻和實踐,根據理解和實踐的結果進行總結的;javascript
文章內容參考書籍《深刻淺出 Webpack》,由於該書籍基於 Webpack 3.4.0 版本,本文的實踐基於 Webpack 4.28.2 版本,因此也踩了很多因爲模塊版本問題出現的坑,已經彙總到第 6 章節 踩坑彙總,你們記得避免踩坑;也印證了那句哲理:紙上得來終覺淺,絕知此事要躬行 ...css
博客 github地址爲:github.com/fengshi123/… ,彙總了做者的全部博客,也歡迎關注及 star ~html
本文實踐 demo 的 github地址java
構建工具就是將源代碼轉換成可執行的 JavaScript、CSS、HTML 代碼,包括如下內容:node
代碼轉換:將 TypeScript 編譯成 JavaScript、將 SCSS 編譯成 CSS 等;webpack
文件優化:壓縮 JavaScript、CSS、HTML 代碼,壓縮合並圖片等;git
代碼分割:提取多個頁面的公共代碼,提取首屏不須要執行部分的代碼,讓其異步加載;github
模塊合併:在採用模塊化的項目裏會有不少個模塊和文件,須要經過構建功能將模塊分類合併成一個文件;web
自動刷新:監聽本地源代碼的變化,自動從新構建、刷新瀏覽器;npm
代碼校驗:在代碼被提交到倉庫前須要校驗代碼是否符合規範,以及單元測試是否經過;
自動發佈:更新代碼後,自動構建出線上發佈代碼並傳輸給發佈系統;
Webpack 有如下幾個核心概念:
Entry :入口,Webpack 執行構建的第一步將從 entry 開始,可抽象成輸入;
Module:模塊,配置處理模塊的規則;在 Webpack 裏一切皆模塊,一個模塊對應一個文件;Webpack 會從配置的 Entry 開始遞歸找出全部依賴的模塊;
Loader:模塊轉換器,用於將模塊的原內容按照需求轉換成新內容;
Resolve:配置尋找模塊的規則;
Plugin:擴展插件,在 Webpack 構建流程中的特定時機會廣播對應的事件,插件能夠監聽這些事情的發生,在特定的時機作對應的事情;
Output:輸出結果,在 Webpack 通過一系列處理並得出最終想要的代碼後輸出結果;
Chunk:代碼塊,一個 Chunk 由多個模塊組合而成,用於代碼合併與分割;
(1)初始化參數:從配置文件和 Shell 語句中讀取與合併參數,得出最終的參數;
(2)開始編譯:用上一步獲得的參數初始化 Compiler 對象,加載全部配置的插件,經過執行對象的 run 方法開始執行編譯;
(3)肯定入口:根據配置中的 entry 找出全部入口文件;
(4)編譯模塊:從入口文件出發,調用全部配置的 Loader 對模塊進行翻譯,再找出該模塊依賴的模塊,再遞歸本步驟直到全部入口依賴的文件都通過了本步驟的處理;
(5)完成模塊編譯:在通過第 4 步使用 Loader 翻譯完全部模塊後,獲得了每一個模塊被翻譯後的最終內容及它們之間的依賴關係;
(6)輸出資源:根據入口和模塊之間的依賴關係,組裝成一個個包含多個模塊的 Chunk,再將每一個 Chunk 轉換成一個單獨的文件加入輸出列表中,這是能夠修改輸出內容的最後機會;
(7)輸出完成:在肯定好輸出內容後,根據配置肯定輸出的路徑和文件名,將文件的內容寫入文件系統中;
在以上過程當中,Webpack 會在特定的時間點廣播特定的事件,插件在監聽到感興趣的事件後會執行特定的邏輯,而且插件能夠調用 Webpack 提供的 API 改變 Webpack 的運行結果;
一、新建 Web 項目
新建一個目錄,再進入項目根目錄執行 npm init 來初始化最簡單的採用了模塊化開發的項目;最終生成 package.json 文件;
$ npm init
複製代碼
二、安裝 Webpack 到本項目
(1)查看 Webpack 版本
運行如下命令能夠查看 Webpack 的版本號
$ npm view webpack versions
複製代碼
(2)安裝 Webpack
能夠選擇(1)步驟羅列獲得的 Webpack 版本號,也能夠安裝最新穩定版、最新體驗版本,相關命令以下所示,我選擇安裝 4.28.2 版本(沒有爲何,就想裝個 4.x 的版本);
// 安裝指定版本
npm i -D webpack@4.28.2
// 安裝最新穩定版
npm i -D webpack
// 安裝最新體驗版本
npm i -D webpack@beta
複製代碼
(3)安裝 Webpack 腳手架
須要安裝 Webpack 腳手架,才能在命令窗口執行 Webpack 命令,運行如下命令安裝 Webpack 腳手架;
$ npm i -D webpack-cli
複製代碼
三、使用 Webpack
使用 Webpack 構建一個採用 CommonJS 模塊化編寫的項目;
(1)新建頁面入口文件 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webpack</title>
</head>
<body>
<!--導入 Webpack 輸出的 JavaScript 文件-->
<script src="./dist/bundle.js"></script>
</body>
</html>
複製代碼
(2)新建須要用到的 JS 文件
show.js 文件
// 操做 DOM 元素,把 content 顯示到網頁上
function show(content) {
window.document.getElementById('app').innerText = 'Hello,' + content;
}
// 經過 CommonJS 規範導出 show 函數
module.exports = show;
複製代碼
main.js 文件
// 經過 CommonJS 規範導入 show 函數
const show = require('./show.js');
// 執行 show 函數
show('Webpack');
複製代碼
(3)新建 Webpack 配置文件 webpack.config.js
const path = require('path');
module.exports = {
// JavaScript 執行入口文件
entry: './main.js',
output: {
// 把全部依賴的模塊合併輸出到一個 bundle.js 文件
filename: 'bundle.js',
// 輸出文件都放到 dist 目錄下
path: path.resolve(__dirname, './dist'),
}
};
複製代碼
(4)執行 webpack 命令進行構建
在 package.json 文件中配置編譯命令,以下所示:
"scripts": {
"build": "webpack --config webpack.config.js",
},
複製代碼
執行如下命令進行項目的 Webpack 編譯,成功後會在項目根目錄下生成編譯目錄 dist ;
$ npm run build
複製代碼
(5)運行 index.html
編譯成功後,咱們用瀏覽器打開 index.html 文件,能看到頁面成功顯示 「Hello Webpack」;
本節經過爲以前的例子添加樣式,來嘗試使用 Loader;
(1)新建樣式文件 main.css
#app{
text-align: center;
color:'#999';
}
複製代碼
(2)將 main.css 文件引入入口文件 main.js 中,以下所示:
// 經過 CommonJS 規範導入 CSS 模塊
require('./main.css');
// 經過 CommonJS 規範導入 show 函數
const show = require('./show.js');
// 執行 show 函數
show('Webpack');
複製代碼
(3)Loader 配置
以上修改後去執行 Webpack 構建是會報錯的,由於 Webpack 不原生支持解析 CSS 文件。要支持非 JavaScript 類型的文件,須要使用 Webpack 的 Loader 機制;
(3.1)運行如下命令,安裝 style-loader 和 css-loader,其中:
$ npm i -D style-loader css-loader
複製代碼
(3.2)進行如下配置
module:{
rules:[
{
// 用正則去匹配要用該 loader 轉換的 CSS 文件
test:/\.css$/,
use:['style-loader','css-loader']
}
]
}
複製代碼
(4)查看結果
編譯後,刷新 index.html ,查看剛剛的樣式 loader 已經起做用;
(1)安裝樣式提取插件 extract-text-webpack-plugin
$ npm i -D extract-text-webpack-plugin@next
複製代碼
(2)plugin 文件配置以下
module:{
rules:[
{
// 用正則去匹配要用該 loader 轉換的 CSS 文件
test:/\.css$/,
use:ExtractTextPlugin.extract({
use:['css-loader']
}),
}
]
},
plugins:[
new ExtractTextPlugin({
// 從 .js 文件中提取出來的 .css 文件的名稱
filename:`[name]_[hash:8].css`
}),
]
複製代碼
(3)查看結果
經過以上配置後,執行 Webapack 的執行命令,發如今 dist 目錄下,生成對應的 css 文件;存在的坑點:
(1)執行如下命令安裝 webpack-dev-server
$ npm i -D webpack-dev-server
複製代碼
在 package.json 中配置啓動命令
"scripts": {
"build": "webpack --config webpack.config.js",
"dev": "webpack-dev-server",
},
複製代碼
運行命令後,就能夠啓動 HTTP 服務
$ npm run dev
複製代碼
啓動結果以下所示,咱們能夠經過 http://localhost:8080/ 訪問咱們的 index.html 的demo
(2)實時預覽
咱們在運行命令後面添加參數 --watch 實現實時預覽,配置以下所示:
"scripts": {
"dev": "webpack-dev-server --watch"
},
複製代碼
而後咱們修改 main.js 的傳入參數,發現並不能實時預覽,也沒有報錯!!! why?
踩坑:
在 index.html 中須要將 js 的路徑修改成:
<script src="bundle.js"></script>
複製代碼
而不能是以前的(由於這個是編譯生成的,並非經過 devServer 生成放在內存的)
<script src="./dist/bundle.js"></script>
複製代碼
(3)模塊熱替換
能夠經過配置 -- hot 進行模塊熱替換;
關於優化的實踐以前有進行過實踐了,這裏再也不累述,感興趣的童鞋能夠查看做者寫的另外一篇文章《Vue項目Webpack優化實踐,構建效率提升50% 》
(1)Loader 爲模塊轉換器,用於將模塊的原內容按照需求轉換成新內容;
(2)Loader 的職責是單一的,只須要完成一種轉換,遵照單一職責原則;
(3)Webpack 爲 Loader 提供了一系列 API 供 Loader 調用,例如:
手寫一個 loader 源碼,其功能是將 /hello/gi 轉換成 HELLO,固然這個 loader 其實沒啥實際意義,純碎是爲了寫 loader 而寫 loader;固然若是你實際業務有須要編寫 loader 需求,那就要反思這個業務的合理性,由於龐大的社區,通常合理的需求都能找到對應的 loader。
(1)源碼編寫
在原有的項目底下,新建目錄 custom-loader 做爲咱們編寫 loader 的名稱,執行 npm init 命令,新建一個模塊化項目,而後新建 index.js 文件,相關源碼以下:
function convert(source){
return source && source.replace(/hello/gi,'HELLO');
}
module.exports = function(content){
return convert(content);
}
複製代碼
(2)Npm link 模塊註冊
正常咱們安裝 Loader 是從 Npm 公有倉庫安裝,也即將 Loader 發佈到 Npm 倉庫,而後再安裝到本地使用;可是咱們可使用 Npm link 作到在不發佈模塊的狀況下,將本地的一個正在開發的模塊的源碼連接到項目的 node_modules 目錄下,讓項目能夠直接使用本地的 Npm 模塊;
在 custom-loader 目錄底下,運行如下命令,將本地模塊註冊到全局:
$ npm link
複製代碼
成功結果以下:
而後在項目根目錄執行如下命令,將註冊到全局的本地 Npm 模塊連接到項目的 node_modules 下:
$ npm link custom-loader
複製代碼
成功結果以下,而且在 node_modules 目錄下能查找到對應的 loader;
該配置跟第一章節的 Webpack 配置並無任何區別,這裏再也不詳述,配置參考以下:
module:{
rules:[
{
test:/\.js/,
use:['custom-loader'],
include:path.resolve(__dirname,'show')
}
]
}
複製代碼
執行運行 or 編譯命令,就能看到咱們的 loader 起做用了。
Webpack 就像一條生產線,要通過一系列處理流程後才能將源文件轉換成輸出結果,這條生產線上的每一個處理流程的職責都是單一的,多個流程之間存在依賴關係,只有在完成當前處理後才能提交給下一個流程去處理。插件就像生產線中的某個功能,在特定的時機對生產線上的資源進行處理。
Webpack 經過 Tapable 來組織這條複雜的生產線。 Webpack 在運行過程當中會廣播事件,插件只須要監聽它所關心的事件,就能加入到這條生產線中,去改變生產線的運做。 Webpack 的事件流機制保證了插件的有序性,使得整個系統擴展性很好。
手寫一個 plugin 源碼,其功能是在 Webpack 編譯成功或者失敗時輸出提示;固然這個 plugin 其實沒啥實際意義,純碎是爲了寫 plugin 而寫 plugin;固然若是你實際業務有須要編寫 plugin 需求,那就要反思這個業務的合理性,由於龐大的社區,通常合理的需求都能找到對應的 plugin。
(1)源碼編寫
在原有的項目底下,新建目錄 custom-plugin 做爲咱們編寫 plugin 的名稱,執行 npm init 命令,新建一個模塊化項目,而後新建 index.js 文件,相關源碼以下:
class CustomPlugin{
constructor(doneCallback, failCallback){
// 保存在建立插件實例時傳入的回調函數
this.doneCallback = doneCallback;
this.failCallback = failCallback;
}
apply(compiler){
// 成功完成一次完整的編譯和輸出流程時,會觸發 done 事件
compiler.plugin('done',(stats)=>{
this.doneCallback(stats);
})
// 在編譯和輸出的流程中遇到異常時,會觸發 failed 事件
compiler.plugin('failed',(err)=>{
this.failCallback(err);
})
}
}
module.exports = CustomPlugin;
複製代碼
(2)Npm link 模塊註冊
跟 Loader 註冊同樣,咱們使用 npm link 進行註冊;
在 custom-plugin 目錄底下,運行如下命令,將本地模塊註冊到全局:
$ npm link
複製代碼
而後在項目根目錄執行如下命令,將註冊到全局的本地 Npm 模塊連接到項目的 node_modules 下:
$ npm link custom-plugin
複製代碼
若是一切順利,能夠在 node_modules 目錄下能查找到對應的 plugin;
該配置跟第一章節的 Webpack 配置並無任何區別,這裏再也不詳述,配置參考以下:
plugins:[
new CustomPlugin(
stats => {console.info('編譯成功!')},
err => {console.error('編譯失敗!')}
),
],
複製代碼
執行運行 or 編譯命令,就能看到咱們的 plugin 起做用了。
一、css-loader 如下配置
rules:[
{
// 用正則去匹配要用該 loader 轉換的 CSS 文件
test:/\.css$/,
use:['style-loader','css-loader?minimize']
}
]
複製代碼
報如下錯誤:
- options has an unknown property 'minimize'. These properties are valid:
object { url?, import?, modules?, sourceMap?, importLoaders?, localsConventio n?, onlyLocals?, esModule? }
複製代碼
緣由:
minimize 屬性在新版本已經被移除,
解決:
先去掉 minimize 選項;
二、ExtractTextPlugin 編譯如下錯誤:
緣由:
extract-text-webpack-plugin 版本號問題
解決:
從新安裝 extract-text-webpack-plugin
$ npm i -D extract-text-webpack-plugin@next
複製代碼
三、修復第2個坑以後,ExtractTextPlugin 編譯繼續報如下錯誤:
緣由:
不存在 contenthash 這個變量
解決:
更改 extract-text-webpack-plugin 的配置:
plugins:[
new ExtractTextPlugin({
// 從 .js 文件中提取出來的 .css 文件的名稱
filename:`[name]_[hash:8].css`
}),
]
複製代碼
四、添加 HappyPack 後,編譯 CSS 文件時報如下錯誤:
緣由:
css-loader 版本的問題
解決:
從新安裝 css-loader@3.2.0
本文主要基於 Webpack 的做用、核心概念、流程,Webpack 的基礎配置,Webpack 優化,編寫 Loader,編寫 Plugin ,從理論到實踐,從基礎到較難,對 Webpack 進行總結掌握,但願對你也有幫助。仍是那句話:紙上得來終覺淺,絕知此事要躬行 ...,若是你沒有手敲過,必定要多動動手 !
博客 github地址爲:github.com/fengshi123/… ,彙總了做者的全部博客,也歡迎關注及 star ~
本文實踐 demo 的 github地址