上一篇的最後提出了對模塊化打包方案或工具的訴求:
- 能夠將散落的模塊打包到一起;
- 能夠編譯代碼中的新特性;
- 能夠支持不同種類的前端資源模塊。
目前,前端領域有一些工具能夠很好的滿足以上這 3 個需求,其中最爲主流的就是 Webpack、Parcel 和 Rollup。
如何使用 Webpack 實現模塊化打包?
- Webpack 作爲一個模塊打包工具,本身就可以解決模塊化代碼打包的問題,將零散的 JavaScript 代碼打包到一個 JS 文件中。
- 對於有環境兼容問題的代碼,Webpack 可以在打包過程中通過
Loader
機制對其實現編譯轉換,然後再進行打包。- 對於不同類型的前端模塊類型,Webpack 支持在 JavaScript 中以模塊化的方式載入任意類型的資源文件,例如,我們可以通過 Webpack 實現在 JavaScript 中加載 CSS 文件,被加載的 CSS 文件將會通過 style 標籤的方式工作。
- Webpack 還具備代碼拆分的能力,它能夠將應用中所有的模塊按照我們的需要分塊打包。
🌰
└─ webpack_test
├── src
│ ├── heading.js
│ └── index.js
└── index.html
// ./src/heading.js
export default () => {
const element = document.createElement('h2')
element.textContent = 'Hello webpack'
element.addEventListener('click', () => alert('Hello webpack'))
return element
}
// ./src/index.js
import createHeading from './heading.js'
const heading = createHeading()
document.body.append(heading)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack - 快速上手</title>
</head>
<body>
<script type="module" src="src/index.js"></script>
</body>
</html>
1. 安裝 Webpack 的核心模塊以及它的 CLI 模塊
npm init --yes #先初始化一個package.json文件,用來管理npm依賴版本
npm i webpack webpack-cli --save-dev
webpack
是 Webpack 的核心模塊,webpack-cli
是 Webpack 的 CLI 程序,用來在命令行中調用 Webpack。
安裝完成之後,webpack-cli
所提供的 CLI 程序就會出現在 node_modules/.bin
目錄當中,我們可以通過 npx
快速找到 CLI 並運行它,具體操作如下:
npx webpack --version
# v4.43.0
npx
是 npm 5.2 以後新增的一個命令,可以用來更方便的執行遠程模塊或者項目 node_modules 中的 CLI 程序。
2. 運行 Webpack
有了 Webpack 後,就可以直接運行 webpack 命令來打包 JS 模塊代碼:
npx webpack
這個命令在執行的過程中,Webpack 會自動從 src/index.js
文件開始打包,然後根據代碼中的模塊導入操作,自動將所有用到的模塊代碼打包到一起。
完成之後,控制檯會提示:順着 index.js 有兩個 JS 文件被打包到了一起。與之對應的就是項目的根目錄下多出了一個 dist
目錄,我們的打包結果就存放在這個目錄下的 main.js
文件中。
修改 inde.html
中的代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack - 快速上手</title>
</head>
<body>
<script src="dist/main.js"></script>
</body>
</html>
成功使用。
對於 Webpack 最基本的使用,總結下來就是:先安裝 webpack 相關的 npm 包,然後使用 webpack-cli 所提供的命令行工具進行打包。
配置 Webpack 的打包過程
🌰:需要它的打包入口是 src/main.js
。
那就需要通過配置文件的方式修改 Webpack 的默認配置,在項目的根目錄下添加一個 webpack.config.js
,具體結構如下:
└─ 02-configuation
├── src
│ ├── heading.js
│ └── main.js
├── index.html
├── package.json
+ └── webpack.config.js ···················· Webpack 配置文件
webpack.config.js 是一個運行在 Node.js 環境中的 JS 文件,也就是說我們需要按照 CommonJS 的方式編寫代碼,這個文件可以導出一個對象,我們可以通過所導出對象的屬性完成相應的配置選項。
添加一個 entry
屬性,這個屬性的作用就是指定 Webpack 打包的入口文件路徑。我們將其設置爲 src/main.js
,具體代碼如下所示:
// ./webpack.config.js
module.exports = {
entry: './src/main.js'
}
配置完成之後,回到命令行終端重新運行打包命令,此時 Webpack 就會從 src/main.js
文件開始打包。
還可以通過 output
屬性設置輸出文件的位置。
output
屬性的值必須是一個對象,通過這個對象的filename
指定輸出文件的文件名稱,path
指定輸出的目錄。
具體代碼如下所示:
// ./webpack.config.js
const path = require('path')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'output')
}
}
Webpack 工作模式
Webpack 4 新增了一個工作模式的用法,這種用法大大簡化了 Webpack 配置的複雜程度。你可以把它理解爲針對不同環境的幾組預設配置:
production
模式下,啓動內置優化插件,自動優化打包結果,打包速度偏慢;development
模式下,自動優化打包速度,添加一些調試過程中的輔助插件;none
模式下,運行最原始的打包,不做任何額外處理。
修改 Webpack 工作模式的方式有兩種:
- 通過
CLI --mode
參數傳入; - 通過配置文件設置
mode
屬性。
如何通過 Loader 實現特殊資源加載?
🌰:通過 Webpack 打包項目中的一個 CSS 文件。
$ npm install css-loader --save-dev
// ./src/webpack.config.js
module.exports = {
entry: './src/main.css',
output: {
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.css$/, // 根據打包過程中所遇到文件路徑匹配是否使用這個 loader
use: 'css-loader' // 指定具體的 loader
}
]
}
}
在配置對象的 module
屬性中添加一個 rules
數組。這個數組就是我們針對資源模塊的加載規則配置,其中的每個規則對象都需要設置兩個屬性:
test
:它是一個正則表達式,用來匹配打包過程中所遇到文件路徑,這裏我們是以 .css 結尾;use
:它用來指定匹配到的文件需要使用的 loader,這裏用到的是 css-loader。
配置完成過後,回到命令行終端重新運行打包命令,打包過程就不會再出現錯誤了,因爲這時 CSS 文件會交給 css-loader 處理過後再由 Webpack 打包。
不過此時 main.css 模塊並沒有工作。只需要再額外添加一個 style-loader
,樣式就可以正常工作了。
$ npm install style-loader --save-dev
module: {
rules: [
{
test: /\.css$/,
// 對同一個模塊使用多個 loader,注意順序
use: [
'style-loader',
'css-loader'
]
}
]
}
如何利用插件機制橫向擴展 Webpack 的構建能力
clean-webpack-plugin
自動清除輸出目錄的插件
$ npm install clean-webpack-plugin --save-dev
安裝過後,在 Webpack 的配置文件中導入 clean-webpack-plugin
插件,這個插件模塊導出了一個叫作 CleanWebpackPlugin
的成員,我們先把它解構出來,具體代碼如下:
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
回到配置對象中,添加一個 plugins
屬性,這個屬性就是專門用來配置插件的地方,它是一個數組,添加一個插件就是在這個數組中添加一個元素。
絕大多數插件模塊導出的都是一個類型,CleanWebpackPlugin
也不例外,使用它,就是通過這個類型創建一個實例,放入 plugins 數組中,具體代碼如下:
// ./webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new CleanWebpackPlugin()
]
}
測試 clean-webpack-plugin 插件的效果:回到命令行終端,再次運行 Webpack 打包,此時之前的打包結果就不會存在了,dist 目錄中存放的就都是我們本次打包的結果。
html-webpack-plugin
用於生成HTML的插件
$ npm install html-webpack-plugin --save-dev
安裝完成過後,回到配置文件,載入這個模塊。
html-webpack-plugin 插件默認導出的就是插件類型,不需要再解構內部成員。
具體如下:
const HtmlWebpackPlugin = require('html-webpack-plugin')
有了這個類型過後,回到配置對象的 plugins 屬性中,同樣需要添加一下這個類型的實例對象,完成這個插件的使用,具體配置代碼如下:
// ./webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin()
]
}
copy-webpack-plugin
用於複製文件的插件
安裝 copy-webpack-plugin
插件,安裝完成後,回到配置文件中,導入這個插件類型。然後同樣在 plugins
屬性中添加一個這個類型的實例,具體代碼如下:
// ./webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js'
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Webpack Plugin Sample',
template: './src/index.html'
}),
new CopyWebpackPlugin([
'public' // 需要拷貝的目錄或者路徑通配符
])
]
}
這個插件類型的構造函數需要傳入一個字符串數組,用於指定需要拷貝的文件路徑。它可以是一個通配符,也可以是一個目錄或者文件的相對路徑。我們這裏傳入的是 public 目錄,表示將這個目錄下所有文件全部拷貝到輸出目錄中。還可以在這個數組中繼續添加其它路徑,這樣它在工作時可以同時拷貝。
配置完成以後回到命令行終端,再次運行 Webpack,此時 public 目錄下的文件就會同時拷貝到輸出目錄中。
名稱 | 鏈接 |
---|---|
file-loader | https://webpack.js.org/loaders/file-loader |
url-loader | https://webpack.js.org/loaders/url-loader |
babel-loader | https://webpack.js.org/loaders/babel-loader |
style-loader | https://webpack.js.org/loaders/style-loader |
css-loader | https://webpack.js.org/loaders/css-loader |
sass-loader | https://webpack.js.org/loaders/sass-loader |
postcss-loader | https://webpack.js.org/loaders/postcss-loader |
eslint-loader | https://github.com/webpack-contrib/eslint-loader |
vue-loader | https://github.com/vuejs/vue-loader |
Webpack 爲每一個工作環節都預留了合適的鉤子,我們在擴展時只需要找到合適的時機去做合適的事情就可以了。
探索 Webpack 運行機制與核心工作原理
工作過程
Webpack 啓動後,會根據配置,找到項目中的某個指定文件(一般這個文件都會是一個 JS 文件)作爲入口。然後順着入口文件中的代碼,根據代碼中出現的 import(ES Modules)或者是 require(CommonJS)之類的語句,解析推斷出來這個文件所依賴的資源模塊,然後再分別去解析每個資源模塊的依賴,週而復始,最後形成整個項目中所有用到的文件之間的依賴關係樹。
有了這個依賴關係樹過後, Webpack 會遍歷(遞歸)這個依賴樹,找到每個節點對應的資源文件,然後根據配置選項中的 Loader 配置,交給對應的 Loader 去加載這個模塊,最後將加載的結果放入 bundle.js(打包結果)中,從而實現整個項目的打包。
工作原理
Webpack 核心工作過程中的關鍵環節,明確“查閱”源碼的思路:
- Webpack CLI 啓動打包流程;
- 載入 Webpack 核心模塊,創建 Compiler 對象;
- 使用 Compiler 對象開始編譯整個項目;
- 從入口文件開始,解析模塊依賴,形成依賴關係樹;
- 遞歸依賴樹,將每個模塊交給對應的 Loader 處理;
- 合併 Loader 處理完的結果,將打包結果輸出到 dist 目錄。
🔗 相關鏈接:
- 上一篇:Webpack(一):背景介紹
- 下一篇:Webpack(三):高階內容