一、簡介
webpack 是一個(gè)現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(module bundler)。主要是用來將前端資源打包、壓縮、優(yōu)化。
- webpack 打包原理:
當(dāng) webpack 處理應(yīng)用程序時(shí),不會(huì)根據(jù) package.json 文件中的devDependencies 或者 Dependencies 的內(nèi)容對文件進(jìn)行打包。而是會(huì)根據(jù)入口文件做查詢,加載其所依賴的包模塊,它會(huì)遞歸地構(gòu)建一個(gè)依賴關(guān)系圖(dependency graph),其中包含應(yīng)用程序需要的每個(gè)模塊,然后將所有這些模塊打包成一個(gè)或多個(gè) bundle。 - 兩個(gè)最核心的概念:
1.一切皆模塊
正如js文件可以是一個(gè)模塊(module)一樣,其他的(如css、image或html)文件也可視作模塊。因此,你可以require(‘myJSfile.js’)也可以require(‘myCSSfile.css’)。這意味著我們可以業(yè)務(wù)分割成更小的易于管理的片段,從而達(dá)到重復(fù)利用的目的。
2.按需加載
傳統(tǒng)的模塊打包工具(module bundlers)最終將所有的模塊編譯生成一個(gè)龐大的bundle.js文件。但是在真實(shí)的app里邊,bundle.js文件可能有10M到15M之大,可能會(huì)導(dǎo)致應(yīng)用一直處于加載中狀態(tài)。因此Webpack使用許多特性來分割代碼然后生成多個(gè)bundle文件,而且異步加載部分代碼以實(shí)現(xiàn)按需加載。
二、安裝
webpack支持使用npm 或者 yarn 方式進(jìn)行安裝。不推薦全局安裝 webpack,這會(huì)將你項(xiàng)目中的 webpack 鎖定到指定版本,并且在使用不同的 webpack 版本的項(xiàng)目中,可能會(huì)導(dǎo)致構(gòu)建失敗。本地安裝可以使我們更容易對不同的項(xiàng)目進(jìn)行分別升級。通常,webpack 通過運(yùn)行一個(gè)或多個(gè) npm scripts,會(huì)在本地 node_modules 目錄中查找安裝的 webpack:
"scripts": {
"start": "webpack"
}
如果使用 webpack 4+ 版本,還需要安裝 CLI。webpack-cli 可以允許直接在控制臺(tái)操作webpack命令。終端中輸入 webpack --help,可以查看所有的幫助信息。
使用npm安裝:
npm install webpack webpack-cli --save-dev
或者使用yarn安裝:
yarn add webpack webpack-cli -D
三、webpack基本概念
以以下目錄結(jié)構(gòu)為例來了解以下webpack的基本概念:

