webpack4.0打包優(yōu)化策略整理小結(jié)
本文介紹了webpack4.0打包優(yōu)化策略整理小結(jié),分享給大家,具體如下:
當(dāng)前依賴包的版本
1.優(yōu)化loader配置
1.1 縮小文件匹配范圍(include/exclude)
通過排除node_modules下的文件 從而縮小了loader加載搜索范圍 高概率命中文件
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/, // 排除不處理的目錄
include: path.resolve(__dirname, 'src') // 精確指定要處理的目錄
}
]
}
1.2 緩存loader的執(zhí)行結(jié)果(cacheDirectory)
cacheDirectory是loader的一個(gè)特定的選項(xiàng),默認(rèn)值是false。指定的目錄(use: 'babel-loader?cacheDirectory=cacheLoader')將用來緩存loader的執(zhí)行結(jié)果,減少webpack構(gòu)建時(shí)Babel重新編譯過程。如果設(shè)置一個(gè)空值(use: 'babel-loader?cacheDirectory') 或true(use: 'babel-loader?cacheDirectory=true') 將使用默認(rèn)的緩存目錄(node_modules/.cache/babel-loader),如果在任何根目錄下都沒有找到 node_modules 目錄,將會(huì)降級回退到操作系統(tǒng)默認(rèn)的臨時(shí)文件目錄。
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader?cacheDirectory', // 緩存loader執(zhí)行結(jié)果 發(fā)現(xiàn)打包速度已經(jīng)明顯提升了
exclude: /node_modules/,
include: path.resolve(__dirname, 'src')
}
]
}
2.resolve優(yōu)化配置
2.1 優(yōu)化模塊查找路徑 resolve.modules
Webpack的resolve.modules配置模塊庫(即 node_modules)所在的位置,在 js 里出現(xiàn) import 'vue' 這樣不是相對、也不是絕對路徑的寫法時(shí),會(huì)去 node_modules 目錄下找。但是默認(rèn)的配置,會(huì)采用向上遞歸搜索的方式去尋找,但通常項(xiàng)目目錄里只有一個(gè) node_modules,且是在項(xiàng)目根目錄,為了減少搜索范圍,可以直接寫明 node_modules 的全路徑;同樣,對于別名(alias)的配置,亦當(dāng)如此:

