Webpack 備忘錄

Webpack 屬於在項目中配置一次就不多改動的那種工具,但這樣就致使新項目再配置 Webpack 時會有些生疏,因此將 Webpack 核心概念及經常使用配置記錄以下。css

1)核心概念

Webpack 4.x 以前的核心概念有四個:entry,output,loaders,plugins,4.x 以後增長了 mode。含義以下:html

  • Entry:指定 webpack 從哪一個 file 開始構建他的依賴關係圖,能夠有一個和多個,推薦對象表示法;
  • Output:webpack 構建完成後的 bundles 輸出位置及 name,其中 name 通常根據 entry 的 file name 動態生成;
  • Loaders:webpack 能夠將非 js 文件(css、圖像等)經過 loaders 處理成可供咱們的應用使用的 modules。也所以在配置文件中,loaders 寫在module對象下面,由於 loaders 處理後返回的是有效 modules。
  • Plugins:若是須要額外的功能,好比壓縮代碼、提取 css、分析打包性能,可以使用插件完成;

    Loaders 將特定文件轉換爲有效 module,而 plugin 擴展性更強vue

  • Mode:接受developmentproductionnone三個值,通常指定爲前兩個值的一種,webpack 內部針對不一樣環境作優化。

2)配置

下面介紹的配置都是module.exports的直接屬性,好比:node

module.exports = {
entry: {},
output: {},
// loaders 配置位置有些特殊,不知直接寫 loaders 對象,而是將它鞋子啊 module 對象下面
// 由於 loaders 處理完後返回的是有效 modules
// 不是 複數!!!
module: {},
// plugin 是數組
// 是 複數!!!
plugins: []
};

同時,文件頭引入相應依賴的過程省略了,實際開發中請自行引入。jquery

2.1 Entry

Entry 指定哪些文件做爲 webpack 打包入口,能夠有一個或者多個:webpack

entry: {
index: './src/js/view/index.js',
people: './src/js/view/people.js',
login: './src/js/view/login.js',
}

2.2 Output

Output 指明 Webpack 處理完成後的 bundles 輸入位置及文件 name:git

output: {
// 輸入目錄,這裏使用了 node 的 path 模塊
path: path.resolve(__dirname, 'dist'),
// [] 裏面的內容表示變量
filename: 'js/[name].[contenthash:8].js',
// 靜態資源在 html 引用的公共目錄
publicPath: '/',
}

Output 配置相對複雜:es6

2.2.1 output.path

即輸出文件目錄,推薦使用絕對路徑,其中__dirname在 Node 的 path 模塊中表示當前目錄(即 webpack.config.js 的目錄)的絕對路徑,resolve方法能夠拼接兩個目錄;github

後一個目錄能夠加/也能夠不加,此處無差異web

2.2.2 output.filename

表示輸出文件名稱,這裏注意,[]中是變量,其中name由在 entry 中定義的文件名決定,而且,有多少個輸入文件,對應有多少個輸出文件,後面的contenthash:8表示 chunk 的 hash 值,後文介紹;

實際輸出文件通常多於輸入文件,由於提取公共代碼、設置 sourcemap 都會單獨生成輸出文件

2.2.3 output.publickPath

這個配置想最爲複雜,它的做用是配合 loaders 或 plugins 中設置的資源路徑,指定靜態文件(css、js、img)插入 html 中的引用路徑,簡單講,就是 對輸出的靜態資源進行目錄管理。這裏直接使用 詳解 Webpack2 的那些路徑的描述,只稍做說明。

靜態資源最終引用路徑計算公式:

html 靜態資源路徑 = output.publicPath + loaders/plugins 中設置的資源路徑

好比:

// publicPath 設置
output.publicPath = '/static/'

// JS output.filename 配置
{
filename: 'js/[name].js'
}
// JS 最終訪問路徑爲
output.publicPath + 'js/[name].js' = '/static/js/[name].js'

