webpack优化相关操作
1、縮小文件搜索的范圍
?? ?? 優化loader配置?? ?
盡量精確使用 include 只命中需要的文件。
?? ?module.exports = {
?? ?? module: {
?? ???? rules: [
?? ?????? {
?? ???????? // 如果項目源碼中只有 js 文件就不要寫成 /\.jsx?$/,提升正則表達式性能
?? ???????? test: /\.js$/,
?? ???????? // babel-loader 支持緩存轉換出的結果,通過 cacheDirectory 選項開啟
?? ???????? use: ['babel-loader?cacheDirectory'],
?? ???????? // 只對項目根目錄下的 src 目錄中的文件采用 babel-loader
?? ???????? include: path.resolve(__dirname, 'src'),
?? ?????? },
?? ???? ]
?? ?? },
?? ?};
?? ???
? 優化 resolve.modules 配置
??? 如果能明確第三方模塊是在本項目下,那么避免遞歸向上查詢。
?? ?module.exports = {
?? ?? resolve: {
?? ???? // 使用絕對路徑指明第三方模塊存放的位置,以減少搜索步驟
?? ???? // 其中 __dirname 表示當前工作目錄,也就是項目根目錄
?? ???? modules: [path.resolve(__dirname, 'node_modules')]
?? ?? },
?? ?};?? ?
??? ? 優化 resolve.mainFields 配置
??? 如果能明確第三方入口文件 描述字段,盡可能設置的少,
?? ? 由于大多數第三方模塊都采用 main 字段去描述入口文件的位置,可以這樣配置 Webpack:
?? ?
?? ?module.exports = {
?? ?? resolve: {
?? ???? // 只采用 main 字段作為入口文件描述字段,以減少搜索步驟
?? ???? mainFields: ['main'],
?? ?? },
?? ?}
??? ? 優化 resolve.alias 配置
??? 對于一些完整性較強的庫采用直接 定義路徑 配置。如 react 庫,vue庫等。
?? ?module.exports = {
? resolve: {
??? // 使用 alias 把導入 react 的語句換成直接使用單獨完整的 react.min.js 文件,
??? // 減少耗時的遞歸解析操作
??? alias: {
????? 'react': path.resolve(__dirname, './node_modules/react/dist/react.min.js'),
??? }
? },
};
??? ? 優化 resolve.extensions 配置
??? 代碼書寫時,盡量帶上文件后綴。該項列表盡可能短,且高頻的放在最前面,不可能的情況不要寫。
?? ?module.exports = {
?? ?? resolve: {
?? ???? // 盡可能的減少后綴嘗試的可能性
?? ???? extensions: ['js'],
?? ?? },
?? ?};?? ?
??? ? 優化 module.noParse配置
??? 對沒有采用模塊化的文件 直接進行忽略設置,如 jQuery 、ChartJS
?? ?const path = require('path');
?? ?
?? ?module.exports = {
?? ?? module: {
?? ???? // 獨完整的 `react.min.js` 文件就沒有采用模塊化,忽略對 `react.min.js` 文件的遞歸解析處理
?? ???? noParse: [/react\.min\.js$/],
?? ?? },
?? ?};?? ?
2、使用DLLPlugin插件
使用動態鏈接庫:對于一些大量復用的基礎模塊,只編譯一次,放置到 動態鏈接庫里。常用于包含第三方模塊,如 react、react-dom。
涉及問題:
打包成動態鏈接庫;如何使用。
涉及插件:
? DllPlugin 插件:用于打包出一個個單獨的動態鏈接庫文件。
? DllReferencePlugin 插件:用于在主要配置文件中去引入 DllPlugin 插件打包好的動態鏈接庫文件
構建:
構建輸出的以下這四個文件
├── polyfill.dll.js
├── polyfill.manifest.json
├── react.dll.js
└── react.manifest.json
和以下這一個文件
├── main.js
是由兩份不同的構建分別輸出的。
動態鏈接庫文件相關的文件需要由一份獨立的構建輸出,用于給主構建使用。新建一個 Webpack 配置文件 webpack_dll.config.js 專門用于構建它們,文件內容如下:
const path = require('path');
const DllPlugin = require('webpack/lib/DllPlugin');
module.exports = {
? // JS 執行入口文件
? entry: {
??? // 把 React 相關模塊的放到一個單獨的動態鏈接庫
??? react: ['react', 'react-dom'],
??? // 把項目需要所有的 polyfill 放到一個單獨的動態鏈接庫
??? polyfill: ['core-js/fn/object/assign', 'core-js/fn/promise', 'whatwg-fetch'],
? },
? output: {
??? // 輸出的動態鏈接庫的文件名稱,[name] 代表當前動態鏈接庫的名稱,
??? // 也就是 entry 中配置的 react 和 polyfill
??? filename: '[name].dll.js',
??? // 輸出的文件都放到 dist 目錄下
??? path: path.resolve(__dirname, 'dist'),
??? // 存放動態鏈接庫的全局變量名稱,例如對應 react 來說就是 _dll_react
??? // 之所以在前面加上 _dll_ 是為了防止全局變量沖突
??? library: '_dll_[name]',
? },
? plugins: [
??? // 接入 DllPlugin
??? new DllPlugin({
????? // 動態鏈接庫的全局變量名稱,需要和 output.library 中保持一致
????? // 該字段的值也就是輸出的 manifest.json 文件 中 name 字段的值
????? // 例如 react.manifest.json 中就有 "name": "_dll_react"
????? name: '_dll_[name]',
????? // 描述動態鏈接庫的 manifest.json 文件輸出時的文件名稱
????? path: path.join(__dirname, 'dist', '[name].manifest.json'),
??? }),
? ],
};
使用動態鏈接庫文件
構建出的動態鏈接庫文件用于給其它地方使用,在這里也就是給執行入口使用。
用于輸出 main.js 的主 Webpack 配置文件內容如下:
const path = require('path');
const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
module.exports = {
? entry: {
??? // 定義入口 Chunk
??? main: './main.js'
? },
? output: {
??? // 輸出文件的名稱
??? filename: '[name].js',
??? // 輸出文件都放到 dist 目錄下
??? path: path.resolve(__dirname, 'dist'),
? },
? module: {
??? rules: [
????? {
??????? // 項目源碼使用了 ES6 和 JSX 語法,需要使用 babel-loader 轉換
??????? test: /\.js$/,
??????? use: ['babel-loader'],
??????? exclude: path.resolve(__dirname, 'node_modules'),
????? },
??? ]
? },
? plugins: [
??? // 告訴 Webpack 使用了哪些動態鏈接庫
??? new DllReferencePlugin({
????? // 描述 react 動態鏈接庫的文件內容
????? manifest: require('./dist/react.manifest.json'),
??? }),
??? new DllReferencePlugin({
????? // 描述 polyfill 動態鏈接庫的文件內容
????? manifest: require('./dist/polyfill.manifest.json'),
??? }),
? ],
? devtool: 'source-map'
};
??? 注意:在 webpack_dll.config.js 文件中,DllPlugin 中的 name 參數必須和 output.library 中保持一致。 原因在于 DllPlugin 中的 name 參數會影響輸出的 manifest.json 文件中 name 字段的值, 而在 webpack.config.js 文件中 DllReferencePlugin 會去 manifest.json 文件讀取 name 字段的值, 把值的內容作為在從全局變量中獲取動態鏈接庫中內容時的全局變量名。
執行構建
在修改好以上兩個 Webpack 配置文件后,需要重新執行構建。 重新執行構建時要注意的是需要先把動態鏈接庫相關的文件編譯出來,因為主 Webpack 配置文件中定義的 DllReferencePlugin 依賴這些文件。
執行構建時流程如下:
??? 如果動態鏈接庫相關的文件還沒有編譯出來,就需要先把它們編譯出來。方法是執行 webpack --config webpack_dll.config.js 命令。
??? 在確保動態鏈接庫存在時,才能正常的編譯出入口執行文件。方法是執行 webpack 命令。這時你會發現構建速度有了非常大的提升。
3、happyPack 插件
使用多進程 進行文件轉換操作,提高轉換效率。
場景:
文件特別多,時長不能忍時,嘗試。普通項目效果不明顯。
涉及插件:
HappyPack。
npm i -D happypack
分解工作 和 管理工作由插件負責
實例:
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HappyPack = require('happypack');
module.exports = {
? module: {
??? rules: [
????? {
??????? test: /\.js$/,
??????? // 把對 .js 文件的處理轉交給 id 為 babel 的 HappyPack 實例
??????? use: ['happypack/loader?id=babel'],
??????? // 排除 node_modules 目錄下的文件,node_modules 目錄下的文件都是采用的 ES5 語法,沒必要再通過 Babel 去轉換
??????? exclude: path.resolve(__dirname, 'node_modules'),
????? },
????? {
??????? // 把對 .css 文件的處理轉交給 id 為 css 的 HappyPack 實例
??????? test: /\.css$/,
??????? use: ExtractTextPlugin.extract({
????????? use: ['happypack/loader?id=css'],
??????? }),
????? },
??? ]
? },
? plugins: [
??? new HappyPack({
????? // 用唯一的標識符 id 來代表當前的 HappyPack 是用來處理一類特定的文件
????? id: 'babel',
????? // 如何處理 .js 文件,用法和 Loader 配置中一樣
????? loaders: ['babel-loader?cacheDirectory'],
????? // ... 其它配置項
??? }),
??? new HappyPack({
????? id: 'css',
????? // 如何處理 .css 文件,用法和 Loader 配置中一樣
????? loaders: ['css-loader'],
??? }),
??? new ExtractTextPlugin({
????? filename: `[name].css`,
??? }),
? ],
};
以上代碼有兩點重要的修改:
?? ?? 在 Loader 配置中,所有文件的處理都交給了 happypack/loader 去處理,使用緊跟其后的 querystring ?id=babel 去告訴 happypack/loader 去選擇哪個 HappyPack 實例去處理文件。
?? ?? 在 Plugin 配置中,新增了兩個 HappyPack 實例分別用于告訴 happypack/loader 去如何處理 .js 和 .css 文件。選項中的 id 屬性的值和上面 querystring 中的 ?id=babel 相對應,選項中的 loaders 屬性和 Loader 配置中一樣。
在實例化 HappyPack 插件的時候,除了可以傳入 id 和 loaders 兩個參數外,HappyPack 還支持如下參數:
?? ?? threads 代表開啟幾個子進程去處理這一類型的文件,默認是3個,類型必須是整數。
?? ?? verbose 是否允許 HappyPack 輸出日志,默認是 true。
?? ?? threadPool 代表共享進程池,即多個 HappyPack 實例都使用同一個共享進程池中的子進程去處理任務,以防止資源占用過多,相關代碼如下:
const HappyPack = require('happypack');
// 構造出共享進程池,進程池中包含5個子進程
const happyThreadPool = HappyPack.ThreadPool({ size: 5 });
module.exports = {
? plugins: [
??? new HappyPack({
????? // 用唯一的標識符 id 來代表當前的 HappyPack 是用來處理一類特定的文件
????? id: 'babel',
????? // 如何處理 .js 文件,用法和 Loader 配置中一樣
????? loaders: ['babel-loader?cacheDirectory'],
????? // 使用共享進程池中的子進程去處理任務
????? threadPool: happyThreadPool,
??? }),
??? new HappyPack({
????? id: 'css',
????? // 如何處理 .css 文件,用法和 Loader 配置中一樣
????? loaders: ['css-loader'],
????? // 使用共享進程池中的子進程去處理任務
????? threadPool: happyThreadPool,
??? }),
??? new ExtractTextPlugin({
????? filename: `[name].css`,
??? }),
? ],
};
4、使用 parallelUglifyPlugin
多進程 進行文件混淆壓縮。每個子進程還是通過內置的UglifyJS 去壓縮代碼。
場景:
普通可用,有提升。
涉及插件:
npm i -D webpack-parallel-uglify-plugin
實例:
const path = require('path');
const DefinePlugin = require('webpack/lib/DefinePlugin');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
module.exports = {
? plugins: [
??? // 使用 ParallelUglifyPlugin 并行壓縮輸出的 JS 代碼
??? new ParallelUglifyPlugin({
????? // 傳遞給 UglifyJS 的參數
????? uglifyJS: {
??????? output: {
????????? // 最緊湊的輸出
????????? beautify: false,
????????? // 刪除所有的注釋
????????? comments: false,
??????? },
??????? compress: {
????????? // 在UglifyJs刪除沒有用到的代碼時不輸出警告
????????? warnings: false,
????????? // 刪除所有的 `console` 語句,可以兼容ie瀏覽器
????????? drop_console: true,
????????? // 內嵌定義了但是只用到一次的變量
????????? collapse_vars: true,
????????? // 提取出出現多次但是沒有定義成變量去引用的靜態值
????????? reduce_vars: true,
??????? }
????? },
??? }),
? ],
};
在通過 new ParallelUglifyPlugin() 實例化時,支持以下參數:
?? ?? test:使用正則去匹配哪些文件需要被 ParallelUglifyPlugin 壓縮,默認是 /.js$/,也就是默認壓縮所有的 .js 文件。
?? ?? include:使用正則去命中需要被 ParallelUglifyPlugin 壓縮的文件。默認為 []。
?? ?? exclude:使用正則去命中不需要被 ParallelUglifyPlugin 壓縮的文件。默認為 []。
?? ?? cacheDir:緩存壓縮后的結果,下次遇到一樣的輸入時直接從緩存中獲取壓縮后的結果并返回。cacheDir 用于配置緩存存放的目錄路徑。默認不會緩存,想開啟緩存請設置一個目錄路徑。
?? ?? workerCount:開啟幾個子進程去并發的執行壓縮。默認是當前運行電腦的 CPU 核數減去1。
?? ?? sourceMap:是否輸出 Source Map,這會導致壓縮過程變慢。
?? ?? uglifyJS:用于壓縮 ES5 代碼時的配置,Object 類型,直接透傳給 UglifyJS 的參數。
?? ?? uglifyES:用于壓縮 ES6 代碼時的配置,Object 類型,直接透傳給 UglifyES 的參數
5、區分環境
process.env.NODE_ENV !== 'production' 中的 NODE_ENV 和 'production' 兩個值是社區的約定,通常使用這條判斷語句在區分開發環境和線上環境。
6、壓縮代碼
壓縮js:
最優配置如下(1版本可用)
const UglifyJSPlugin = require('webpack/lib/optimize/UglifyJsPlugin');
module.exports = {
? plugins: [
??? // 壓縮輸出的 JS 代碼
??? new UglifyJSPlugin({
????? compress: {
??????? // 在UglifyJs刪除沒有用到的代碼時不輸出警告
??????? warnings: false,
??????? // 刪除所有的 `console` 語句,可以兼容ie瀏覽器
??????? drop_console: true,
??????? // 內嵌定義了但是只用到一次的變量
??????? collapse_vars: true,
??????? // 提取出出現多次但是沒有定義成變量去引用的靜態值
??????? reduce_vars: true,
????? },
????? output: {
??????? // 最緊湊的輸出
??????? beautify: false,
??????? // 刪除所有的注釋
??????? comments: false,
????? }
??? }),
? ],
};
注意事項:
uglifyjsplugin 插件版本不一致,可能配置項也不一樣,注意區分。目前有1和2。vue-cli 目前依賴的是 1 版本。
壓縮ES6:
npm i -D uglifyjs-webpack-plugin@beta
場景:
支持es6環境的系統。如最新版Chrome等。暫時用處不大。
const UglifyESPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
? plugins: [
??? new UglifyESPlugin({
????? // 多嵌套了一層
????? uglifyOptions: {
??????? compress: {
????????? // 在UglifyJs刪除沒有用到的代碼時不輸出警告
????????? warnings: false,
????????? // 刪除所有的 `console` 語句,可以兼容ie瀏覽器
????????? drop_console: true,
????????? // 內嵌定義了但是只用到一次的變量
????????? collapse_vars: true,
????????? // 提取出出現多次但是沒有定義成變量去引用的靜態值
????????? reduce_vars: true,
??????? },
??????? output: {
????????? // 最緊湊的輸出
????????? beautify: false,
????????? // 刪除所有的注釋
????????? comments: false,
??????? }
????? }
??? })
? ]
}
壓縮css:
css-loader 選項 minimize 支持壓縮。可用性 待測試
use: ['css-loader?minimize']
7、Tree Shaking
去除無用代碼(沒有用到的)。得是 基于es6模塊化規范的,才會被搖除。
舉例:
配置:
1、關閉es6 模塊轉換功能,保留es6 語法。此時用不到 es6 轉換器。
{
? "presets": [
??? [
????? "env",
????? {
??????? "modules": false
????? }
??? ]
? ]
}
2、使用uglifyPlugin 插件來壓縮,或者 啟動 Webpack 時帶上 --optimize-minimize 參數
8、提取公共代碼
使用場景:
多個頁面時,每個頁面都是一個獨立的單頁應用。會有很多相同資源 被重復加載。
如何提取:
1、多頁面使用的技術棧一致的,此時將 基礎庫和 基礎樣式提取 作為基礎庫文件:base.js。
以react 舉例,所有頁面會依賴 react、react-dom等庫。
2、與業務相關的 通用js 可提取到 業務基礎文件:common.js。
場景:
作為基礎庫文件,基本不會變動;業務基礎文件 可能變動性稍微大一些,做到了 最優緩存。
webpck實現
所用插件:
CommonsChunkPlugin?? ?webpack4 里用他倆替代
?? ?optimization.splitChunks and optimization.runtimeChunk)
?? ?
?? ?來自 <https://blog.csdn.net/VhWfR2u02Q/article/details/79969250>
?? ?
示例-common提取:
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
new CommonsChunkPlugin({
? // 從哪些 Chunk 中提取
? chunks: ['a', 'b'],
? // 提取出的公共部分形成一個新的 Chunk,這個新 Chunk 的名稱
? name: 'common'
})
示例-base提取:
?? ?base.js
// 所有頁面都依賴的基礎庫
import 'react';
import 'react-dom';
// 所有頁面都使用的樣式
import './base.css';
?? ?base配置
module.exports = {
? entry: {
??? base: './base.js'
? },
};
?? ?從common中提取base
new CommonsChunkPlugin({
? // 從 common 和 base 兩個現成的 Chunk 中提取公共的部分
? chunks: ['common', 'base'],
? // 把公共的部分放到 base 中
? name: 'base'
})
示例-引用:
<script src="base.js"></script>
<script src="common.js"></script>
<script src="a.js"></script>
其他情形:
除了基礎庫 內容,一些常用內容提取到 common.js 里。用到了 插件選項
minChunks:指定的代碼塊中出現的最小次數。
假如 minChunks=2、chunks=['a','b','c','d'],任何一個文件只要在 ['a','b','c','d'] 中任意兩個以上的 Chunk 中都出現過,這個文件就會被提取出來
9、按需加載
支持異步加載。
10、prepack
求值器,編譯時 提前將結果就編譯進代碼里,而不是 運行時才求值。
涉及插件:
prepack-webpack-plugin?? ?目前只能運行在 webpack 4.0之上
11、scope hoisiting 作用域提升
文件內容合并,壓縮 優化。僅用于 es6 語法。
涉及插件:
webpack.optimize.ModuleConcatenationPlugin
最優配置:
module.exports = {
? resolve: {
??? // 針對 Npm 中的第三方模塊優先采用 jsnext:main 中指向的 ES6 模塊化語法的文件
??? mainFields: ['jsnext:main', 'browser', 'main']
? },
? plugins: [
??? // 開啟 Scope Hoisting
??? new ModuleConcatenationPlugin(),
? ],
};
12、輸出分析用以 特定優化
使用命令:
webpack --profile --json > stats.json
會在項目下生成 stats.json文件,里面包含了所有的 輸出信息。
可視化工具:
1、在線可視,非常全。?? ?http://webpack.github.io/analyse/
2、插件?? ?webpack-bundle-analyzer;全局安裝,使用:先生成 stats.json文件,然后在項目下使用命令webpack-bundle-analyzer stats.json
13、優化總結
側重優化開發體驗的配置文件 webpack.config.js:
const path = require('path'); const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin'); const {AutoWebPlugin} = require('web-webpack-plugin'); const HappyPack = require('happypack'); // 自動尋找 pages 目錄下的所有目錄,把每一個目錄看成一個單頁應用 const autoWebPlugin = new AutoWebPlugin('./src/pages', {// HTML 模版文件所在的文件路徑template: './template.html',// 提取出所有頁面公共的代碼 commonsChunk: {// 提取出公共代碼 Chunk 的名稱name: 'common',}, }); module.exports = {// AutoWebPlugin 會找為尋找到的所有單頁應用,生成對應的入口配置,// autoWebPlugin.entry 方法可以獲取到生成入口配置 entry: autoWebPlugin.entry({// 這里可以加入你額外需要的 Chunk 入口base: './src/base.js',}),output: {filename: '[name].js',},resolve: {// 使用絕對路徑指明第三方模塊存放的位置,以減少搜索步驟// 其中 __dirname 表示當前工作目錄,也就是項目根目錄modules: [path.resolve(__dirname, 'node_modules')],// 針對 Npm 中的第三方模塊優先采用 jsnext:main 中指向的 ES6 模塊化語法的文件,使用 Tree Shaking 優化// 只采用 main 字段作為入口文件描述字段,以減少搜索步驟mainFields: ['jsnext:main', 'main'],},module: {rules: [{// 如果項目源碼中只有 js 文件就不要寫成 /\.jsx?$/,提升正則表達式性能test: /\.js$/,// 使用 HappyPack 加速構建use: ['happypack/loader?id=babel'],// 只對項目根目錄下的 src 目錄中的文件采用 babel-loaderinclude: path.resolve(__dirname, 'src'),},{test: /\.js$/,use: ['happypack/loader?id=ui-component'],include: path.resolve(__dirname, 'src'),},{// 增加對 CSS 文件的支持test: /\.css$/,use: ['happypack/loader?id=css'],},]},plugins: [autoWebPlugin,// 使用 HappyPack 加速構建new HappyPack({id: 'babel',// babel-loader 支持緩存轉換出的結果,通過 cacheDirectory 選項開啟loaders: ['babel-loader?cacheDirectory'],}),new HappyPack({// UI 組件加載拆分id: 'ui-component',loaders: [{loader: 'ui-component-loader',options: {lib: 'antd',style: 'style/index.css',camel2: '-'}}],}),new HappyPack({id: 'css',// 如何處理 .css 文件,用法和 Loader 配置中一樣loaders: ['style-loader', 'css-loader'],}),// 4-11提取公共代碼new CommonsChunkPlugin({// 從 common 和 base 兩個現成的 Chunk 中提取公共的部分chunks: ['common', 'base'],// 把公共的部分放到 base 中name: 'base'}),],watchOptions: {// 4-5使用自動刷新:不監聽的 node_modules 目錄下的文件ignored: /node_modules/,} };?
側重優化輸出質量的配置文件 webpack-dist.config.js:
?
轉載于:https://www.cnblogs.com/fan-zha/p/10517252.html
總結
以上是生活随笔為你收集整理的webpack优化相关操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows系统MySQL安装配置
- 下一篇: 061_打印斐波那契数列(100以内)