const path = require('path');
function resolve(dir) { // 轉(zhuǎn)換為絕對路徑
return path.join(__dirname, dir);
}
resolve: {
modules: [ // 優(yōu)化模塊查找路徑
path.resolve('src'),
path.resolve('node_modules') // 指定node_modules所在位置 當(dāng)你import 第三方模塊時(shí) 直接從這個(gè)路徑下搜索尋找
]
}
配置好src目錄所在位置后,由于util目錄是在src里面 所以可以用下面方式引入util中的工具函數(shù)
// main.js import dep1 from 'util/dep1'; import add from 'util/add';
2.2 resolve.alias 配置路徑別名
創(chuàng)建 import 或 require 的路徑別名,來確保模塊引入變得更簡單。配置項(xiàng)通過別名來把原導(dǎo)入路徑映射成一個(gè)新的導(dǎo)入路徑 此優(yōu)化方法會(huì)影響使用Tree-Shaking去除無效代碼
例如,一些位于 src/ 文件夾下的常用模塊:
alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/')
}
現(xiàn)在,替換「在導(dǎo)入時(shí)使用相對路徑」這種方式,就像這樣:
import Utility from '../../utilities/utility';
你可以這樣使用別名:
import Utility from 'Utilities/utility';
resolve: {
alias: { // 別名配置 通過別名配置 可以讓我們引用變的簡單
'vue$': 'vue/dist/vue.common.js', // $表示精確匹配
src: resolve('src') // 當(dāng)你在任何需要導(dǎo)入src下面的文件時(shí)可以 import moduleA from 'src/moduleA' src會(huì)被替換為resolve('src') 返回的絕對路徑 而不需要相對路徑形式導(dǎo)入
}
}
也可以在給定對象的鍵后的末尾添加 $,以表示精準(zhǔn)匹配:
alias: {
util$: resolve('src/util/add.js')
}
這將產(chǎn)生以下結(jié)果:
import Test1 from 'util'; // 精確匹配,所以 src/util/add.js 被解析和導(dǎo)入 import Test2 from 'util/dep1.js'; // 精確匹配,觸發(fā)普通解析 util/dep1.js
2.3resolve.extensions
當(dāng)引入模塊時(shí)不帶文件后綴 webpack會(huì)根據(jù)此配置自動(dòng)解析確定的文件后綴
- 后綴列表盡可能小
- 頻率最高的往前放
- 導(dǎo)出語句盡可能帶上后綴
resolve: {
extensions: ['.js', '.vue']
}
3.module.noParse
用了noParse的模塊將不會(huì)被loaders解析,所以當(dāng)我們使用的庫如果太大,并且其中不包含import require、define的調(diào)用,我們就可以使用這項(xiàng)配置來提升性能, 讓 Webpack 忽略對部分沒采用模塊化的文件的遞歸解析處理。
// 忽略對jquery lodash的進(jìn)行遞歸解析
module: {
// noParse: /jquery|lodash/
// 從 webpack 3.0.0 開始
noParse: function(content) {
return /jquery|lodash/.test(content)
}
}
4.HappyPack
HappyPack是讓webpack對loader的執(zhí)行過程,從單一進(jìn)程形式擴(kuò)展為多進(jìn)程模式,也就是將任務(wù)分解給多個(gè)子進(jìn)程去并發(fā)的執(zhí)行,子進(jìn)程處理完后再把結(jié)果發(fā)送給主進(jìn)程。從而加速代碼構(gòu)建 與 DLL動(dòng)態(tài)鏈接庫結(jié)合來使用更佳。
npm i happypack@next -D
webpack.config.js
const HappyPack = require('happypack');
const os = require('os'); // node 提供的系統(tǒng)操作模塊
// 根據(jù)我的系統(tǒng)的內(nèi)核數(shù)量 指定線程池個(gè)數(shù) 也可以其他數(shù)量
const happyThreadPool = HappyPack.ThreadPool({size: os.cpus().lenght})
module: {
rules: [
{
test: /\.js$/,
use: 'happypack/loader?id=babel',
exclude: /node_modules/,
include: path.resolve(__dirname, 'src')
}
]
},
plugins: [
new HappyPack({ // 基礎(chǔ)參數(shù)設(shè)置
id: 'babel', // 上面loader?后面指定的id
loaders: ['babel-loader?cacheDirectory'], // 實(shí)際匹配處理的loader
threadPool: happyThreadPool,
// cache: true // 已被棄用
verbose: true
});
]
happypack提供的loader,是對文件實(shí)際匹配的處理loader。這里happypack提供的loader與plugin的銜接匹配,則是通過id=happypack來完成。
npm run dev
5.DLL動(dòng)態(tài)鏈接庫
在一個(gè)動(dòng)態(tài)鏈接庫中可以包含其他模塊調(diào)用的函數(shù)和數(shù)據(jù),動(dòng)態(tài)鏈接庫只需被編譯一次,在之后的構(gòu)建過程中被動(dòng)態(tài)鏈接庫包含的模塊將不會(huì)被重新編譯,而是直接使用動(dòng)態(tài)鏈接庫中的代碼。
- 將web應(yīng)用依賴的基礎(chǔ)模塊抽離出來,打包到單獨(dú)的動(dòng)態(tài)鏈接庫中。一個(gè)鏈接庫可以包含多個(gè)模塊。
- 當(dāng)需要導(dǎo)入的模塊存在于動(dòng)態(tài)鏈接庫,模塊不會(huì)再次打包,而是去動(dòng)態(tài)鏈接庫中去獲取。
- 頁面依賴的所有動(dòng)態(tài)鏈接庫都需要被加載。
5.1 定義DLL配置
依賴的兩個(gè)內(nèi)置插件:DllPlugin 和 DllReferencePlugin
5.1.1 創(chuàng)建一個(gè)DLL配置文件webpack_dll.config.js
module.exports = {
entry: {
react: ['react', 'react-dom']
},
output: {
filename: '[name].dll.js', // 動(dòng)態(tài)鏈接庫輸出的文件名稱
path: path.join(__dirname, 'dist'), // 動(dòng)態(tài)鏈接庫輸出路徑
libraryTarget: 'var', // 鏈接庫(react.dll.js)輸出方式 默認(rèn)'var'形式賦給變量 b
library: '_dll_[name]_[hash]' // 全局變量名稱 導(dǎo)出庫將被以var的形式賦給這個(gè)全局變量 通過這個(gè)變量獲取到里面模塊
},
plugins: [
new webpack.DllPlugin({
// path 指定manifest文件的輸出路徑
path: path.join(__dirname, 'dist', '[name].manifest.json'),
name: '_dll_[name]_[hash]', // 和library 一致,輸出的manifest.json中的name值
})
]
}
5.1.2 output.libraryTarget 規(guī)定了以哪一種導(dǎo)出你的庫 默認(rèn)以全局變量形式 瀏覽器支持的形式

