webpack打包
?目錄總覽:

問題的產(chǎn)生
瀏覽器環(huán)境中模塊化開發(fā)的問題:
- 效率問題:模塊化導(dǎo)致JS代碼需要不斷的被細(xì)分, 而精細(xì)的模塊劃分帶來了更多的JS文件, 更多的JS文件帶來了更多的網(wǎng)絡(luò)請求, 降低了頁面訪問效率
-
兼容性問題:
- 瀏覽器不識別ES6(或者更高版本的JS語法)語法,或者CSS預(yù)編譯處理器(如less,scss)
- 目前瀏覽器僅支持ES6的模塊化, 那么意味著我們不能在代碼中書寫commonjs規(guī)范的代碼, 更意味著我們在瀏覽器更加不能用commonjs導(dǎo)出的庫和包, 我們知道, 學(xué)習(xí)了npm, yarn這類包管理工具以后, 我們不會再用很low的方式去引入庫或者包, 都會用npm install的方式。
- 使用工具的兼容問題:在默認(rèn)情況下, 瀏覽器跟npm的結(jié)合問題非常大, 要解決這個問題, 必須使用第三方工具。
-
代碼環(huán)境問題:瀏覽器端, 開發(fā)環(huán)境(development)和線上環(huán)境(production)的側(cè)重點(diǎn)完全不同:
- 開發(fā)環(huán)境
- 模塊劃分越細(xì)越好
- 最好支持多種模塊化標(biāo)準(zhǔn)
- 支持npm和其他包管理器下載的模塊
- 能解決其他工程化的問題
- 線上環(huán)境(代碼上線后)
- 文件數(shù)量越少越好
- 文件體積越小越好
- 代碼內(nèi)容越亂越好
- 所有瀏覽器都要兼容
- 執(zhí)行效率越高越好
- 開發(fā)環(huán)境
問題的解決
什么是構(gòu)建工具?
能夠讓開發(fā)者專心的書寫開發(fā)環(huán)境的代碼, 然后利用這個工具將開發(fā)時態(tài)編寫的代碼轉(zhuǎn)化為運(yùn)行時態(tài)的工具。
常見的構(gòu)建工具:webpack、grunt、gulp、browserfly等等。
其中webpack是目前生態(tài)最繁榮構(gòu)建工具。
所有資源文件(js/json/css/img/less/…)都會被webpack作為模塊處理。

