前言
作為前端開發者,相信大家或多或少都接觸過webpack,現如今webpack已經滲透在了前端的各個方面,所以我們有必要來了解並學習webpack,webpack 是一種用於構建 JavaScript 應用程序的靜態模塊打包器,它能夠以一種相對一致且開放的處理方式,加載應用中的所有資源文件(圖片、CSS、視頻、字體文件等),並將其合併打包成瀏覽器兼容的 Web 資源文件。webpack相比其它構建工具功能更加強大,可擴展性也更強,它能夠融合多種工程化工具,將開發階段的應用代碼編譯、打包成適合網絡分發、客户端運行的應用產物。
核心概念
輸入輸出
entry
webpack的構建入口,入口起點(entry point) 指示 webpack 應該使用哪個模塊,來作為構建其內部 依賴圖(dependency graph) 的開始。進入入口起點後,webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。
// 單入口
module.exports = {
entry: './src/main.js'
}
// 多入口
module.exports = {
entry: {
a: './src/a.js',
b: './src/b.js'
}
}
output
output 屬性告訴 webpack 在哪裏輸出它所創建的 bundle,以及如何命名這些文件。主要輸出文件的默認值是./dist/main.js,其他生成文件默認放置在./dist文件夾中。
// 單入口
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
}
}
// 多入口
module.exports = {
entry: {
a: './src/a.js',
b: './src/b.js'
},
output: {
filename: '[name].[hash:6].js', // 通過佔位符確保文件名唯一,考慮緩存問題,還可以為文件名加上hash
path: __dirname + '/dist',
publicPath: '/', // 生產環境一般是CDN地址,開發環境配置為/或不配置
}
}
模塊處理
loader
webpack 只能理解 JavaScript 和 JSON 文件,這是 webpack 開箱可用的自帶能力。loader 讓 webpack 能夠去處理其他類型的文件,並將它們轉換為有效 模塊,以供應用程序使用,以及被添加到依賴圖中。
比如:配置webpack為css文件應用css-loader
module.exports = {
module: {
rules: [
{test: /.css$/, use: 'css-loader'}
]
}
}
module.rules 允許你在 webpack 配置中指定多個 loader。 這種方式是展示 loader 的一種簡明方式,並且有助於使代碼變得簡潔和易於維護。
plugin
插件 是 webpack 的 支柱 功能。Webpack 自身也是構建於你在 webpack 配置中用到的 相同的插件系統 之上!
插件目的在於解決 loader 無法實現的其他事。Webpack 提供很多開箱即用的 插件。
比如:為編譯過程添加進度報告插件
const Webpack = require('webpack')
module.exports = {
plugins: [new Webpack.ProgressPlugin()]
}
resolve
用於配置模塊路徑解析規則,可用於幫助 Webpack 更精確、高效地找到指定模塊
比如配置別名:
創建 import 或 require 的別名,來確保模塊引入變得更簡單。例如,一些位於 src/ 文件夾下的常用模塊:
module.exports = {
resolve: {
alias: {
node_modules: path.resolve(__dirname, './node_modules'),
'@': path.resolve(__dirname, './src'),
api: path.resolve(__dirname, './src/api'),
components: path.join(__dirname, './src/components'),
}
}
}
module
這些選項決定了如何處理項目中的不同類型的模塊。
比如我們常見的loader就是在module.rules內配置的。
module.exports = {
module: {
rules: [
{test: /.css$/, use: 'css-loader'}
]
}
}
externals
用於聲明外部資源,Webpack 會直接忽略這部分資源,跳過這些資源的解析、打包操作
比如防止將某些 import 的包(package)打包到 bundle 中,而是在運行時(runtime)再去從外部獲取這些擴展依賴(external dependencies) 。
比如:從CDN引入Vue
<!-- index.html -->
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.3/vue.min.js"></script>
// webpack.config.js
module.exports = {
externals: {
vue: 'vue'
}
}
後處理
optimization
用於控制如何優化產物包體積,內置 Dead Code Elimination、Scope Hoisting、代碼混淆、代碼壓縮等功能
從 webpack 4 開始,會根據你選擇的
mode來執行不同的優化, 不過所有的優化還是可以手動配置和重寫。
module.exports = {
//...
optimization: {
chunkIds: 'named',
},
};
target
用於配置編譯產物的目標運行環境,支持 web、node、electron 等值,不同值最終產物會有所差異
比如:target設置為node,webpack將在node環境下進行編譯
module.exports = {
target: 'node'
}
mode
提供 mode 配置選項,告知 webpack 使用相應模式的內置優化。
string = 'production': 'none' | 'development' | 'production'
module.exports = {
mode: 'development',
};
或者從cli--mode 參數進行傳遞
webpack --mode development
開發效率
watch
啓用 Watch 模式。這意味着在初始構建之後,webpack 將繼續監聽任何已解析文件的更改。
module.exports = {
watch: true
}
⚠️注意:webpack-dev-server和webpack-dev-middleware默認是開啓watch模式的
devtool
此選項控制是否生成,以及如何生成 source map。
string = 'eval' | false
選擇一種 source map 風格來增強調試過程。不同的值會明顯影響到構建(build)和重新構建(rebuild)的速度。
devServer
用於配置與 HMR 強相關的開發服務器功能
通過 webpack-dev-server 的這些配置,能夠以多種方式改變其行為,這裏比較常見的配置有:port、host、proxy等
module.exports = {
devServer: {
static: {
directory: path.join(__dirname, 'public'),
},
compress: true,
port: 9000,
}
}
cache
Webpack 5 之後,該項用於控制如何緩存編譯過程信息與編譯結果
緩存生成的 webpack 模塊和 chunk,來改善構建速度。cache 會在開發 模式被設置成 type: 'memory' 而且在 生產 模式 中被禁用。 cache: true 與 cache: { type: 'memory' } 配置作用一致。 傳入 false 會禁用緩存:
module.exports = {
cache: false
}
上手配置
瞭解完上面這些webpack核心概念,我們可以嘗試來手動配置好一個Vue開發環境
初始化項目
首先npm init -y初始化package.json文件
接着安裝好我們的webpack、webpack-cli
npm i webpack webpack-cli -D
⚠️注意: 我這裏的webpack是5版本的
"webpack": "^5.85.1",
"webpack-cli": "^4.7.2",
處理Vue代碼
原生 Webpack 並不能處理這種內容格式的文件,為此我們需要引入專用於 Vue SFC 的加載器:vue-loader
npm i vue-loader
// webpack.config.js
const Webpack = require('webpack')
const {VueLoaderPlugin} = require('vue-loader')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.[hash:6].js',
path: __dirname + '/dist',
},
module: {
rules:[
{test: /.vue$/, use: 'vue-loader'},
]
},
plugins:[
new Webpack.ProgressPlugin(),
new VueLoaderPlugin(),
],
}
提示:vue-loader 庫同時提供用於處理 SFC 代碼轉譯的 Loader 組件,與用於處理上下文兼容性的 Plugin 組件,兩者需要同時配置才能正常運行。
此時我們的文件結構是這樣的,大致與Vue項目結構一致
嘗試啓動看一下:
// package.json
"dev": "webpack --mode development",
npm run dev
由於我們的vue文件中有css內容,而webpack默認是不理解css內容的,所以導致報錯了
處理CSS內容
這裏需要安裝style-loader、css-loader來進行處理。
修改webpack配置
// webpack.config.js
const Webpack = require('webpack')
const {VueLoaderPlugin} = require('vue-loader')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.[hash:6].js',
path: __dirname + '/dist',
},
module: {
rules:[
{test: /.vue$/, use: 'vue-loader'},
{
test: /.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins:[
new Webpack.ProgressPlugin(),
new VueLoaderPlugin(),
],
}
此時再跑起來,發現沒有報錯了。
處理JS內容
我們平時在開發中肯定會用到ES6語法,這裏我們也需要配置對應的loader來進行處理
安裝babel-loader
npm i babel-loader @babel/preset-env @babel/core
配置
module.exports = {
module: {
rules: [
{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
exclude: /node_modules/
}
]
}
}
當然這裏的options配置你也可以在.babelrc或babel.config.js中單獨配置。
處理圖片資源
與CSS一樣,webpack也是默認不理解圖片的,所以這裏也需要配置loader進行處理。
webpack4
在webpack4中,我們常用的處理圖片的loader有:file-loader、url-loader
file-loader將文件發送到輸出目錄
// webpack.config.js
module.exports = {
// ...
module: {
rules: [{
test: /.(png|jpg|gif|jpeg)$/,
use: ['file-loader']
}],
},
};
經過 file-loader 處理後,原始圖片會被重命名並複製到產物文件夾,同時在代碼中插入圖片 URL 地址
url-loader將文件作為 data URI 內聯到 bundle 中,它有兩種表現,對於小於閾值limit的圖像直接轉化為 base64 編碼;大於閾值的圖像則調用file-loader進行加載
module.exports = {
// ...
module: {
rules: [{
test: /.(png|jpg|gif|jpeg)$/,
use: [{
loader: 'url-loader',
options: {
limit: 1024
}
}]
}],
},
};
經過 url-loader 處理後,小於 limit 參數即 1024B 的圖片會被轉譯為 Base64 編碼,對於超過 limit 值的圖片則直接調用 file-loader 完成加載。
webpack5
file-loader、url-loader並不侷限於處理圖片,它們還可以被用於加載任意類型的多媒體或文本文件,使用頻率極高,幾乎已經成為標配組件!所以 Webpack5 直接內置了這些能力,開箱即可使用。
用法上,原本需要安裝、導入 Loader,Webpack5 之後只需要通過 module.rules.type 屬性指定資源類型即可
比如:
module.exports = {
// ...
module: {
rules: [{
test: /.(png|jpg|gif|jpeg)$/,
type: 'asset/resource'
}],
},
};
運行頁面
配置了這麼多內容,我們卻還不能看到頁面的內容,心裏肯定不樂意,上面這幾步操作其實就相當於翻譯 Vue SFC 文件的內容,接下來我們還需要讓頁面真正運行起來。
粗暴方案
有一種快速驗證我們的打包配置是否正確:我們只需要新建一個html文件,將打包產物引入進去,並創建好掛載節點就可以
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="../dist/bundle.42603d.js"></script>
</body>
</html>
我們再把這個html文件在瀏覽器打開,就能看到我們vue項目能夠正常打開了。
這種方案有一種弊端就是:我們打包出來的文件一般都會帶有hash,那就需要我們每次在打包完後去html文件修改引入的文件,這樣是不是有點太費勁了,你們能忍嗎?
優雅方案
上面那種方案在日常開發中顯然是不能接受的,身為程序員能偷懶的地方必須得偷懶!
我們可以利用下面兩個工具讓這個過程變得更加智能化、自動化。
html-webpack-plugin: 自動生成 HTML 頁面
HtmlWebpackPlugin 簡化了 HTML 文件的創建,以便為你的 webpack 包提供服務。這對於那些文件名中包含哈希值,並且哈希值會隨着每次編譯而改變的 webpack 包特別有用。
webpack-dev-server :讓頁面真正運行起來,並具備熱更新能力。
webpack-dev-server 主要提供兩種功能:
- 結合 Webpack 工作流,提供基於 HTTP(S) 協議的靜態資源服務;
- 提供資源熱更新能力,在保持頁面狀態前提下自動更新頁面代碼,提升開發效率。
安裝
npm i html-webpack-plugin webpack-dev-server
修改配置
const Webpack = require('webpack')
const {VueLoaderPlugin} = require('vue-loader')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/main.js',
output: {
filename: 'bundle.[hash:6].js',
path: __dirname + '/dist',
},
module: {
rules:[
{test: /.vue$/, use: 'vue-loader'},
{
test: /.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
exclude: /node_modules/
},
{
test: /.(png|jpg|gif|jpeg)$/,
type: 'asset/resource',
}
]
},
plugins:[
new Webpack.ProgressPlugin(),
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html',
filename: 'index.html'
})
],
devServer: {
hot: true,
open: true
}
}
修改啓動腳本
"dev": "webpack serve --mode development"
運行
npm run dev
這時webpack就能自動幫我們打開瀏覽器運行頁面了
vue文件內容如下:
<template>
<div class="title">webpack + vue -- {{ name }}</div>
<img src="../asset/1.png" class="top_bg" />
</template>
<script setup>
import { ref } from 'vue'
const name = ref('前端南玖')
</script>
<style>
.title {
font-size: 16px;
font-weight: bold;
color: salmon;
}
.top_bg {
width: 100%;
height: auto;
}
</style>
如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公眾號首發,關注 前端南玖 第一時間獲取最新文章~