具體包括如下:
- "var" - 以直接變量輸出(默認(rèn)library方式) var Library = xxx (default)
- "this" - 通過設(shè)置this的屬性輸出 this["Library"] = xxx
- "commonjs" - 通過設(shè)置exports的屬性輸出 exports["Library"] = xxx
- "commonjs2" - 通過設(shè)置module.exports的屬性輸出 module.exports = xxx
- "amd" - 以amd方式輸出
- "umd" - 結(jié)合commonjs2/amd/root
5.1.3 打包生成動(dòng)態(tài)鏈接庫
webpack --config webpack_dll.config.js --mode production

在dist目錄下 多出react.dll.js 和 react.manifest.json
- react.dll.js 動(dòng)態(tài)鏈接庫 里面包含了 react和react-dom的內(nèi)容
- react.manifest.json 描述鏈接庫(react.dll)中的信息
5.2 在主配置文件中使用動(dòng)態(tài)鏈接庫文件
// webpack.config.js
const webpack = require('webpack');
plugins: [
// 當(dāng)我們需要使用動(dòng)態(tài)鏈接庫時(shí) 首先會(huì)找到manifest文件 得到name值記錄的全局變量名稱 然后找到動(dòng)態(tài)鏈接庫文件 進(jìn)行加載
new webpack.DllReferencePlugin({
manifest: require('./dist/react.manifest.json')
})
]
5.3 將動(dòng)態(tài)鏈接庫文件加載到頁面中
需要借助兩個(gè)webpack插件
html-webpack-plugin 產(chǎn)出html文件
html-webpack-include-assets-plugin 將js css資源添加到html中 擴(kuò)展html插件的功能
npm i html-webpack-plugin html-webpack-include-assets-plugin -D
配置webpack.config.js
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HtmlIncludeAssetsPlugin = require('html-webpack-include-assets-plugin');
pluings: [
new webpack.DllReferencePlugin({
manifest: require('./dist/react.manifest.json')
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.html')
}),
new HtmlIncludeAssetsPlugin({
assets: ['./react.dll.js'], // 添加的資源相對html的路徑
append: false // false 在其他資源的之前添加 true 在其他資源之后添加
});
]
此時(shí)react.dll.js和main.js被自動(dòng)引入到頁面中,并且dll文件在main.js之前加載
6.ParallelUglifyPlugin
這個(gè)插件可以幫助有很多入口點(diǎn)的項(xiàng)目加快構(gòu)建速度。把對JS文件的串行壓縮變?yōu)殚_啟多個(gè)子進(jìn)程并行進(jìn)行uglify。
cnpm i webpack-parallel-uglify-plugin -D
// webpck.config.js
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
plugins: [
new ParallelUglifyPlugin({
workerCount: 4,
uglifyJS: {
output: {
beautify: false, // 不需要格式化
comments: false // 保留注釋
},
compress: { // 壓縮
warnings: false, // 刪除無用代碼時(shí)不輸出警告
drop_console: true, // 刪除console語句
collapse_vars: true, // 內(nèi)嵌定義了但是只有用到一次的變量
reduce_vars: true // 提取出出現(xiàn)多次但是沒有定義成變量去引用的靜態(tài)值
}
}
});
]
執(zhí)行壓縮
webpack --mode production
7.Tree Shaking
剔除JavaScript中用不上的代碼。它依賴靜態(tài)的ES6模塊化語法,例如通過impot和export導(dǎo)入導(dǎo)出
commonJS模塊 與 es6模塊的區(qū)別
commonJS模塊:
1.動(dòng)態(tài)加載模塊 commonJS 是運(yùn)行時(shí)加載 能夠輕松實(shí)現(xiàn)懶加載,優(yōu)化用戶體驗(yàn)
2.加載整個(gè)模塊 commonJS模塊中,導(dǎo)出的是整個(gè)模塊
3.每個(gè)模塊皆為對象 commonJS模塊被視作一個(gè)對象
4.值拷貝 commonJS的模塊輸出和函數(shù)的值傳遞相似,都是值得拷貝
es6模塊
1.靜態(tài)解析 es6模塊時(shí) 編譯時(shí)加載 即在解析階段就確定輸出的模塊的依賴關(guān)系,所以es6模塊的import一般寫在被引入文件的開頭
2.模塊不是對象 在es6里,每個(gè)模塊并不會(huì)當(dāng)做一個(gè)對象看待
3.加載的不是整個(gè)模塊 在es6模塊中 一個(gè)模塊中有好幾個(gè)export導(dǎo)出
4.模塊的引用 es6模塊中,導(dǎo)出的并不是模塊的值得拷貝,而是這個(gè)模塊的引用
7.1 保留ES6模塊化語法
// .babelrc
{
"presets": [
[
"env", {
modules: false // 不要編譯ES6模塊
},
"react",
"stage-0"
]
]
}
7.2 執(zhí)行生產(chǎn)編譯 默認(rèn)已開啟Tree Shaking
webpack --mode production
什么是Tree Shaking?
有個(gè)funs.js 里面有兩個(gè)函數(shù)
// funs.js export const sub = () => 'hello webpack!'; export const mul = () => 'hello shaking!';
main.js 中依賴funs.js
// main.js
import {sub} from './funs.js'
sub();
在main.js只使用了里面的 sub函數(shù) 默認(rèn)情況下也會(huì)將funs.js里面其他沒有的函數(shù)也打包進(jìn)來, 如果開啟tree shaking 生產(chǎn)編譯時(shí)
webpack --mode production //此時(shí)funs.js中沒有被用到的代碼并沒打包進(jìn)來 而被剔除出去了
未完待續(xù)...
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
基于JS實(shí)現(xiàn)導(dǎo)航條flash導(dǎo)航條
flash導(dǎo)航條在網(wǎng)站建設(shè)中應(yīng)用比較廣泛,此種效果給瀏覽者帶來極好的視覺效果,非常棒,下面小編給大家介紹基于JS實(shí)現(xiàn)導(dǎo)航條flash導(dǎo)航條,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧2016-06-06
JS代碼實(shí)現(xiàn)百度地圖 畫圓 刪除標(biāo)注
這篇文章主要介紹了JS代碼實(shí)現(xiàn)百度地圖 畫圓 刪除標(biāo)注的相關(guān)資料,實(shí)現(xiàn)此功能的設(shè)計(jì)思路非常明確,代碼簡單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友參考下吧2016-10-10
javascript 獲取函數(shù)形參個(gè)數(shù)
本節(jié)主要介紹了javascript獲取函數(shù)形參個(gè)數(shù)的具體實(shí)現(xiàn),需要的朋友可以參考下2014-07-07
JavaScript設(shè)計(jì)模式之原型模式詳情
這篇文章主要介紹了JavaScript設(shè)計(jì)模式之原型模式詳情,原型的這種設(shè)計(jì)模式,是一種比較簡單的設(shè)計(jì)模式,由于初始化都做一些重復(fù)性的東西,造成的性能消2022-06-06
javascript設(shè)計(jì)模式之中介者模式Mediator
這篇文章主要介紹了javascript設(shè)計(jì)模式之中介者模式Mediator,需要的朋友可以參考下2014-12-12
Js類的靜態(tài)方法與實(shí)例方法區(qū)分及jQuery拓展的兩種方法
這篇文章主要介紹了Js類的靜態(tài)方法與實(shí)例方法區(qū)分及jQuery拓展的兩種方法 的相關(guān)資料,對靜態(tài)方法(Static)和實(shí)例方法(非Static)不太理解的朋友可以一起學(xué)習(xí)下2016-06-06
JavaScript for in錨點(diǎn)的動(dòng)態(tài)創(chuàng)建
主要包括for..in的使用,錨點(diǎn)的動(dòng)態(tài)創(chuàng)建,狀態(tài)欄文字效果2008-09-09

