https://webpack.docschina.org/
一、Webpack快速上手
yarn init --yes 初始化package.json
yarn add webpack webpack-cli --dev 安裝webpack 和cli模塊
yarn webpack --version 查看版本
yarn webpack 打包
二、ebpack 配置文件
會按照約定打包
src/index ----> dist/main.js
在根目錄下添加文件webpack.config.js
const path = require('path')
module.exports = {
entry:'./src/main.js', //指定webpack打包入口文件路徑
output:{
filename:'dist111.js', //打包輸出文件的名稱
path:path.join(__dirname,'output'), //打包輸出文件路徑
}
}
三、Webpack 工作模式
// webpack.development.config.js
module.exports = {
mode: 'development',
};
// webpack.production.config.js
module.exports = {
mode: 'production',
};
// webpack.custom.config.js
module.exports = {
mode: 'none',
};
如果要根據(jù) webpack.config.js 中的 mode 變量更改打包行為,則必須將配置導出為函數(shù),而不是導出對象:
var config = {
entry: './app.js',
//...
};
module.exports = (env, argv) => {
if (argv.mode === 'development') {
config.devtool = 'source-map';
}
if (argv.mode === 'production') {
//...
}
return config;
};
四、Webpack 資源模塊加載
Webpack默認只會解析js代碼

安裝css-loader
yarn add css-loader --dev
webpack.config.js
module.exports={
module:{
rules:[
{
test:'/.css$/',
use:'style-loader',//先執(zhí)行css-loader,倒序執(zhí)行
use:'css-loader'
}
]
}
}
安裝style-loader
yarn add style-loader --dev
作用:將css-loader轉(zhuǎn)換的結(jié)果通過style標簽的形式追加到頁面上
Loader是Webpack的核心特性,借助于Loader就可以加載任何類型的資源
五、webpack 導入資源模塊
JavaScript驅(qū)動整個前端應用
import './heading.css'
六、webpack 文件資源加載器Loader
yarn add file-loader --dev
webpack.config.js
output:{
publicPath:'dist/'
},
module:{
rules:[
{
test:/.css$/,
use:[
'style-loader',
'css-loader',
]
},{
test:/.png$/,
use:'file-loader'
}
]
}

七、webpack URL 加載器 url-Loader
安裝url-loader
yarn add url-loader --dev
module:{
rules:[
{
test:/.css$/,
use:[
'style-loader',
'css-loader',
]
},{
test:/.png$/,
use:'url-loader'
}
]
}
yarn webpack 打包測試
小文件使用Data URLs,減少請求次數(shù)
大文件單獨提取存放,提高加載速度
module:{
rules:[
{
test:/.css$/,
use:[
'style-loader',
'css-loader',
]
},{
test:/.png$/,
use:{
loader:'url-loader',
options:{
limit:10 * 1024, //限制10K一下的用url-loader 轉(zhuǎn)換
}
}
}
]
}
超出10KB文件單獨提取存放,小于10KB文件轉(zhuǎn)換為Data URLs 嵌入代碼,需要同時安裝file-loader,url-loader對于超出的文件會去調(diào)用file-loader模塊
八、Webpack 常用加載器分類
- 編譯轉(zhuǎn)換類型 :加載到的資源模塊轉(zhuǎn)換為js代碼,例如css-loader
- 文件操作類型:加載到的資源模塊拷貝到輸出目錄,同時將文件訪問路徑向外導出,例如file-loader
- 代碼檢查類:對于加載到的資源文件進行校驗的加載器,目的是為了統(tǒng)一代碼風格 例如eslint-loader
九、Webpack 與ES2015
因為模塊打包需要,所以處理import和export,并不能夠轉(zhuǎn)換代碼中其他的es6特性,要轉(zhuǎn)換es6,const等語法,需要安裝babel-loader,而babel-loader 需要依賴babel的核心模塊,所以需要同時安裝@babel/core和用于轉(zhuǎn)換具體特性轉(zhuǎn)換插件的集合 @babel/preset-env
yarn add babel-loader @babel/core @babel/preset-env --dev
webpack.config.js
module:{
rules:[
{
test:/.js$/,
use:{
loader:'babel-loader',
options:{
presets:['@babel/preset-env']
}
}
}
]
}
再次運行yarn webpack
Webpack 只是打包工具,加載器可以用力編譯轉(zhuǎn)換代碼
十、Webpack加載資源的方式
- 一、遵循ES Modules 標準的import聲明
- 二、遵循CommonJS標準的require函數(shù)
- 三、遵循AMD標準的define函數(shù)和require函數(shù)
還有一些獨立的加載器在工作的時候也會去處理加載的資源當中導入的模塊,例如css-loader 加載的文件,@import指令和url函數(shù)也會觸發(fā)相應的資源模塊的加載,html-loader 加載的html文件當中的圖片標簽的src屬性也會觸發(fā)相應的模塊加載。
安裝html-loader
yarn add html-loader --dev
webpack.config.js
module:{
rules:[
{
test:/.html$/,
use:{
loader:'html-loader',
options:{
attrs:['img:src','a:href']
}
}
}
]
}
- 四、*樣式代碼中的@import指令和url函數(shù)
- 五、 * HTML代碼中圖片標簽的src屬性
十一、Webpack 核心工作原理