// 圖片資源, file-loader,這裏省略了 file-loader 其餘配置,只列出指定輸出路徑及文件名的配置
{
name: 'img/[name].[ext]'
}
// 圖片最終的訪問路徑爲
output.publicPath + 'img/[name].[ext]' = '/static/img/[name].[ext]'

// CSS,ExtractTextPlugin 爲提取 CSS 的插件
new ExtractTextPlugin("css/style.css")
// CSS 最終訪問路徑爲
output.publicPath + 'css/style.css' = '/static/css/style.css'

咱們上述指定publicPath爲絕對路徑,實際上,也支持相對路徑,相對於index.html,用的比較少。

publicPath默認爲空字符串,但爲了使輸出目錄更加條理,推薦使用publicPath對靜態資源進行目錄管理。

2.3 Loaders

Loaders 的配置寫在module對象中,如前所述,由於 loaders 最終返回的是有效 modules,故使用了module命名,注意是單數。這節介紹 loaders 配置及經常使用 loaders 兩部份內容。

2.3.1 loaders 配置

loaders 規則寫在 module.rules 裏面(不知道爲何不直接寫在 module 中),其中 rules 是個數組,可接受一個或多個 loader 配置。兩點須要注意:

1)若是某類型須要多個 loader 進行處理,在 use 中按 從右往左 的順序流式處理;
2)每一個 loader 能夠進行額外配置。

module: {
// 含有 rules,這一個屬性
// rules 是數組,每一個元素對應一個 loader 配置
// loader 配置自己是個對象
rules: [
{
// 正則匹配文件,最終匹配的是一個路徑,具體間解析 zepto 源文件時的設置
test: /\.scss$/,
// 使用的 loader,能夠字符串(單個 loader)、數組(多個 loader)
// 若是是數組,從右往左開始解析
use: ['style-loader', 'css-loader', 'scss-loader'],
},
{
test: /\.(png|jpg|jpeg|svg|gif)$/,
// 每一個 loader 可進行額外配置
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[ext]',
},
},
],
},
],
},

2.3.2 經常使用 loader

  • babel-loader:es6+語法轉換;
  • html-loader:解析 html 文件;
  • css-loader:解析 css 文件;
  • sass-loader:解析 scss/sass 文件;
  • style-loader:將解析後的 css 嵌入 js;
  • file-loader:解析圖片文件;
  • url-loader:具備 file-loader 的所有功能,同時能夠提取小圖片爲 base64(若是開啓 HTTP2 這樣增大 靜態資源體積反而很差?);
  • postcss-loader:完成 css 自動化處理,好比添加前綴、壓縮 css、自動生成雪碧圖等

    postcss 自己支持插件擴展,經常使用的有 autoprefixer、cssnano、postcss-sprites,更多參考官網介紹
    要在 css-loader 以前處理 css:use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']

Vue 相關

  • vue-loader:解析 vue 文件;
  • vue-style-loader:解析 vue 中的樣式文件。

2.4 Plugins

同 loaders 同樣,分配置和經常使用 plugins 兩部分

2.4.1 plugins 配置

Plugins 使用相對簡單,配置項寫在plugins(複數)數組中,元素爲插件的實例(經過new調用),生成實例的時候可接收參數,具體看相應插件文檔。見下面代碼及註釋:

plugins: [
// 帶有參數
new CleanWebpackPlugin(['dist']),
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
chunkFileName: '[id].[contenthash:8].css',
}),
// 直接調用
new UglifyJsPlugin(),
],