一、webpack概述
- webpack 是一個流行的前端項(xiàng)目構(gòu)建工具(打包工具),可以解決當(dāng)前 web 開發(fā)中所面臨的困境。
- webpack 提供了友好的模塊化支持,以及代碼壓縮混淆、處理 js 兼容問題、性能優(yōu)化等強(qiáng)大的功能,從而讓程序員把工作的重心放到具體的功能實(shí)現(xiàn)上,提高了開發(fā)效率和項(xiàng)目的可維護(hù)性。
-
五個核心概念:入口(Entry)、輸出(Output)、Loader(加載器)、Plugins(插件)、Mode(模式)
二、webpack 的基本使用
2.1 在項(xiàng)目中安裝和配置 webpack
① 運(yùn)行 npm install webpack webpack-cli –D 命令,安裝 webpack 相關(guān)的包
② 在項(xiàng)目根目錄中,創(chuàng)建名為 webpack.config.js 的 webpack 配置文件
③ 在 webpack 的配置文件中,初始化如下基本配置:
module.exports = {
mode: 'development' // mode 用來指定構(gòu)建模式
}
④ 在 package.json 配置文件中的 scripts 節(jié)點(diǎn)下,新增 dev 腳本如下:
"scripts": {
"dev": "webpack" // script 節(jié)點(diǎn)下的腳本,可以通過 npm run 執(zhí)行
}
⑤ 在終端中運(yùn)行 npm run dev 命令,啟動 webpack 進(jìn)行項(xiàng)目打包。
2.2 配置打包的入口與出口
webpack 的 4.x 版本中默認(rèn)約定:
- 打包的入口文件為 src -> index.js
- 打包的輸出文件為 dist -> main.js
如果要修改打包的入口與出口,可以在 webpack.config.js 中新增如下配置信息:
const path = require('path') // 導(dǎo)入 node.js 中專門操作路徑的模塊
module.exports = {
entry: path.join(__dirname, './src/index.js'), // 打包入口文件的路徑
output: {
path: path.join(__dirname, './dist'), // 輸出文件的存放路徑
filename: 'bundle.js' // 輸出文件的名稱
}
}
2.3 配置 webpack 的自動打包功能
① 運(yùn)行 npm install webpack-dev-server –D 命令,安裝支持項(xiàng)目自動打包的工具
② 修改 package.json -> scripts 中的 dev 命令如下:
"scripts": {
"dev": "webpack-dev-server" // script 節(jié)點(diǎn)下的腳本,可以通過 npm run 執(zhí)行
}
③ 將 src -> index.html 中,script 腳本的引用路徑,修改為 "/buldle.js“
④ 運(yùn)行 npm run dev 命令,重新進(jìn)行打包
⑤ 在瀏覽器中訪問 http://localhost:8080 地址,查看自動打包效果
注意:
- webpack-dev-server 會啟動一個實(shí)時打包的 http 服務(wù)器
- webpack-dev-server 打包生成的輸出文件,默認(rèn)放到了項(xiàng)目根目錄中,而且是虛擬的、看不見的
2.4 配置 html-webpack-plugin 生成預(yù)覽頁面
① 運(yùn)行 npm install html-webpack-plugin –D 命令,安裝生成預(yù)覽頁面的插件
② 修改 webpack.config.js 文件頭部區(qū)域,添加如下配置信息:
// 導(dǎo)入生成預(yù)覽頁面的插件,得到一個構(gòu)造函數(shù)
const HtmlWebpackPlugin = require('html-webpack-plugin')
const htmlPlugin = new HtmlWebpackPlugin({ // 創(chuàng)建插件的實(shí)例對象
template: './src/index.html', // 指定要用到的模板文件
filename: 'index.html' // 指定生成的文件的名稱,該文件存在于內(nèi)存中,在目錄中不顯示
})
③ 修改 webpack.config.js 文件中向外暴露的配置對象,新增如下配置節(jié)點(diǎn):
module.exports = {
plugins: [ htmlPlugin ] // plugins 數(shù)組是 webpack 打包期間會用到的一些插件列表
}
2.5 配置自動打包相關(guān)的參數(shù)
// package.json中的配置
// --open 打包完成后自動打開瀏覽器頁面
// --host 配置 IP 地址
// --port 配置端口
"scripts": {
"dev": "webpack-dev-server --open --host 127.0.0.1 --port 8888"
},
三、 webpack 中的加載器(loader)
1. 通過 loader 打包非 js 模塊
在實(shí)際開發(fā)過程中,webpack 默認(rèn)只能打包處理以 .js 后綴名結(jié)尾的模塊,其他非 .js 后綴名結(jié)尾的模塊,webpack 默認(rèn)處理不了,需要調(diào)用 loader 加載器才可以正常打包,否則會報錯!
loader 加載器可以協(xié)助 webpack 打包處理特定的文件模塊,比如:
- less-loader 可以打包處理 .less 相關(guān)的文件
- sass-loader 可以打包處理 .scss 相關(guān)的文件
- url-loader 可以打包處理 css 中與 url 路徑相關(guān)的文件
2. loader 的調(diào)用過程