根據(jù)配置找到入口文件作為打包的入口,一般為js文件,根據(jù)代碼中的import或者require 來解析推斷文件依賴的模塊,分別去解析每一個資源模塊對應的依賴,就會形成整個項目中用到的所有文件的依賴關系的依賴樹 ,webpack會遞歸這個依賴樹,找到每個節(jié)點對應的資源文件,最后根據(jù)配置文件中的rules屬性找到這個模塊對應的加載器,然后加載這個模塊,最后會將加載到的結(jié)果放到打包結(jié)果文件當中,從而去實現(xiàn)整個項目的打包,Loader 機制是Webpack 的核心
十二、Webpack Loader的工作原理(開發(fā)一個Loader )



yarn add marked --dev
// markdown-loader.js
const marked = require('marked')
module.exports = source =>{
// return 'console.log("hello")';//必須是js
const html = marked(source)
// return `module.exports = ${JSON.stringify(html)}`;//方法1
// return `exports default = ${JSON.stringify(html)}`;//方法2
// 返回html 字符串交給下一個loader處理
return html
}
// webpack.config.js
module:{
rules:[
{
test:/.md$/,
use:[
'html-loader',
'./markdown-loader.js'
]
}
]
}
Loader 負責資源文件從輸入到輸出的轉(zhuǎn)換,loader 是一個管道的概念,對于同一個資源可以依次使用多個Loader,例如css-loader---->style-loader等
十三、Webpack 插件機制介紹
插件目的是為了增強Webpack 自動化能力,Loader是實現(xiàn)資源模塊的加載從而實現(xiàn)整體項目的打包,而Plugin解決其他的自動化工作,例如在打包之前plugin可以幫我們清除dist目錄,可以用來拷貝靜態(tài)文件至輸出目錄,壓縮輸出的代碼。webpack + plugin 實現(xiàn)了大多數(shù)前端工程化工作。
webpack != 前端工程化
十四、Webpack 常用插件--自動清除輸出目錄
yarn add clean-webpack-plugin --dev
webpack.config.js
const {cleanWebpackPlugin} = require('clean-webpack-plugin')
module.exports = {
plugins:[
new cleanWebpackPlugin()
]
}
再次運行yarn webpack
十五、Webpack 自動生成HTML插件
yarn add html-webpack-plugin --dev
webpack.config.js
const {htmlWebpackPlugin} = require('html-webpack-plugin')
module.exports = {
output:{
//publicPath:'dist/' 這里需要注釋掉這個路徑
},
plugins:[
//用于生成index.html文件
new htmlWebpackPlugin({
title:'設置標題',
meta:{
viewport:'width=device-width'
},
template:'./src/index.html',//指定模板文件
}),
// 用于生成about.html文件
new htmlWebpackPlugin({
filename:'about.html'
}),
]
}
再次運行yarn webpack
十六、Webpack 常用插件 使用總結(jié)
yarn add copy-webpack-plugin --dev
webpack.config.js
const {copyWebpackPlugin} = require('copy-webpack-plugin')
module.exports = {
plugins:[
new copyWebpackPlugin([
'plublic'
]),
]
}
再次運行yarn webpack
十七、Webpack 開發(fā)一個插件
相比于Loader,Plugin擁有更寬的能力范圍,plugin通過鉤子機制實現(xiàn)。
webpack 要求必須是一個函數(shù)或者一個包含apply方法的對象
class MyPlugin {
apply(compiler) {
console.log("MyPlugin");
compiler.hook.emit.tap("MyPlugin", (compiler) => {
// compilation 可以理解為此次打包的上下文
for (const name in compilation.assets) {
// console.log(name);
console.log(compilation.assets[name].source());
if (name.endsWith(".js")) {
const contents = compilation.assets[name].source();
const withoutComments = contents.replace(/\/\*\*+\*\//g, "");
compilation.assets[name] = {
source: () => withoutComments,
size: () => widthoutComments.length,
};
}
}
});
}
}
module.exports = {
plugin: [new MyPlugin()],
};
十八、Webpack 自動編譯
yarn webpack --watch
十九、Webpack 自動刷新瀏覽器
browser-sync dist --files "**/*"
二十、Webpack Dev Server
集成【自動編譯】和【自動刷新瀏覽器】等功能
yarn add webpack-dev-server --dev
yarn webpack-dev-server --open 運行并且自動打開瀏覽器
二十一、Webpack Dev Server靜態(tài)資源訪問
contentBase 可以為webpack dev Server 額外的為開發(fā)服務器指定查找資源目錄
webpack.config.js
module.exports = {
devServer:{
contentBase:['./plublic']
}
};
二十二、Webpack Dev Server 代理API
webpack.config.js
module.exports = {
devServer:{
contentBase:['./plublic'],
proxy:{
'/api':{
target:'https://api.github.com',
pathRewrite:{
'^/api':''
},
//不能使用localhost:8080作為請求github的主機名
changeOrigin:true
}
}
}
};
main.js
const ul = ducument.createElement('ul');
document.body.append(ul)
//跨域請求,雖然GitHub支持CORS,但不是每個服務端都應該支持
//fetch('https://api.github.com/users')
fetch('/api/users')
.then(res=>res.json())
.then(data=>{
data.forEach(item=>{
const li = document.createElement('li')
li.textContent = item.login
ul.append(li)
})
})
二十三、Webpack Source Map 配置 (源代碼地圖)
webpack.config.js
module.exports = {
devtool:'source-map'
};
二十四、Webpack eval模式的Source Map
webpack.config.js
module.exports = {
devtool:'eval'
};
可以定位錯誤文件,不能定位具體行列信息
二十五、Webpack devtool 模式對比
- eval 可以定位錯誤文件,不能定位具體行列信息,不生成.map文件
- eval-source-map 可以定位錯誤出現(xiàn)的文件行和列信息,生成.map文件
- cheap-eval-source-map 指定位到行 沒有列的信息,生成.map文件,代碼為轉(zhuǎn)換es6過后的代碼
- cheap-module-eval-source-map 只定位到行 代碼為源代碼
- eval - 是否使用eval執(zhí)行模塊代碼
- cheap - Source Map 是否包含行信息
- module - 是否能夠得到Loader 處理之前的源代碼
二十六、Webpack 選擇Source Map 模式
開發(fā)環(huán)境:cheap-moudule-eval-source-map
- 每行代碼不超過80個字符
- 代碼經(jīng)過Loader 轉(zhuǎn)換后的差異較大
- 首次打包速度慢無所謂,重寫打包相對較快
生產(chǎn)環(huán)境:none
- Source Map 會暴露源代碼
- 調(diào)試是開發(fā)階段的事情
或者nosources-source-map:可以找到源代碼錯誤位置,不至于暴露源碼。
理解不同模式的差異,適配不同的環(huán)境
二十七、Webpack 自動刷新的問題 開啟HMR 模塊熱更新
- 需求:自動刷新導致的頁面狀態(tài)丟失,在頁面不刷新的前提下模塊也可以及時更新
HMR是Webpack中最強大的功能之一,極大程度的提高了開發(fā)者的工作效率。
-Webpack 開啟HMR
HMR集成在webpack-dev-server中,不需要單獨安裝模塊
yarn webpack-dev-server --hot 開啟熱更新
也可以在配置文件中配置開啟熱更新
const webpack = require('webpack')
module.exports ={
devServer:{
hot:true
},
plugins:[
new webpack.HotModuleReplacementPlugin()
]
}
yarn webpack-dev-server --open
二十八、Webpack HMR 的疑問
- webpack 中的HMR并不可以開箱即用
Webpack 中的HMR需要手動處理模塊熱替換邏輯
現(xiàn)有的腳手架工具中已經(jīng)包括了HMR邏輯
二十九、Webpack 使用HMR API
main.js
module.hot.accept('./editor',()=>{
console.log('editor 模塊更新了');
})
三十、Webpack HMR注意事項
- 處理HMR的代碼報錯會導致自動刷新
hotOnly 不會使用自動刷新
devServer:{
hotOnly:true
}
- 沒有啟用HMR的情況下,HMR API報錯
先去判斷
if(module.hot){
...
...
}
- 代碼找那個多了一些與業(yè)務無關的代碼
代碼壓縮后會自動去掉
三十一、Webpack 不同環(huán)境下的配置
- 1、配置文件根據(jù)環(huán)境不同導出不同的配置
const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = (env, argv) => {
const config = {
mode: "development",
entry: "./src/main.js",
output: {
filename: "js/bundle.js",
},
devtool: "cheap-eval-module-source-map",
devServer: {
hot: true,
contentBase: "public",
},
module: {
rulesL: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.(png|jpg?g|gif)$/,
use: {
loader: "file-loader",
options: {
outputPath: "img",
name: "[name].[ext]",
},
},
},
],
},
plugin: [
new HtmlWebpackPlugin({
title: "webpack title",
template: "./src/index.html",
}),
new webpack.HotModuleReplacementPlugin(),
],
};
if (env === "production") {
config.mode = "production";
config.devtool = false;
config.plugins = [
...config.plugins,
new CleanWebpackPlugin(),
new CopyWebpackPlugin(["plublic"]),
];
}
return config
};
// yarn webpack --env production
- 2、一個環(huán)境一個配置文件
三十二、Webpack 不同環(huán)境的配置文件
webpack.common.js 公共的配置
webpack.dev.js 開發(fā)環(huán)境
webpack.prod.js 生產(chǎn)環(huán)境
安裝yarn add webpack-merge --dev
例如webpack.prod.js
const common = require("./webpack.common");
const merge = require("webpack-merge");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = merge(common, {
mode: "production",
plugin: [new CleanWebpackPlugin(), new CopyWebpackPlugin(["plublic"])],
});
yarn webpack --config webpack.prod.js //指定運行文件
也可以將命令構(gòu)建到npm 模塊
package.json -->scripts-->'build':'webpack --config webpack.prod.js'
運行yarn build
三十三、Webpack DefinePlugin
為我們的代碼注入全局成員 process.env.NODE_ENV
const webpack = require("webpack");
module.exports = {
mode: "none",
entry: "./src/main.js",
output: {
filename: "bundle.js",
},
plugins: [
new webpack.DefinePlugin({
API_BASE_URL: JSON.stringify("https://api.example.com"),
}),
],
};
三十四、Webpack 使用Tree Shaking
Tree Shaking 不是指某個配置選項,是一組功能搭配使用后的優(yōu)化效果,會在生產(chǎn)模式下(production)自動啟用
module.exports = {
optimization: {
usedExports: true, //用來標記冗余代碼
minimize:true, 用于清除標記的冗余代碼
},
}
yarn webpack
三十五、Webpack 合并模塊 concatenateModules
concatenateModules :盡可能的將所有的模塊合并輸出到一個函數(shù)中,即提升了運行效率,又減少了代碼的體積。Scope Hoisting 作用域提升
三十六、Webpack Tree Shaking 與Babel
Tree Shaking 前提是ES Modules,由Webpack打包的代碼必須使用ESM,為了轉(zhuǎn)換代碼中的ECMAScript新特性,會選擇babel-loader處理js,ES Modules可能會轉(zhuǎn)換成CommonJS,
在最新版本的babel-loader中自動幫我們關閉了ES M的轉(zhuǎn)換插件。
三十七、Webpack sideEffects
標識代碼是否有副作用,為Tree Shaking 提供更大的空間
副作用:模塊執(zhí)行時除了導出成員之外所做的事情
sideEffects 一般用于npm 包標記是否有副作用
optimization: {
//開啟功能
sideEffects:true,
// 模塊指導處被使用的成員
// usedExports: true,
// 盡可能合并每一個模塊到一個函數(shù)中
// concatenateModules:true,
// 壓縮輸出結(jié)果
// minimize:true
},
package.json
"sideEffects":false, //標記沒有副作用
yarn webpack
三十八、Webpack sideEffects 注意
確保代碼沒有副作用
例如為Number的原型添加一個擴展方法 就是副作用的代碼
Number.prototype.pad = function(size){
//將數(shù)字轉(zhuǎn)換為字符 1 2
let result = this + ''
//在數(shù)字前補指定個數(shù)的0 001 002
while(result.length < size ){
result = '0' + result
}
return result
}
在代碼中載入的css 模塊也屬于副作用代碼,解決辦法 在package.json 中關掉副作用,或者標記當前文件那些是有副作用的,
package.json
有副作用的文件
"sideEffects":[
'./src/extend.js',
'*.css'
]
yarn webpack
三十九、Webpack 代碼分割 Code Splitting
代碼分割的兩種而方式
- 一、Webpack 多入口打包
const webpack = require("webpack");
const {CleanWebpackPlugin} = require('Clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: "none",
// entry: "./src/index.js",
entry:{//多入口
index:'./src/index.js',
main:'./src/main.js'
},
output: {//多輸出
filename: "[name].bundle.js",
},
module:{
rules:[
{
test:'/\.css$/',
use:[
'style-loader',
'css-loader'
]
}
]
},
optimization: {
// sideEffects:true,
// 模塊指導處被使用的成員
// usedExports: true,
// 盡可能合并每一個模塊到一個函數(shù)中
// concatenateModules:true,
// 壓縮輸出結(jié)果
// minimize:true
},
plugins: [
new webpack.DefinePlugin({
API_BASE_URL: JSON.stringify("https://api.example.com"),
}),
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title:'indexhtml',
template:'./src/index.html',
filename:'index.html',
chunks:['index'],//指定需要引入的bundle
}),
new HtmlWebpackPlugin({
title:'main.html',
template:'./src/main.html',
filename:'main.html',
chunks:['main']//指定需要引入的bundle
})
],
};
Webpack 提取公共模塊
optimization: {
splitChunks:{
chunks:'all',
}
}
- 二、Webpack 動態(tài)導入
// import post from './post/post'
// import album from './album/album'
const render = () => {
const hash = window.location.hash || "#posts";
const mainElement = document.querySelector("main");
mainElement.innerHTML = "";
if (hash === "#posts") {
import("./posts/posts").then(({ default: posts }) => {
mainElement.appendChild(posts());
});
} else if (hash === "#album") {
import("./album/album").then(({ default: posts }) => {
mainElement.appendChild(album());
});
}
};
render();
window.addEventListener("hashchange", render);
四十、Webpack 魔法注釋Magic Comments
if (hash === "#posts") {
import(/*webpackChunkName:'posts'*/"./posts/posts").then(({ default: posts }) => {
mainElement.appendChild(posts());
});
} else if (hash === "#album") {
import((/*webpackChunkName:'album'*/"./album/album").then(({ default: posts }) => {
mainElement.appendChild(album());
});
}
四十一、Webpack MiniCssExtractPlugin 提取CSS到單文件
yarn add mini-css-extract-plugin --dev
四十二、Webpack OptimizeCssAssetsWebpackPlugin 壓縮CSS插件
yarn add optimize-css-assets-webpack-plugin --dev
yarn add terser-webpack-plugin --dev
webpack.config.js
optimization: {
minimizer: [
new TerserWebpackPlugin(),
new OptimizeCssAssetsWebpackPlugin(),
],
}
四十三、Webpack 輸出文件名Hash
output: {
//多輸出
filename: "[name]-[contenthash:8].bundle.js",
path: path.resolve(__dirname, "./dist"),
},
可以指定位數(shù),當前為8 不指定不寫 :8