一拢军、簡(jiǎn)介
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)容對(duì)文件進(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)建失敗空执。本地安裝可以使我們更容易對(duì)不同的項(xiàng)目進(jìn)行分別升級(jí)。通常穗椅,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" }
};
單頁(yè)面配置:
module.exports = {
entry: './path/to/my/entry/file.js'
};
多頁(yè)面配置:
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需要為絕對(duì)路徑
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)瓢棒。簡(jiǎn)單來說,loader 就是一個(gè)翻譯官丘喻,將瀏覽器不認(rèn)識(shí)的語(yǔ)言脯宿,轉(zhuǎn)化為瀏覽器能解析的語(yǔ)言,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文件中,對(duì)一個(gè)單獨(dú)的 module 對(duì)象定義了 rules 屬性哈误,里面包含兩個(gè)必須屬性:test 和 use哩至。這告訴 webpack 編譯器(compiler) 如下信息:當(dāng)編譯器碰到「在 require()/import 語(yǔ)句中被解析為 ‘.sass/.scss’ 的路徑」時(shí)躏嚎,在對(duì)它打包之前,先使用 sass-loader憨募、css-loader紧索、style-loader 轉(zhuǎn)換一下。最終.scss文件會(huì)被轉(zhuǎn)成內(nèi)聯(lián)樣式菜谣,插入html頁(yè)面中珠漂。
- 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è)面應(yīng)用,則每個(gè)頁(yè)面都需要有對(duì)應(yīng)的new HtmlWebpackPlugin()滩届。webpack在編譯的時(shí)候集侯,會(huì)把所有的 js 合并輸出在一個(gè)js文件,而設(shè)置 chunks 屬性的目的帜消,就是讓當(dāng)前頁(yè)面模板只加載當(dāng)前頁(yè)面所需要的模塊棠枉。上面的寫法表示,在編譯./src/views/index.html 時(shí)泡挺,只需要加載main.js文件辈讶。
四、webpack配置
- mode 模式
mode 配置選項(xiàng)娄猫,告知 webpack 使用相應(yīng)模式的內(nèi)置優(yōu)化荞估。有兩個(gè)值:development(開發(fā)模式不會(huì)對(duì)代碼做壓縮、優(yōu)化稚新,方便代碼調(diào)試勘伺,會(huì)將 process.env.NODE_ENV 的值設(shè)為 development)和production(生產(chǎn)模式下會(huì)對(duì)代碼做壓縮、優(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)刷新瀏覽器彼宠。可以使用webpack-dev-server插件弟塞。直接使用webpack執(zhí)行凭峡,會(huì)對(duì)文件進(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)勢(shì)
與其他的構(gòu)建工具相比往枷,webpack具有如下的一些優(yōu)勢(shì):
- 對(duì) CommonJS 、 AMD 凄杯、ES6 的語(yǔ)法做了兼容
- 對(duì) js错洁、css、圖片等資源文件都支持打包
- 串聯(lián)式模塊加載器以及插件機(jī)制戒突,讓其具有更好的靈活性和擴(kuò)展性屯碴,例如提供對(duì) CoffeeScript、ES6的支持
- 有獨(dú)立的配置文件 webpack.config.js
- 可以將代碼切割成不同的 chunk膊存,實(shí)現(xiàn)按需加載导而,降低了初始化時(shí)間
- 支持 SourceUrls 和 SourceMaps,易于調(diào)試
- 具有強(qiáng)大的 Plugin 接口隔崎,大多是內(nèi)部插件今艺,使用起來比較靈活
- webpack 使用異步 IO 并具有多級(jí)緩存,這使得 webpack 很快且在增量編譯上更加快