一、webpack簡介 webpack 是當下十分流行的一款靜態模塊打包工具,將JS、CSS、HTML、圖片等各種靜態資源視為一個個模塊,通過一個或者多個入口文件通過解析依賴關係生成一個依賴圖,最終打包成一個或者多個bundles,webpack本身只能打包JS文件,但是通過配置的loader和pl ...
一、webpack簡介
webpack 是當下十分流行的一款靜態模塊打包工具,將JS、CSS、HTML、圖片等各種靜態資源視為一個個模塊,通過一個或者多個入口文件通過解析依賴關係生成一個依賴圖,最終打包成一個或者多個bundles,webpack本身只能打包JS文件,但是通過配置的loader和plugin可以打包Css和Html等其他格式的文件,本文基於webpack5為基礎的,其中webpack4前後版本區別很大,使用時需要註意區別版本。
webpack和glup、grunt的區別:webpack可以說是一種模塊化解決方案,內嵌服務支持項目獨立開發,同時可以打包單頁面、多頁面和提取公共模塊以及處理圖片文件。只打包被引用的文件,同時搭配強大的loader和plugin支持打包不同類型的語言,傻瓜式配置即可使用;grunt和glup則是基於任務式的打包工具,主要是用於編譯CSS和JS,主要是用來合併、壓縮和拷貝文件,不支持模塊化打包。
二、配置詳解
常用配置概覽
module.exports = { // 模式,webpack針對不同模式設置內部優化項,設置後會自動調起優化項 mode: 'development', // 入口 可以配置為相對路徑或者絕對路徑 entry: './src/index.js', // 輸出文件名稱和路徑, // path.reslove()返回一個絕對路徑,__dirname當前文件的文件夾絕對路徑 output: { path: path.resolve(__dirname, 'dist'), // 打包輸出文件名 filename: 'main.js', // chunkFilename 指未被列在 entry 中,卻又需要被打包出來的 chunk 文件的名稱。一般來說,這個 chunk 文件指的就是要懶載入的代碼 chunkFilename: '[id].js', // 下次打包時將上次打包目錄下的資源清空 clean: true }, // 載入器 module: { rules: [], }, // 插件 pulgins: [], // 模式 mode: 'development', // devtool: '', // 設置webpack如何解析模塊,如別名、擴展名等 resolve: { // 模塊的尾碼名,引入這些模塊時不需要寫擴展名,自動補充擴展名 extensions: ['.js', '.json', '.css'] }, // 本地服務配置 devServer: { // 指定埠 port: 8000, // 是否開啟模塊熱交換功能 hot: true, // 是否自動在瀏覽器打開網頁 open: true }, // 打包時排除編譯該文件 externals: { jquery: 'JQuery' }, // 打包代碼優化,進行公共代碼提取,代碼壓縮,去除冗餘代碼等功能 optimization: { splitChunks: { chunks: 'all', // 打包文件大於30kb時會被分割 minSize: 30 * 1024, // 最大沒有限制 maxSize: 0, // 要提取的chunk最少被引用1次 minChunks: 1, cacheGroups: { // node_modules下的文件會被打包到wendors中 vendors: { test: /[\\/]node_modules[\\/]/, name: 'vendors', // 優先順序 priority: -10 }, // 提取公共組件 commons: { name: 'chunk-commons', test: resolve('src/components'), minChunks: 3, priority:10 } } } } }
1. entry:打包入口,webpack打包從此處開始,通過解析依賴載入來構建依賴關係圖,包含格式有包含格式有:string | [string] | object { <key>: string | [string] } | (function: () => string | [string] | object { <key>: string | [string] }),可以配置:
// 單入口單模塊 entry: './index.js', // 多入口單模塊(多個入口一個出口) entry: ['./src/Component1/main.js', './src/Component2/main.js'], // 多入口 entry:{ com1: './src/Component1/main.js', './src/Component1/main.js' }, // 接受通過函數方式返回配置 entry: glob.sync('./src/**/main.js').reduce((acc, path) => { const entry = path.substring(path.indexOf('/src/') + 5, path.lastIndexOf('/main.js')); acc[entry] = path; return acc; }, {}),
2. output:打包文件輸出配置,支持設置entry對應的name、id、hash、contenthash,需要註意filename和chunkFilename的區別
filename: "[name].[id].[contenthash].[hash:7].js",
3. mode:模式,內嵌development和production以及none三種模式,針對開發和生產兩種場景內置不同的優化項,比如壓縮、去除冗餘代碼等
4. module:模塊化配置,由於webpack只能解析JS,所以針對CSS、TS、SASS等無法解析的類型需要通過配置不同的loader進行處理編譯。
4.1 style-loader: 將編譯完成的CSS樣式掛載到style標簽上,實際使用中一般放在第一位,因為loader都是從右到左,從下往上執行
// 解析CSS rules: [{ test: /\.css/, use: ['style-loader'] }]
4.2 css-loader:識別.css文件,處理CSS一般需要配合style-loader使用,否則樣式不會生效,或者安裝MiniCssExtractPlugin將文件單獨打包
rules: [{ test: /\.css$/, // use: ['style-loader', 'css-loader'], use: [MiniCssExtractPlugin.loader, 'css-loader'], // 可以排除對應文件夾的內容按照該rule進行打包 exclude: /node_modules/
}]
4.3 sass-loader處理.sass類型樣式文件、postcss-loader用於補充CSS樣式在各個瀏覽器內核的首碼,不需要手動寫
// 打包SASS rules: [{ test: /\.scss$/, use: [ 'style-loader', 'css-loader', 'sass-loader', 'postcss-loader', ], include: '/sc/' }]
4.4 bable-loader: 將ES6+語法轉換為ES5語法。需要配合@bable/core 和bable/preset-env來使用
// 打包JS rules: [{ test: /\.js$/, use: { loader: 'bable-loader', options: { presets: [[ 'bable/preset-env', { targets: 'defaults' } ]] } } }]
4.5 html-loader:將 HTML 導出為字元串。當編譯器需要時,將壓縮 HTML 字元串。
4.6 file-loader:處理文件資源,比如jpg、png等圖片
// 處理圖片資源 rules: [{ test: /\.(png|jpg|jpeg)$/, use:[ { loader: 'file-loader', options: { name: '[name]_[hash:8].[ext]' } } ] }]
4.7 url-loader:同樣是處理圖片資源,與file-loader不同的是可以根據tip的大小決定是否將圖片生成Base64保存到JS文件里,方便快速載入
// 處理圖片資源 rules: [{ test: /\.(png|jpg|jpeg)$/, use:[ { loader: 'url-loader', options: { name: '[name]_[hash:8].[ext]',
// 當小於10kb時轉換為base64打包到JS當中,否則保存為圖片
limit: 10240 } } ] }]
4.8 其餘還有vue-loader 載入和解析vue文件;eslint-loader 通過eslint檢查JS代碼;i18n-loader 載入多語言版本,支持國際化。
註:官方loader查看地址 https://webpack.js.org/loaders/
5. plugins: 插件,和loader一樣是webpack的支柱功能,主要解決loader無法解決的問題,需要使用某個插件時,首先需要安裝,其次引入文件到本地中,最後才能使用
5.1 clean-webpack-plugin:打包前清理dist目錄下的內容,避免再次打包時文件覆蓋,之前的老文件遺留下來
5.2 copy-webpack-plugin:拷貝某個目錄下的文件到dist下的某個目錄,
new CopyWebpackPlugin([ // 將src目錄下的內容拷貝到dist/common下 { from: './src', to: './common' } ])
5.3 html-webpack-plugin:生成html文件,以及自動引入打包好的css文件和JS文件(由於打包後的JS和CSS為了避免緩存都會動態生成文件名,所以每次打包手動引入比較麻煩)
// 單頁面打包 new HtmlWebpackPlugin({ template: './index.html' }), // 多頁面打包 entry: { com1: './src/Component1/main.js', com2: './src/Component2/main.js' }, plugins: [ new HtmlWebpackPlugin({ // 使用模板 template: path.join(__dirname, 'src/component1/index.html'), // 打包後的文件名 filename: 'com1.html', // 和入口文件的key值相對應 chunks: ['com1'] }), new HtmlWebpackPlugin({ template: path.join(__dirname, 'src/component2/index.html'), filename: 'com2.html', chunks: ['com2'] }) ],
5.4 mini-css-extract-plugin:打包好的樣式一般通過style-loader會插入大頁面中,但是如果想要實現CSS分離生成單獨的文件,可以通過這個組件實現,但是需要配合前文中的loader使用
rules: [{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] }], plugins: [ new HtmlWebpackPlugin({ template: './index.html' }), new MiniCssExtractPlugin({ filename: 'css/[name].css' }) ],
5.5 自定義組件AddAuthorPlugin開發和使用:
// 在plugin文件夾下新增一個add-author-plugin.js文件 // 其中apply是內置方法 class AddAuthorPlugin { constructor(options = {}) { this.author = options.author; this.date = options.date; } apply(compiler) { compiler.hooks.compile.tap('AddAuthorPlugin', (compilation, callback) => { console.log('***'); }) } } module.exports = AddAuthorPlugin;
// 使用的時候
const AddAuthorPlugin = require('./plugin/add-author-plugin');
new AddAuthorPlugin({ author: '張三', date: new Date() });
打包時如何調測自定義組件:
首先執行命令 node --inspect-brk .\node_modules\webpack-cli\bin\cli.js,然後在chrome瀏覽器中輸入 chrome://inspect/#devices 回車後,點擊 Open dedicated DevTools for Node 就可以進入調測模式,為了方便打斷點,可以現在代碼中設置debugger。詳細步驟看錄屏
註:官方plugin查看地址 https://webpack.js.org/plugins/
6. devtool:已不同的方式打包文件,配置項總共有十幾種,為了區分開發環境和生產環境,主要介紹下麵幾種
6.1 開發環境推薦使用:
eval:每個模塊都使用eval執行,速度非常快,但是不會顯示正確的行號,所以沒法進行調試
eval-source-map:,每個模式使用eval()執行,起初是緩慢的,但是重建的速度較快而且產生真是的文件,行號也被正確映射,產生的開發資源也是最優的。
cheap-eval-source-map:每個模塊也是經過eval執行,但是只映射了行號沒有列,只顯示與eval類似的被轉換的代碼。
6.2 生產環境推薦使用:
none:不觸發sourcemap,性能最佳
source-map:會生成一個完整的sourceMap作為一個單獨的文件,為bundle添加了引用註釋,因此開發工作知道如何找到它。
hidden-source-map:與source-map類似,但不添加引用註釋。
7. devServer:方便前後端分離開發,一個本地服務
8. optimization:打包優化,可以實現提取公共代碼,代碼壓縮以及去除冗餘代碼等功能
三、常見問題
1. chunk和bundle的概念:chunk是一個代碼塊,在構建的時候會根據入口文件和依賴關係生成多個chunk,bundle是指最後生成的文件,包含所有的chunk和模塊
2. vue中的webpack是如何使用的:當前通過vue-cli腳手架創建的Vue項目都內置了webpack的打包功能,基本不需要獨立配置即可使用,如果更改配置項則需要在vue.config.js中配置
// 兩種方式自定義webpack配置 configureWebpack: { rules: [], plugins: [] }, configureWebpack: (config) => { config.devtool = 'source-map'; config.plugins.push(***); }
3. webpack的優缺點和其他組件(Vite)的對比:
優點:模塊化打包、零配置
缺點:隨著項目變大,模塊化增多,啟動時會逐漸變慢,熱更新也會變慢
Vite:由於打包原理和底層開發使用的語言不同,總體Vite打包和啟動都要比webpack快很多,但是Vite生態不完善。