2.4.2 經常使用 plugins

  • html-webpack-plugin:將構建後的靜態文件動態插入 html 中;
  • webpack-merge:實際項目中,通常將 webpack 配置文件拆分爲 base、dev、pro,這個插件用戶合併配置文件;
  • webpack.HotModuleReplacementPlugin:熱更新插件,webpack 內置;
  • clean-webpack-plugin:清除指定文件夾,通常是構建的目錄(大型項目慎用,更新須要時間);
  • mini-css-extract-plugin:提取 css 文件,減少增量更新成本(須要在module代理 style-loader 處理 css);

    style-loader 先把 css 嵌入 js 使其變成有效 module,這個插件將 css 從 js 中分離出來。這個過程不矛盾,由於最開始 webpack 沒法處理 css 文件,因此須要 css-loader, style-loader 處理,嵌入到 js 中的 css mini-css-extract-plugin 能夠識別並提出。這個插件是 webpack 4.x 新引入,代替 extract-text-webpack-plugin。

  • optimize-css-assets-webpack-plugin:將提取出的 css 作進一步優化;
  • uglifyjs-webpack-plugin:不只僅是壓縮代碼,還進行了 tree shaking 工做
  • webpack-bundle-analyzer:打包後文件圖形化展現工具,一目瞭然各文件體積;
  • ProvidePlugin:webpack 內置,提取第三方庫的 api,好比經過 $ 符號調用 jq

    若是不是經過 npm 安裝,而是直接在項目中引入,須要配合resolve的別名使用,否則找不到

2.5 Mode

接受developmentproductionnone三個值,通常取前兩個值之一,指定構建環境,webpack 自己會作相應優化(配合optimization使用,下文會有更多介紹),同時省去在命令行中指定構建環境的過程。

// 至關於 webpack --mode=production
mode: 'production',

2.6 其餘

上述配置對應 Webpack 核心概念,除此以外,還有下面配置項很經常使用。

2.6.1 optimization

這個選項是在 Webpack 4.x 中引入的,最經常使用的配置是代替CommonsChunkPlugin提取公共代碼。下面直介紹提取公共 JS 代碼的配置:

optimization: {
splitChunks: {
// 只對入口文件處理
chunks: 'initial',
cacheGroups: {
vendor: {
// split `src/js/vendor` 目錄下共用的代碼到`vendor.js`
// 這裏指的是第三方庫,不多改動,故單獨作持久化緩存
// 注意,目前 splitChunks 在 split css 時有 bug?如今只 split js 代碼
// 不推薦直接匹配 node_modules 文件夾,如上,由於涉及 css,打包後 css 錯誤
test: /src\/js\/vendor\//,
name: 'vendor',
// 只要第三方庫,只要 > 0B, 就 split 出來
minSize: 0,
// 只要被引用一次,也要分離出來
minChunks: 1,
// priority: 10,
// enforce: true,
},
common: {
// split `src/js/common` 目錄下共用的代碼到`common.js`
// 這裏是自定義的公共 js 代碼,改的頻率比第三方庫要高,但比具體頁面的 js 文件低
// 故單獨 split 出來作持久化緩存
// 不必定共用才 split ,不多改動的也 split 出來,如上的 vender.js
// 但此處至少兩個共用 minChunks 才抽出
test: /src\/js\/common\//,
name: 'common',
minChunks: 2,
minSize: 0,
// priority: 9,
// enforce: true,
},
},
},
},

2.6.2 devtool

devtool最經常使用功能是配置 sourcemap,生產和開發環境通常使用不一樣的 sourcemap,配置以下:

// 生產環境
devtool: 'source-map',

// 開發環境
devtool: 'cheap-module-eval-source-map',

關於各配置項的含義可參考Devtool和這片文章Webpack 的 devtool 和 source maps

sourcemap 是構建後的代碼 map 到源代碼的映射表,便於定位 bug,具體的映射關係存儲在構建後的.map文件中。

NOTE: 這裏咱們在生產環境使用的是 source-map 選項而不是其餘,是由於目前 webpack 在開啓 uglifyjs-webpack-plugin 優化代碼後,其餘模式下的 sourcemap 選項無效,官網對此也有說明。雖然使用source-map選項生成的映射表比較大,但只有開啓開發者工具的時候.map文件纔會加載,這意味着 映射表文件的大小不影響正經常使用戶的訪問體驗,可是否壓縮 js 文件對正經常使用戶有直接影響,故,現階段,生產環境 devtool 使用 source-map 選項。

2.6.3 devServer

開發環境爲了更好的開發體驗,能夠開啓熱更新等功能,在devServer中配置:

devServer: {
open: true,
hot: true,
},

須要在插件中進行下面配置:

