1. devtool代碼調試
1. 生產模式下
source-map: 生成一個map文件,直接定位到源碼的行列
✅可以使用該模式,用於測試服務器
cheap-source-map: 只能定位到行,且只能定位到babel轉碼后的代碼
cheap-module-source-map: 只能定位到行,但是可以定位到源碼
2. 開發模式下
eval: 定位到編譯后的代碼
cheap-eval-source-map: 定位到babel轉碼后的行
cheap-module-eval-source-map: 定位到源碼的行
eval-source-map: 定位到源碼的行列
✅推薦使用該方法
line-*模式慢,不考慮
2. 第三方庫的應用
1. webpack.ProvidePlugin
對於類似lodash的各模塊頻繁使用的情況, 為了避免每次都手動引入,可以使用該插件實現全部模塊的自動引入該文件。只是優化了重復引入的問題,打包體積和直接引入相同。
new webpack.ProvidePlugin({ _: 'lodash' }),
相當於在每個模塊都默認執行了引入,用戶可以直接使用。
import _ from 'lodash';
❎該方法不是全局變量
2. expose-loader
該方法可以避免重復引入高頻使用的庫。而且可以將其作為全局變量。對於debugging很方便。 在控制台就可以直接使用。
在入口文件使用:
// 必須是require require('expose-loader?_!lodash'); // !前是全局變量名稱;!后是庫名
然后可以直接使用window._訪問。
3. externals
當已經從CDN等外部引入第三方庫時,如果代碼中又手動引入了同樣的庫,該配置可以讓webpack不打包配置中的第三方庫。
externals: { lodash: '_' // :前的key是庫的名稱;后面是全局變量的名稱 }
想要起作用,必須要在html中引入CDN
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js"></script>
如上,即使在模塊中手動引入,webpack也不會將其打入包內
import _ from 'lodash'; //會忽略引入的lodash庫 const a = _.join(['a', 'b'], '~');
4. html-webpack-externals-plugin
3中,需要在html文件中引入CDN文件。如果不想手動引入,直接配置生成,可以使用該插件。
⚠️該插件必須在html-webpack-plugin插件實例化之后再進行實例化
new HtmlWebpackPlugin({ }), new HtmlWebpackExternalsPlugin({ externals: [ { module: 'lodash', global: '_', entry: 'https://cdn.jsdelivr.net/npm/lodash@4.17.15/lodash.min.js' } ] }),
3. 打包后的代碼添加注釋說明
new webpack.BannerPlugin('Lyra'),
4. 拷貝靜態文件(txt/doc等)
new CopyWebpackPlugin([{ from: path.join(__dirname, './src/assets'), to: path.join(__dirname, './dist/assets') }]),
5. devServer實現proxy代理,和模擬服務器
devServer: { contentBase: path.join(__dirname, 'dist'), port: 8080,
host: 'localhost',
compress: true, // 啟用gzip壓縮 before(app) { // devServer本身是一個express服務器,app是其對應的app;before指的是在app.listen之前執行 app.get('/api/user', (req, res) => { res.json([{ a: 'lyra' }]); }); }, proxy: { '/api': { target: 'http://localhost3000', // 代理本地服務到目標服務器;相當於nginx,不存在跨域問題 pathRewrite: { '^/api': '' // 重寫路徑;如:/api/user -> /user } } } },
6. webpack-dev-middleware模擬實現webpack-dev-server
const express = require('express'); const WebpackDevMiddleware = require('webpack-dev-middleware'); const webpack = require('webpack'); const webpackConfig = require('./webpack.config.js'); const app = express(); // 返回一個編譯對象 const compiler = webpack(webpackConfig); // 1.使用webpack-dev-middleware插件啟動編譯 // 2.使用該插件響應客戶端請求的打包文件 app.use(WebpackDevMiddleware(compiler, {})); app.get('/api/user', (req, res) => { res.json([{ a: 'name' }]); }); app.listen(5000);
通過命令啟動
"scripts": { "node": "node devServer.js", "build": "webpack", "dev": "webpack-dev-server --open" },
7. 模塊解析規則resolve
1. extensions
當引入文件不寫擴展名時,根據extentsions設置的規則,進行文件查找。
module.exports = { ... resolve: { extensions: ['.js', '.jsx', '.json'] } }
2. alias
給查找路徑定義別名,加快文件查找速度。
對於引入npm安裝的模塊時,它會按照查找規則,依次查找。對於相對路徑查找,也會按照相對路徑的規則,依次查找。
通過定義alias可以直接按照alias指定的路徑查找,避免路徑解析消耗的時間。
module.exports = { //... resolve: { alias: { Utilities: path.resolve(__dirname, 'src/utilities/'), Templates: path.resolve(__dirname, 'src/templates/') } } };
引入alias之前:
import Utility from '../../utilities/utility';
引入之后
import Utility from 'Utilities/utility';
另外,別名最后加上$表示,嚴格匹配,即使用時只能是別名,其他的都不匹配。
module.exports = { //... resolve: { alias: { xyz$: path.resolve(__dirname, 'path/to/file.js') } } };
示例:
import Test1 from 'xyz'; // 嚴格匹配,按照別名解析 import Test2 from 'xyz/file.js'; // 不嚴格匹配,按照原來的解析規則解析
3. modules
指定查找的模塊,也可以在默認規則上添加其他需要查找的模塊
module.exports = { //... resolve: { modules: ['node_modules', 'myloaders'] //后者是自定義的模塊 } };
4. mainFields
定義入口文件字段的查找順序
module.exports = { //... resolve: { //定義了先查找package.json中的browser字段... mainFields: ['browser', 'module', 'main'] } };
5. mainFiles
當解析文件夾目錄的時候,會按照該字段查找文件夾中的文件
module.exports = { //... resolve: { mainFiles: ['index'] //也可以修改為main.js等 } };
8.resolveLoaders
屬性和規則同resolve, 對應的是處理模塊的loader的查找規則。
module.exports = { //... resolveLoader: { modules: ['node_modules'], extensions: ['.js', '.json'], mainFields: ['loader', 'main'] } };
在module中按照rules使用loaders進行解析時,查找loader模塊默認按照resolveLoader中的定義規則查找。
9. 自定義全局變量webpack.DefinePlugin
該插件中對應的字符串的內容,會被當作代碼片段解析。變量對應的值如果是非字符串,要通過JSON.stringify()進行序列化。
new webpack.DefinePlugin({ "PRODUCTION": JSON.stringify(true), "AUTHOR": { "USER": JSON.stringify('lyra') // 字符串也需要序列化,否則按照表達式處理 } }),
10. 環境變量process.env.NODE_ENV
在代碼中可以通過訪問process.env.NODE_ENV來獲取當前項目的mode值。
應用:
可以根據該值來封裝函數,實現在不同環境下的不同表現。
const oldLog = console.log; // 覆蓋原來的日志函數 console.log = function newlog() { if (process.env.NODE_ENV === 'development') { oldLog('開發環境打印日志'); } }; //使用時,引入該文件 import './console'; console.log("node_env", process.env.NODE_ENV);
11. glob匹配多入口文件
glob是一個匹配文件的工具。
npm i glob
如果需要實現多個入口文件,使用多個html模版文件。我們可以將入口文件統一放入entries文件中;將模版文件統一放入templates文件夾中。且名稱一一對應。
則不在需要一個個添加html-webpack-plugin插件。可以批量操作。
const glob = require('glob'); const files = glob.sync('./src/entries/*.js'); //獲取匹配的所有文件路徑 const entries = {}; const templates = []; files.forEach((file) => { const fileName = path.basename(file, '.js'); //獲取文件的名稱,不含擴展名 entries[fileName] = file; templates.push(new HtmlWebpackPlugin({ template: `./src/templates/${fileName}.html`, filename: `${fileName}.html`, // hash: true, chunks: [fileName], // 指定各自的chunk塊,否則所有chunk將都引入 chunksSortMode: 'manual' })); });
12. 日志優化stats
webpack編譯時打印的日志多數是無效日志,我們通過設置stats的值自定義顯示的代碼。
stats默認是"normal"。
module.exports = { //... stats: 'normal' };
還可以是其他值,如
1. errors-only //只打印錯誤信息 2. errors-warnings // 錯誤和警告 3. verbose // 打印所有的信息 4. minimal // 只打印錯誤和新的編譯 5. none // 不打印
如果想要更清晰,可以使用friendly-errors-webpack-plugin插件(作用不大)。
module.exports = { // ... plugins: [ new FriendlyErrorsWebpackPlugin(), ], }
13. 實現ES6等新API的兼容問題polyfill
一般會推薦使用babel-polyfill,用法
import 'babel-polyfill';
但是該方法會增大打包文件的體積,而且對於不需要的瀏覽器也會打包。
為了解決這些問題,可以使用
<script src="https://polyfill.io/v3/polyfill.min.js/"></script>
該接口可以根據UserAgent返回需要的polyfills。如果是Chrome瀏覽器,將返回空內容,因為它不需要polyfills。
13. stats.json
npx webpack --profile --json > stats.json
通過名稱生成stats.json文件,里面包含打包的所有信息。主要有幾個屬性:
1. chunks
代碼塊。生成代碼的途徑:
1. 入口文件會生成對應的chunk
output: { filename: '[name].[contenthash].js' //hot:true下不能contenthash }
2. 動態導入模塊會生成對應的chunk(import())
這類代碼塊的chunk命名規則如下 import(/* webpackChunkName: "example" */'./example')
3. splitChunk會生成分割的代碼chunk(代碼塊)
optimization: { splitChunks: { cacheGroups: { vendors: { chunks: 'initial', //必須有,默認是async(import());initial是同步,不設置該值不會生成代碼塊 name: 'vendors', //設置代碼塊名稱 test: /node_modules/, //指定要打包的模塊 minSize: 50*1024, // 打包最小體積 priority: 1 }, commons: { chunks: 'initial', name: 'commons', test: /src/, minChunks: 2, // 至少被minChunks個module引用過 priority: 2 } } } },
2. modules
在webpack中一切皆模塊。每個js,css,image等都是模塊。
webpack本身只能識別js文件,對於任何非js文件都需要通過loader進行編譯, 最終將其編譯為js輸出。
3. assets
最后打包生成的所有文件都是assets。