四、webpack 中加載器的基本使用
1. 打包處理 css 文件
① 運(yùn)行 npm i style-loader css-loader -D 命令,安裝處理 css 文件的
② 在 webpack.config.js 的 module -> rules 數(shù)組中,添加 loader 規(guī)則如下:
// 所有第三方文件模塊的匹配規(guī)則
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }
]
}
其中,test 表示匹配的文件類型, use 表示對應(yīng)要調(diào)用的 loader
注意:
- use 數(shù)組中指定的 loader 順序是固定的
- 多個 loader 的調(diào)用順序是:從后往前調(diào)用
2. 打包處理 less 文件
① 運(yùn)行 npm i less-loader less -D 命令
② 在 webpack.config.js 的 module -> rules 數(shù)組中,添加 loader 規(guī)則如下:
// 所有第三方文件模塊的匹配規(guī)則
module: {
rules: [
{ test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }
]
}
3. 打包處理 scss 文件
① 運(yùn)行 npm i sass-loader node-sass -D 命令
② 在 webpack.config.js 的 module -> rules 數(shù)組中,添加 loader 規(guī)則如下:
// 所有第三方文件模塊的匹配規(guī)則
module: {
rules: [
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }
]
}
4. 配置 postCSS 自動添加 css 的兼容前綴
① 運(yùn)行 npm i postcss-loader autoprefixer -D 命令
② 在項(xiàng)目根目錄中創(chuàng)建 postcss 的配置文件 postcss.config.js,并初始化如下配置:
const autoprefixer = require('autoprefixer') // 導(dǎo)入自動添加前綴的插件
module.exports = {
plugins: [ autoprefixer ] // 掛載插件
}
③ 在 webpack.config.js 的 module -> rules 數(shù)組中,修改 css 的 loader 規(guī)則如下:
module: {
rules: [
{ test:/\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader'] }
]
}
5. 打包樣式表中的圖片和字體文件
① 運(yùn)行 npm i url-loader file-loader -D 命令
② 在 webpack.config.js 的 module -> rules 數(shù)組中,添加 loader 規(guī)則如下:
module: {
rules: [
{
test: /\.jpg|png|gif|bmp|ttf|eot|svg|woff|woff2$/,
use: 'url-loader?limit=16940'
}
]
}
其中 ? 之后的是 loader 的參數(shù)項(xiàng)。
limit 用來指定圖片的大小,單位是字節(jié)(byte),只有小于 limit 大小的圖片,才會被轉(zhuǎn)為 base64 圖片
6. 打包處理 js 文件中的高級語法(babel)
① 安裝babel轉(zhuǎn)換器相關(guān)的包:npm i babel-loader @babel/core @babel/runtime -D
② 安裝babel語法插件相關(guān)的包:
npm i @babel/preset-env @babel/plugin-transform
runtime @babel/plugin-proposal-class-properties –D
③ 在項(xiàng)目根目錄中,創(chuàng)建 babel 配置文件 babel.config.js 并初始化基本配置如下:
module.exports = {
presets: [ '@babel/preset-env' ],
plugins: [ '@babel/plugin-transform-runtime', '@babel/plugin-proposal?class-properties’ ]
}
④ 在 webpack.config.js 的 module -> rules 數(shù)組中,添加 loader 規(guī)則如下:
// exclude 為排除項(xiàng),表示 babel-loader 不需要處理 node_modules 中的 js 文件
{ test: /\.js$/, use: 'babel-loader', exclude: /node_modules/ }
五、打包發(fā)布
項(xiàng)目開發(fā)完成之后 ,需要使用 webpack 對項(xiàng)目進(jìn)行打包發(fā)布 ,主要原因有以下兩點(diǎn):
- 開發(fā)環(huán)境下,打包生成的文件存放于內(nèi)存中 ,無法獲取到最終打包生成的文件
- 開發(fā)環(huán)境下,打包生成的文件 不會進(jìn)行代碼壓縮和性能優(yōu)化
為了讓項(xiàng)目能夠在生產(chǎn)環(huán)境中高性能的運(yùn)行,因此需要對項(xiàng)目進(jìn)行打包發(fā)布。
5.1、配置webpack的打包發(fā)布
在 package.json 文件的 scripts 節(jié)點(diǎn)下,新增 build 命令如下
{
...
"script": {
"serve": "vue-cli-service serve" // 開發(fā)環(huán)境中,運(yùn)行dev命令
"build": "vue-cli-service build" // 項(xiàng)目發(fā)布時,運(yùn)行 build 命令
}
}
-- model 是一個參數(shù)項(xiàng),用來指定 webpack 的運(yùn)行模式 。production 代表生產(chǎn)環(huán)境,會對打包生成的文件
進(jìn)行代碼壓縮和性能優(yōu)化 。
注意:通過 -- model 指定的參數(shù)項(xiàng),會覆蓋 webpack.config.js 中的 model 選項(xiàng)
5.2、把JavaScript文件統(tǒng)一生成到j(luò)s目錄中
在 webpack.config.js 配置文件的 output 節(jié)點(diǎn)中,進(jìn)行如下的配置
const path = require('path')
module.exports = {
// 入口:可以是字符串/數(shù)組/對象,我們這里的入口只有一個,所以寫一個字符串即可
entry: path.join(__dirname,'./src/index.js')
// 出口:通常是一個對象,里面至少包含兩個重要屬性,path 和 filename
output :{
// 注意: path通常是一個絕對路徑
path: path.resolve(__dirname,'dist'),
// 明確告訴 webpack 把生成的 bundle.js 文件存放到 dist 目錄下的 js 子目錄中
filename: 'js/bundle.js'
}
}
5.3、把圖片文件統(tǒng)一生成到 image 目錄中
修改 webpack.config.js 中的 url-loader 配置項(xiàng),新增 outputPath 選項(xiàng)即可指定圖片文件的輸出路徑
{
test: /\.jpg|png|gif$/,
use: {
loader: 'url-loader',
options: {
limit: 22228,
// 明確指定把打包生成的圖片文件,存儲到 dist 目錄下的 image 文件夾中
outputPath: 'image',
}
}
}
5.4、自動清理 dist 目錄下的舊文件
為了在每次打包發(fā)布時自動清理掉 dist 目錄中的舊文件 ,可以安裝并配置 clean-webpack-plugin 插件
- 安裝插件
npm install clean-webpack-plugin --save-dev
- 按需導(dǎo)入插件,得到插件的構(gòu)造函數(shù)后,創(chuàng)建插件的實(shí)例對象
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
const cleanPlugin = new CleanWebpackPlugin()
- 把創(chuàng)建的 cleanPlugin 插件實(shí)例對象掛載到 plugins 節(jié)點(diǎn)中
// 1.導(dǎo)入清理插件插件,得到插件的構(gòu)造函數(shù)后,創(chuàng)建插件的實(shí)例對象
const {CleanWebpackPlugin} = require('clean-webpack-plugin')
// 2.創(chuàng)建HTML插件的實(shí)例對象
const cleanPlugin = new CleanWebpackPlugin()
// 3.把創(chuàng)建的 cleanPlugin 插件實(shí)例對象,掛載到 plugins 節(jié)點(diǎn)中,使插件生效
module.exports = {
mode: 'development',
plugins: [htmlPlugin,cleanPlugin], // 掛載插件
}
六、Source Map
6.1、生產(chǎn)環(huán)境遇到的問題
前端項(xiàng)目在投入生產(chǎn)環(huán)境之前,都需要對JavaScript 源代碼進(jìn)行 壓縮混淆 ,從而減小文件的體積,提高文件的加載效率。此時就不可避免的產(chǎn)生了另一個問題:
- 對壓縮混淆之后的代碼除錯(debug)是一件極其困難的事情
- 變量被替換成 沒有任何語義 的名稱
- 空行和注釋被剔除
6.2、什么是SourceMap
- Source Map就是一個信息文件,里面儲存著位置信息
- 也就是說, Source Map 文件中存儲著壓縮混淆后的代碼所對應(yīng)的轉(zhuǎn)換前的位置 。
- 有了它,出錯的時候,除錯工具將直接顯示原始代碼 ,而不是轉(zhuǎn)換后的代碼 ,能夠極大的方便后期的調(diào)試。
6.2.1、webpack 開發(fā)環(huán)境下的 Source Map
在開發(fā)環(huán)境下,webpack 默認(rèn)啟用了 Source Map 功能。當(dāng)程序運(yùn)行出錯時,可以直接在控制臺提示錯誤行的位置,并定位到具體的源代碼。

6.2.2、默認(rèn)Source Map的問題
開發(fā)環(huán)境下默認(rèn)生成的 SourceMap ,記錄的是 生成后的代碼的位置 。會導(dǎo)致運(yùn)行時報錯的行數(shù)與源代碼的行數(shù)不一致的問題。示意圖如下:

6.2.3、解決默認(rèn)Source Map的問題
開發(fā)環(huán)境下,推薦在 webpack.config.js 中添加如下的配置,即可保證運(yùn)行時報錯的行數(shù)與源代碼的行數(shù)保持一致
module.exports = {
mode: 'development',
// eval-source-map 僅限在'開發(fā)模式'下使用,不建議在 '生產(chǎn)模式'下使用。
// 此選項(xiàng)生產(chǎn)的 Source Map 能夠保證運(yùn)行時報錯的行數(shù)與源代碼的行數(shù)保持一致
devtool: 'eval-source-map',
}
6.3、webpack生產(chǎn)環(huán)境下的SourceMap
在生產(chǎn)環(huán)境下 ,如果省略了 devtool 選項(xiàng) ,則最終生成的文件中不包含 Source Map 。這能夠防止原始代碼通過Source Map 的形式暴露給別有所圖之人。

6.3.1、只定位行數(shù)不暴露源碼
在生產(chǎn)環(huán)境下,如果 只想定位報錯的具體行數(shù) ,且不想暴露源碼 。此時可以將 devtool 的值設(shè)置為nosources-source-map 。實(shí)際效果如圖所示:

6.3.2、定位行數(shù)且暴露源碼
在生產(chǎn)環(huán)境下,如果想在定位報錯行數(shù)的同時,展示具體報錯的源碼。此時可以將 devtool 的值設(shè)置為 source-map。實(shí)際效果如圖所示:

6.4、Source Map最佳實(shí)踐
- 開發(fā)環(huán)境下
- 建議把 devtool 的值設(shè)置為
eval-source-map - 好處:可以精準(zhǔn)定位到具體的錯誤行
- 建議把 devtool 的值設(shè)置為
- 生產(chǎn)環(huán)境下
- 建議關(guān)閉 Source Map 或?qū)?devtool 的值設(shè)置為
nosources-source-map - 好處:放置源碼泄露,提高網(wǎng)站的安全性
- 建議關(guān)閉 Source Map 或?qū)?devtool 的值設(shè)置為
總結(jié):實(shí)際開發(fā)中不需要自己配置webpack
- 實(shí)際開發(fā)中會使命令行工具(俗稱 CLI)一鍵生成 帶有 webpack 的項(xiàng)目
- 開箱即用,所有 webpack 配置項(xiàng)都是現(xiàn)成的
- 但是我們?nèi)孕枰私馀渲庙?xiàng)的基本概念
