參考資料:https://www.webpackjs.com/(中文文檔) https://www.webpackjs.com/(官方文檔)
首先有必要說明一下,本文側(cè)重講解webpack基本配置屬性,不附帶實(shí)例,將會(huì)以通俗易懂的形式地講解;如若需要實(shí)例進(jìn)行相關(guān)練習(xí),可將本文作為理論基礎(chǔ);
Webpack是前端項(xiàng)目自動(dòng)化構(gòu)建工具,本質(zhì)上,webpack 是一個(gè)現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(module bundler)。當(dāng) webpack 處理應(yīng)用程序時(shí),它會(huì)遞歸地構(gòu)建一個(gè)依賴關(guān)系圖(dependency graph),其中包含應(yīng)用程序需要的每個(gè)模塊,然后將所有這些模塊打包成一個(gè)或多個(gè) bundle。(官網(wǎng)定義)。
那么用我們?nèi)嗽拋碚f:webpack是一個(gè)前端模塊化的解決方案,更側(cè)重點(diǎn)打包,可以把開發(fā)中的資源文件(圖片、js文件、css文件等)看成是一個(gè)個(gè)的模塊,然后通過webpack提供的loader(加載器)和plugins(插件)來進(jìn)行處理、合并以及壓縮,打包成符合生產(chǎn)環(huán)境、體積更小的文件資源,以方便提升瀏覽器的渲染速度;
首先我們要理解一下webpack的四大核心概念:
入口(entry)
出口(output)
loader(加載器)
plugins(插件)
入口(entry):
webpack入口指的是通過配置來指示webpack的入口文件,即從哪里開始,我們可以在webpack中配置entry屬性,來指定入口文件的路徑;
我們可以看一個(gè)簡單的案例:
//必須將模塊拋出webpack才能訪問得到
module.exports = {
//entry屬性指定入口文件 值為相對路徑
entry: './src/app.js'
}
出口(output):
出口即告訴webpack在哪里輸出它所創(chuàng)建的bundles,以及如何命名這些文件,輸出的文件目錄路徑;也就是說在你用webpack打包的時(shí)候可以通過添加output屬性,來設(shè)置最終經(jīng)過webpack打包輸出后的文件名以及輸出路徑;
//引入node的path模塊
const path = require('path');
module.exports = {
// webpack執(zhí)行入口文件
entry: './src/app.js',
//出口
output: {
// 把所有依賴的模塊合并輸出到一個(gè) bundle.js 文件
filename: 'bundle.js',
// 輸出文件都放到 dist 目錄下
//__dirname是node環(huán)境中全局變量,表示當(dāng)前目錄
path: path.resolve(__dirname, './dist'),
}
};
加載器(loader):
loader是讓webpack能夠去處理一些非Javscript類型的文件(webpack自身只能夠識(shí)別原生JS和ES5),loader可以將所有類型的文件轉(zhuǎn)為webpack能處理的有效模塊,然后就可以通過webpack的打包能力,對這些文件進(jìn)行處理;loader的配置有兩個(gè)選項(xiàng):
test:一般都是正則表達(dá)式,用于匹配文件類型;
use:可以是key:value的形式也可以是數(shù)組,用來告訴webpack使用什么loader來加載文件
//引入node的path模塊
const path = require('path');
module.exports = {
// webpack執(zhí)行入口文件
entry: './src/app.js',
//出口
output: {
// 把所有依賴的模塊合并輸出到一個(gè) bundle.js 文件
filename: 'bundle.js',
// 輸出文件都放到 dist 目錄下
//__dirname是node環(huán)境中全局變量,表示當(dāng)前目錄
path: path.resolve(__dirname, './dist'),
}
//模塊關(guān)鍵字,加載器需要在這里進(jìn)行配置
module:{
//rules為數(shù)組,保存每個(gè)加載器的配置
rules:[
{
//test屬性必須配置,值為正則表達(dá)式,用于匹配文件
test:/\.css$/,
// 對同一個(gè)文件引入多個(gè)loader的方法
use:[
//loader為loader加載器的名稱,必須配置,值為字符串
{loader:"style-loader"},
{loader:"css-loader"}
],
//過濾,排除node_module目錄下的文件
exclude:/node_module/,
//指定匹配文件的范圍 指定/demo/目錄下的.css文件進(jìn)行匹配
include:/demo/
}
]
}
};
webpack中規(guī)定,在webpack中定義loader時(shí),需要定義在module.rules中,否則會(huì)報(bào)錯(cuò);test和use兩個(gè)屬性分別指定了匹配文件的規(guī)則和用什么loader來處理,這兩個(gè)屬性都是必選的,exclude、include分別表示過濾(不處理)某個(gè)文件中的文件和指定(處理)某個(gè)文件夾中的文件,這兩個(gè)屬性都是可選項(xiàng);
插件(plugins):
loader 被用于轉(zhuǎn)換某些類型的模塊,而插件則可以用于執(zhí)行范圍更廣的任務(wù)。插件的范圍包括,從打包優(yōu)化和壓縮,一直到重新定義環(huán)境中的變量。插件接口功能極其強(qiáng)大,可以用來處理各種各樣的任務(wù)。要使用某個(gè)插件,你需要require(引入)插件,然后添加到plugins數(shù)組中,多數(shù)插件可以通過選項(xiàng)option來自定義
//引入node的path模塊
const path = require('path');
//引入extract-text-webpack-plugin插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// webpack執(zhí)行入口文件
entry: './src/app.js',
//出口
output: {
// 把所有依賴的模塊合并輸出到一個(gè) bundle.js 文件
filename: 'bundle.js',
// 輸出文件都放到 dist 目錄下
//__dirname是node環(huán)境中全局變量,表示當(dāng)前目錄
path: path.resolve(__dirname, './dist'),
}
//模塊關(guān)鍵字,加載器需要在這里進(jìn)行配置
module:{
//rules為數(shù)組,保存每個(gè)加載器的配置
rules:[
{
//test屬性必須配置,值為正則表達(dá)式,用于匹配文件
test:/\.css$/,
// 對同一個(gè)文件引入多個(gè)loader的方法
use:[
//loader為loader加載器的名稱,必須配置,值為字符串
{loader:"style-loader"},
{loader:"css-loader"}
],
//過濾,排除node_module目錄下的文件
exclude:/node_module/,
//指定匹配文件的范圍 指定/demo/目錄下的.css文件進(jìn)行匹配
include:/demo/
}
]
}
//配置插件
plugins: [
new ExtractTextPlugin({
//自定義配置插件選項(xiàng)
// 從 .js 文件中提取出來的 .css 文件的名稱
filename: `[name]_[md5:contenthash:hex:8].css`,
}),
]
};
上面就是webpack的四大核心概念,下面我們來介紹一些常用的插件:
loader處理css和Sass:
默認(rèn)情況下webpack是處理不了CSS的代碼的,但是我們可以通過webpack的loader加載器來處理;
module.exports = {
entry: './src/app.js',
output: {
path: __dirname + '/dist',
filename: 'app.bundle.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
]
}
};
在日常開發(fā)中,我們只需要在webpack.config.js文件中寫上上面的配置代碼即可讓webpack來處理CSS代碼和Sass代碼;
webpack-dev-server:
webpack-dev-server是webpack的一個(gè)常用插件,可以用來在本地上開啟服務(wù)、啟動(dòng)瀏覽器并且可以實(shí)時(shí)監(jiān)聽文件修改;
module.exports = {
entry: './src/app.js',
...
//進(jìn)行webpack-dev-server插件配置
devServer: {
//端口號(hào),默認(rèn)8080,可以自定義修改
port: 9000,
//運(yùn)行webpack-dev-server的時(shí)候自動(dòng)打開瀏覽器
open: true
},
...
};
source-map調(diào)試:
開發(fā)總是離不開調(diào)試,但是我們用webpack給項(xiàng)目打包了之后,我們是很不方便找到錯(cuò)誤或者問題所在的,當(dāng)然webpack也想到了這一點(diǎn),因此webpack給我們提供了source-map來進(jìn)行調(diào)試;
devtool選項(xiàng)
source-map
配置結(jié)果
在一個(gè)單獨(dú)的文件中產(chǎn)生一個(gè)完整且功能完全的文件。這個(gè)文件具有最好的source map,但是它會(huì)減慢打包速度;
devtool選項(xiàng)
cheap-module-source-map
配置結(jié)果
在一個(gè)單獨(dú)的文件中生成一個(gè)不帶列映射的map,不帶列映射提高了打包速度,但是也使得瀏覽器開發(fā)者工具只能對應(yīng)到具體的行,不能對應(yīng)到具體的列(符號(hào)),會(huì)對調(diào)試造成不便;
devtool選項(xiàng)
eval-source-map
配置結(jié)果
使用eval打包源文件模塊,在同一個(gè)文件中生成干凈的完整的source map。這個(gè)選項(xiàng)可以在不影響構(gòu)建速度的前提下生成完整的sourcemap,但是對打包后輸出的JS文件的執(zhí)行具有性能和安全的隱患。在開發(fā)階段這是一個(gè)非常好的選項(xiàng),在生產(chǎn)階段則一定不要啟用這個(gè)選項(xiàng);
devtool選項(xiàng)
cheap-module-eval-source-map
配置結(jié)果
這是在打包文件時(shí)最快的生成source map的方法,生成的Source Map會(huì)和打包后的JavaScript文件同行顯示,沒有列映射,和eval-source-map選項(xiàng)具有相似的缺點(diǎn);
綜上所述,從上到下打包速度越來越快,不過同時(shí)帶來的副作用也越多,較快的打包速度的后果就是對打包后的文件的的執(zhí)行有一定影響。在中小型項(xiàng)目中,eval-source-map是一個(gè)比較不錯(cuò)的選擇;
cheap-module-eval-source-map方法構(gòu)建速度更快,但是不利于調(diào)試,推薦在大型項(xiàng)目考慮時(shí)間成本時(shí)使用。
module.exports={
devtool:"eval-source-map",
}
在webpack打包之后,如果報(bào)錯(cuò)我們是看不到源文件的,因?yàn)榇藭r(shí)文件已經(jīng)被webpack打包了,這很不方便我們的開發(fā)調(diào)試,source-map就是用來解決這個(gè)問題的,通過添加配置devtool:"eval-source-map"這一行簡單的代碼,我們即可在調(diào)試的時(shí)候看到我們的源文件進(jìn)行調(diào)試;