- 入口 entry:在webpack.config.js配置文件中,通過設(shè)置entry字段的值,指定一個(gè)入口起點(diǎn)(或多個(gè)入口起點(diǎn)),不配置的話,默認(rèn)入口起點(diǎn)為src目錄下的index.js模塊。
默認(rèn)值為:
module.exports = {
entry: './src'
//相當(dāng)于
entry:{ main:"/src/index.js" }
};
單頁面配置:
module.exports = {
entry: './path/to/my/entry/file.js'
};
多頁面配置:
module.exports = {
entry: {
'main': "./src/js/index.js",
'detail': "./src/js/detai.js"
}
};
- 輸出output :output 屬性告訴 webpack 在哪里輸出它所創(chuàng)建的 bundles,以及如何命名這些文件,默認(rèn)值為 ./dist?;旧?,整個(gè)應(yīng)用程序結(jié)構(gòu),都會(huì)被編譯到指定的輸出路徑的文件夾中??梢酝ㄟ^在配置中指定一個(gè) output 字段,來配置這些處理過程。
默認(rèn)值為:
const path = require('path');
module.exports = {
output: {
//path需要為絕對路徑
path: path.resolve(__dirname, './dist'),
filename: 'main.js'
}
};
自定義配置:output 的 filename屬性中,使用[name]來表示entry中的key;使用[hash],會(huì)自動(dòng)在文件名稱后面添加hash值,并直接關(guān)聯(lián)到html模板文件中。
const path = require('path');
module.exports = {
entry: {
'main': "./src/js/index.js",
'detail': "./src/js/detai.js"
},
output: {
path: path.resolve(__dirname, './dev'),
filename: '[name]-[hash].js'
},
};
- loader:loader 讓 webpack 能夠去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。簡單來說,loader 就是一個(gè)翻譯官,將瀏覽器不認(rèn)識(shí)的語言,轉(zhuǎn)化為瀏覽器能解析的語言,loader使用之前需要先安裝。loader 能夠 import 導(dǎo)入任何類型的模塊(例如 .scss 文件),這是 webpack 特有的功能,其他打包程序或任務(wù)執(zhí)行器的可能并不支持。
webpack.config.js文件:
module.exports = {
module: {
rules: [
{
test: /\.s[ac]ss$/,
//當(dāng) use 里有多個(gè)loader時(shí),執(zhí)行的順序?yàn)閺挠蚁蜃? use: [
// 將 JS 字符串生成為 style 節(jié)點(diǎn),插入html中,生成內(nèi)聯(lián)樣式
'style-loader',
//將 CSS 轉(zhuǎn)化成 CommonJS 模塊,可以直接使用import/require導(dǎo)入
'css-loader',
//把.scss文件文件轉(zhuǎn)換為.css文件
'sass-loader'
]
}
]
}
};
在webpack.config.js文件中,對一個(gè)單獨(dú)的 module 對象定義了 rules 屬性,里面包含兩個(gè)必須屬性:test 和 use。這告訴 webpack 編譯器(compiler) 如下信息:當(dāng)編譯器碰到「在 require()/import 語句中被解析為 ‘.sass/.scss’ 的路徑」時(shí),在對它打包之前,先使用 sass-loader、css-loader、style-loader 轉(zhuǎn)換一下。最終.scss文件會(huì)被轉(zhuǎn)成內(nèi)聯(lián)樣式,插入html頁面中。
- plugins 插件:loader 被用于轉(zhuǎn)換某些類型的模塊,而插件則可以用于執(zhí)行范圍更廣的任務(wù),插件的范圍包括,從打包優(yōu)化和壓縮,一直到重新定義環(huán)境中的變量。想要使用一個(gè)插件,只需要 require() 它,然后把它添加到 plugins 數(shù)組中。多數(shù)插件可以通過選項(xiàng)(option)自定義,也可以在一個(gè)配置文件中因?yàn)椴煌康亩啻问褂猛粋€(gè)插件,這時(shí)需要通過使用 new 操作符來創(chuàng)建它的一個(gè)實(shí)例。
webpack.config.js文件:
//html-webpack-plugin 將html模板文件和js文件整合到一起
const htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins: [
new HtmlWebpackPlugin({
//模板文件路徑
template: "./src/views/index.html",
//自動(dòng)存儲(chǔ)到output 配置的目錄
filename: 'index.html',
chunks: ['main']
})
]
};
html-webpack-plugin 插件的作用是將html模板文件和js文件整合到一起,如果是多頁面應(yīng)用,則每個(gè)頁面都需要有對應(yīng)的new HtmlWebpackPlugin()。webpack在編譯的時(shí)候,會(huì)把所有的 js 合并輸出在一個(gè)js文件,而設(shè)置 chunks 屬性的目的,就是讓當(dāng)前頁面模板只加載當(dāng)前頁面所需要的模塊。上面的寫法表示,在編譯./src/views/index.html 時(shí),只需要加載main.js文件。
四、webpack配置
- mode 模式
mode 配置選項(xiàng),告知 webpack 使用相應(yīng)模式的內(nèi)置優(yōu)化。有兩個(gè)值:development(開發(fā)模式不會(huì)對代碼做壓縮、優(yōu)化,方便代碼調(diào)試,會(huì)將 process.env.NODE_ENV 的值設(shè)為 development)和production(生產(chǎn)模式下會(huì)對代碼做壓縮、優(yōu)化,會(huì)將 process.env.NODE_ENV 的值設(shè)為 production)。如果不配置mode,會(huì)有警告信息,但是不影響項(xiàng)目運(yùn)行。
module.exports = {
mode:"development",
};
- devtool
開發(fā)模式通常配合devtool使用,可以在瀏覽器的控制臺(tái)看到源代碼,而不是解析后的代碼。
module.exports = {
//代碼調(diào)試,可以看到源代碼
devtool: 'inline-source-map',
};
- webpack-dev-server
在開發(fā)模式下,新建一個(gè)開發(fā)服務(wù)器,并且當(dāng)代碼更新的時(shí)候自動(dòng)刷新瀏覽器??梢允褂脀ebpack-dev-server插件。直接使用webpack執(zhí)行,會(huì)對文件進(jìn)行打包,并輸出在指定目錄下,默認(rèn)為./dev下。使用webpack-dev-server執(zhí)行,文件會(huì)放在內(nèi)存里,不會(huì)生成整合后的文件,也不會(huì)將文件輸出到指定目錄里。
在webpack.config.js文件中配置
module.exports = {
devServer:{
contentBase:"./dist", //設(shè)置Http服務(wù)器的文件根目錄
port:8088, //端口
open:true, //是否打開瀏覽器
proxy: { //配置代理
"/api": {
target: "http://localhost:3000",
pathRewrite: {"^/api" : ""}
}
}
}
};
- webpack.config.js
一個(gè)開發(fā)模式下,完整的webpack.config.js文件內(nèi)容如下:
const path=require("path");
//將html模板和js文件整合在一起
const htmlWebpackPlugin=require("html-webpack-plugin");
//將css樣式提取出來,使用Link標(biāo)簽導(dǎo)入
const miniCssExtractPlugin=require("mini-css-extract-plugin");
//將靜態(tài)資源打包到輸出目錄中
const copyWebpackPlugin=require("copy-webpack-plugin");
module.exports={
mode:"development",
devtool:"inline-source-map",
entry:"./src/js/index.js",
output:{
path:path.resolve(__dirname+"./dev"),
filename:"[name]-[hash].js"
},
devServer:{
port:8088,
open:true,
proxy: { //配置代理
"/api": {
target: "http://localhost:3000",
pathRewrite: {"^/api" : ""}
}
}
},
plugins:[
new htmlWebpackPlugin({
title:"demo title",
template:"./src/views/index.html",
filename:"index.html",
chunks:["main"]
}),
new miniCssExtractPlugin({
filename:"[name]-[hash].css"
}),
new copyWebpackPlugin([
{from:"./src/static",to:"./static"}
])
],
module:{
rules:[
{
test:/\.s[ac]ss$/,
use:[
miniCssExtractPlugin.loader,
"css-loader",
"sass-loader"
]
},
{
test:/\.html$/,
use:["string-loader"]
}
]
}
}
package.json文件內(nèi)容如下:
{
"name": "admin-fe",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "webpack-dev-server --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"handlebars": "^4.7.3",
"sme-router": "^0.12.8"
},
"devDependencies": {
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.4.2",
"html-webpack-plugin": "^4.0.1",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.1",
"sass-loader": "^8.0.2",
"string-loader": "^0.0.1",
"style-loader": "^1.1.3",
"webpack": "^4.42.1",
"webpack-cli": "^3.3.11",
"webpack-dev-server": "^3.10.3"
}
}
五、webpack優(yōu)勢
與其他的構(gòu)建工具相比,webpack具有如下的一些優(yōu)勢:
- 對 CommonJS 、 AMD 、ES6 的語法做了兼容
- 對 js、css、圖片等資源文件都支持打包
- 串聯(lián)式模塊加載器以及插件機(jī)制,讓其具有更好的靈活性和擴(kuò)展性,例如提供對 CoffeeScript、ES6的支持
- 有獨(dú)立的配置文件 webpack.config.js
- 可以將代碼切割成不同的 chunk,實(shí)現(xiàn)按需加載,降低了初始化時(shí)間
- 支持 SourceUrls 和 SourceMaps,易于調(diào)試
- 具有強(qiáng)大的 Plugin 接口,大多是內(nèi)部插件,使用起來比較靈活
- webpack 使用異步 IO 并具有多級緩存,這使得 webpack 很快且在增量編譯上更加快