plugins: [
new webpack.HotModuleReplacementPlugin(),
],

NOTE:若是傳統多頁面項目,在入口文件後面添加下面代碼,配合熱更新:

// 配合 webpack 配置實現熱更新
if (module.hot) {
module.hot.accept();
}

多頁面目前只有在 JS 或者 CSS 文件改變的時候實現了熱更新,若是是模版(html)文件改變,沒有實現熱更新(能夠實現自動刷新頁面,但感受很雞肋,若是改動了模版文件,手動刷新)。

devServer 還能夠設置反向代理,後續填坑。

2.6.4 resolve

resolve 選項能夠指定如何解析 modules,更可能是經過設置 alias 告訴 webpack 去哪找文件解析。好比,若是在項目中經過文件的形式引入的 jq,那在使用 ProvidePlugin 對 jq 進行解析的時候,就須要經過設置別名的形式告訴 webpack 去哪找 jq 源文件。

resolve: {
alias: {
// 後面加 $ 符號表示精確匹配
jquery$: path.resolve(__dirname, 'path/jquery.min.js');
}
}


// 解析插件
new webpack.ProvidePlugin({
// 加載 jquery
$: 'jquery',
}),

3)常見需求詳細配置

下面記錄針對具體需求的完整代碼描述。

3.1 引入第三方庫

好比,jq,zepto 等,若是經過 CDN 能夠在項目中直接使用 $ 符號,但若是是經過 npm 安裝到本地,甚至直接將第三方庫源文件寫在項目中,那是沒法直接使用 $ 符號這種調用方式的,須要使用上文介紹的 ProvidePlugin 插件。固然,還有細節須要注意,見下面詳細代碼。

3.1.1 使用 npm 引入

相對於直接引入,使用 npm 能夠省去咱們手動指定 module 路徑的麻煩,已 zepto 爲例:

使用 zepto 時,直接使用 ProvidePlugin 會報錯,具體參考這片文章如何在 webpack 中引入未模塊化的庫,如 Zepto

// 處理 zepto 模塊化問題,須要安裝 exports-loader 和 script-loader
{
test: require.resolve('zepto'),
use: ['exports-loader?window.Zepto', 'script-loader'],
},

// 給 zepto 設置別名,能夠任何名稱
plugins: [
new webpack.ProvidePlugin({
$: 'zepto',
}),
],

3.1.2 直接在項目中引入源文件

同 npm 不一樣之處在於咱們須要手動指定 zepto 的路徑,以下

// 設置別名
resolve: {
alias: {
zepto$: path.resolve(__dirname, 'src/js/vendor/zepto.min.js'),
},
},

// 處理 zepto 模塊化問題
{
// test 路徑也要跟着改變,不知道爲何不能用上面指定的別名。。
test: path.resolve(__dirname, 'src/js/vendor/zepto.min.js'),
use: ['exports-loader?window.Zepto', 'script-loader'],
},

// 給 zepto 設置別名,能夠任何名稱
plugins: [
new webpack.ProvidePlugin({
$: 'zepto',
}),
],

3.2 提取 css

爲了能將 css 從 js 中提取出來,須要在 module 中將 style-loader 替換爲 MiniCssExtractPlugin.loader,見下面代碼:

module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
{
test: /\.scss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
},
],
},

plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
// 這個幹嗎用?
chunkFileName: '[id].[contenthash:8].css',
}),
],

3.3 緩存控制

主要一下幾個方面:

  • 提取 css,添加 contenthash(使用 mini-css-extract-plugin);
  • 提取公共代碼(工具函數等,更改頻率相比業務代碼要小),添加 contenthash,使用 webpack 自帶 splitChunks 提取;
  • 提取第三方庫代碼(好比 jquery,更改頻率相比公共代碼還要小),添加 contenthash,使用 webpack 自帶 splitChunks 提取;
  • 圖片能夠直接打 hash(圖片文件添加 hash 並不同,也不會隨每次構建改變,還不知原理,反正能夠工做,待填坑)。
相關文章
相關標籤/搜索