-
速度分析
- webpack有時(shí)侯打包很慢,而我們?cè)陧?xiàng)目中可能用了很多的
plugin
和loader
,想知道那個(gè)環(huán)節(jié)慢脚线,下面這個(gè)插件可以計(jì)算plugin
和loader
的耗時(shí)
yarn add -D speed-measure-webpack-plugin
- webpack有時(shí)侯打包很慢,而我們?cè)陧?xiàng)目中可能用了很多的
-
體積分析
- 打包后的體積優(yōu)化是一個(gè)很重要的點(diǎn),譬如引入的一些第三方組件庫過大,這是就要考慮是否需要尋找替代品了愈诚。這里可以采用
webpack-bundle-analyzer
,它可以用交互式可縮放樹形圖顯示webpack
輸出文件的大小牛隅。
yarn add -D webpack-bundle-analyzer
- 安裝完后炕柔,配置
webpcak.config.js
倔叼,配置好后汗唱,yarn run dev
啟動(dòng)后,會(huì)默認(rèn)起一個(gè)端口號(hào)為8888的本地服務(wù)器丈攒。這樣就能看到每個(gè)模塊的的文件大小哩罪,進(jìn)行針對(duì)性的優(yōu)化授霸。
- 打包后的體積優(yōu)化是一個(gè)很重要的點(diǎn),譬如引入的一些第三方組件庫過大,這是就要考慮是否需要尋找替代品了愈诚。這里可以采用
-
多線程/多實(shí)例構(gòu)建
-
大家都知道
webpack
是運(yùn)行在node
環(huán)境中,二node
际插,是單線程的碘耳。webpack
的打包過程是io
密集和計(jì)算機(jī)密集型操作,如果能fork
框弛,多個(gè)進(jìn)程并行處理各個(gè)任務(wù)辛辨,將會(huì)有效縮短構(gòu)建時(shí)間。一般使用較多的兩個(gè)是thread-loader
和HappyPack
瑟枫。- thread-loader(官方推薦)
yarn add -D thread-loader
thread-loader
會(huì)將你的loader
放置在一個(gè)worker
池里面運(yùn)行斗搞,以達(dá)到多線程構(gòu)建。
注:放置在這個(gè)thread-loader
之后的loader會(huì)在一個(gè)單獨(dú)的worker
池(worker pool
)中運(yùn)行慷妙。- HappyPack(該插件作者不在維護(hù))
yarn add - D happypack
HappyPack可以讓W(xué)ebpack同一時(shí)間處理多個(gè)任務(wù)僻焚,發(fā)揮多喝CPU的能力,將任務(wù)分解給多個(gè)子進(jìn)程去并發(fā)的執(zhí)行膝擂,子進(jìn)程處理完成后虑啤,再把結(jié)果發(fā)送給主進(jìn)程。通過多進(jìn)程模型架馋,來加速代碼的構(gòu)建狞山。
-
-
多進(jìn)程并行壓縮代碼
- 通常我們?cè)陂_發(fā)環(huán)境,代碼構(gòu)建時(shí)間比較快叉寂,而構(gòu)建用于發(fā)布到線上的代碼時(shí)會(huì)添加壓縮代碼這一流程萍启,則會(huì)道濟(jì)計(jì)算量大耗時(shí)多
-
webpack
默認(rèn)提供了UglifyJS
插件來壓縮JS
代碼,但是使用的是單線程壓縮代碼屏鳍,也就是說多個(gè)js文件需要被壓縮伊约,它需要一個(gè)個(gè)文件進(jìn)行壓縮。所以說在正式環(huán)境打包壓縮代碼速度非常慢(因?yàn)閴嚎sJS
代碼需要先把代碼解析成用Object
抽象表示的AST
語法樹孕蝉,再應(yīng)用各種規(guī)則分析處理AST
屡律,導(dǎo)致這個(gè)過程耗時(shí)非常大)。所以我們要對(duì)壓縮代碼這里一步驟進(jìn)行優(yōu)化降淮,常用的做法是多進(jìn)程并行壓縮超埋,目前主要有三種主流的壓縮方案。paralle-uglify-plugin
uglifyjs-webpack-plugin
-
terser-webpack-plugin
-
parallel-uglify-plugin
上面介紹的HappyPack
的思想是使用多個(gè)子進(jìn)程去解析和編譯JS
,CSS
等佳鳖,這樣就可以并行處理多個(gè)子任務(wù)霍殴,多個(gè)子任務(wù)完成后,再將結(jié)果發(fā)到主進(jìn)程中系吩,有了這個(gè)思想后来庭,ParallelUglifyPlugin
插件就產(chǎn)生了。當(dāng)
webpack
有多個(gè)JS
文件需要輸出和壓縮時(shí)穿挨,原來會(huì)使用UglifyJS
去一個(gè)個(gè)壓縮并且輸出月弛,而ParallelUglifyPlugin
插件則會(huì)開啟多個(gè)子進(jìn)程肴盏,把對(duì)多個(gè)文件壓縮的工作分給多個(gè)子進(jìn)程去完成,但是每個(gè)子進(jìn)程還是通過UglifyJS
去壓縮代碼帽衙。并行壓縮可以顯著的提升效率yarn add -D webpack-parallel-uglify-plugin
注:
webpack-parallel-uglify-plugin
已不再維護(hù)菜皂,這里不推薦使用 -
uglifyjs-webpack-plugin
yarn add -D uglifyjs-webpack-plugin
注:其實(shí)它和上面的
parallel-uglify-plugin
類似,也可通過設(shè)置parallel: true
開啟多進(jìn)程壓縮厉萝。 terser-webpack-plugin
不知道你有沒有發(fā)現(xiàn):webpack4 已經(jīng)默認(rèn)支持 ES6語法的壓縮恍飘。而這離不開terser-webpack-plugin.
yarn add -D uglifyjs-webpack-plugin
注:其實(shí)它和上面的
parallel-uglify-plugin
類似,也可通過設(shè)置parallel: true
開啟多進(jìn)程壓縮谴垫。 -
-
預(yù)編譯資源模塊
- 什么是預(yù)編譯資源模塊章母?
在使用webpack
進(jìn)行打包時(shí)候,對(duì)于依賴的第三方庫翩剪,比如vue
胳施,vuex
等這些不會(huì)修改的依賴,我們可以讓它和我們自己編寫的代碼分開打包肢专,這樣做的好處是每次更改我本地代碼的文件的時(shí)候,webpack
只需要打包我項(xiàng)目本身的文件代碼焦辅,而不會(huì)再去編譯第三方庫博杖。那么第三方庫在第一次打包的時(shí)候只打包一次,以后只要我們不升級(jí)第三方包的時(shí)候筷登,那么webpack
就不會(huì)對(duì)這些庫去打包剃根,這樣的可以快速的提高打包的速度。其實(shí)也就是預(yù)編譯資源模塊前方。webpack
中狈醉,我們可以結(jié)合DllPlugin
和DllReferencePlugin
插件來實(shí)現(xiàn)。 - DllPlugin是什么惠险?
它能把第三方庫代碼分離開苗傅,并且每次文件更改的時(shí)候,它只會(huì)打包該項(xiàng)目自身的代碼班巩。所以打包速度會(huì)更快渣慕。DLLPlugin
插件是在一個(gè)額外獨(dú)立的webpack
設(shè)置中創(chuàng)建一個(gè)只有dll
的bundle
,也就是說我們?cè)陧?xiàng)目根目錄下除了有webpack.config.js
抱慌,還會(huì)新建一個(gè)webpack.dll.js
文件逊桦。webpack.dll.js
的作用是把所有的第三方庫依賴打包到一個(gè)bundle
的dll
文件里面,還會(huì)生成一個(gè)名為manifest.json
文件抑进。該manifest.json
的作用是用來讓DllReferencePlugin
映射到相關(guān)的依賴上去的强经。 - DllReferencePlugin又是什么?
這個(gè)插件是在webpack.config.js
中使用的寺渗,該插件的作用是把剛剛在webpack.dll.js
中打包生成的dll
文件引用到需要的預(yù)編譯的依賴上來匿情。什么意思呢兰迫?就是說在webpack.dll.js
中打包后比如會(huì)生成vendor.dll.js
文件和vendor-manifest.json
文件,vendor.dll.js
文件包含了所有的第三方庫文件码秉,vendor-manifest.json
文件會(huì)包含所有庫代碼的一個(gè)索引逮矛,當(dāng)在使用webpack.config.js
文件打包DllReferencePlugin
插件的時(shí)候,會(huì)使用該DllReferencePlugin
插件讀取vendor-manifest.json
文件转砖,看看是否有該第三方庫须鼎。vendor-manifest.json
文件就是一個(gè)第三方庫的映射而已。 - 這么在項(xiàng)目中使用
- 上面說了這么多府蔗,主要是為了方便大家對(duì)于預(yù)編譯資源模塊和
DllPlugin
和DllReferencePlugin
插件作用的理解晋控。先來看下完成的項(xiàng)目目錄結(jié)構(gòu),主要在兩塊配置,分別是webpack.dll.js
和webpack.config.js
(對(duì)應(yīng)這里我是webpack.base.js
)
image.png - webpack.dll.js
const path = require('path'); const webpack = require('webpack'); module.exports = { mode: 'production', entry: { vendors: ['lodash', 'jquery'], react: ['react', 'react-dom'] }, output: { filename: '[name].dll.js', path: path.resolve(__dirname, './dll'), library: '[name]' }, plugins: [ new webpack.DllPlugin({ name: '[name]', path: path.resolve(__dirname, './dll/[name].manifest.json') }) ] }
這里我拆了兩部分:
vendors
(存放了lodash
姓赤、jquery
等)和react
(存放了react
相關(guān)的庫赡译,react
、react-dom
等)- webpack.config.js(對(duì)應(yīng)我這里就是webpack.base.js)
const path = require("path"); const fs = require('fs'); // ... const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin'); const webpack = require('webpack'); const plugins = [ // ... ]; const files = fs.readdirSync(path.resolve(__dirname, './dll')); files.forEach(file => { if(/.*\.dll.js/.test(file)) { plugins.push(new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(__dirname, './dll', file) })) } if(/.*\.manifest.json/.test(file)) { plugins.push(new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, './dll', file) })) } }) module.exports = { entry: { main: "./src/index.js" }, module: { rules: [] }, plugins, output: { // publicPath: "./", path: path.resolve(__dirname, "dist") } }
由于上面我把第三方庫做了一個(gè)拆分不铆,所以對(duì)應(yīng)生成也就會(huì)是多個(gè)文件蝌焚,這里讀取了一下文件,做了一層遍歷誓斥。最后在
package.json
里面再添加一條腳本就可以了:"scripts": { "build:dll": "webpack --config ./webpack.dll.js", },
運(yùn)行
yarn build:dll
就會(huì)生成本小節(jié)開頭貼的那張項(xiàng)目結(jié)構(gòu)圖了~ - 什么是預(yù)編譯資源模塊章母?
-
利用緩存提升二次構(gòu)建速度
- 一般來說只洒,對(duì)于靜態(tài)資源,我們都希望瀏覽器能夠進(jìn)行緩存劳坑,那樣以后進(jìn)入頁面就可以直接使用緩存資源毕谴,頁面打開速度會(huì)顯著加快,既提高了用戶的體驗(yàn)也節(jié)省了寬帶資源距芬。當(dāng)然瀏覽器緩存方法有很多種涝开,這里只簡(jiǎn)單討論下在
webpack
中如何利用緩存來提升二次構(gòu)建速度。在webpack
中利用緩存一般有以下幾種思路:-
babel-loader
開啟緩存 - 使用
cache-loader
- 使用
hard-source-webpack-plugin
-
- babel-loader
-
babel-loader
在執(zhí)行的時(shí)候框仔,可能會(huì)產(chǎn)生一些運(yùn)行期間重復(fù)的公共文件舀武,造成代碼體積冗余,同時(shí)也會(huì)減慢編譯效率离斩∞忍辏可以加上cacheDirectory
參數(shù)開啟緩存:
{ test: /\.js$/, exclude: /node_modules/, use: [{ loader: "babel-loader", options: { cacheDirectory: true } }], },
-
- cache-loader
- 在一些性能開銷較大的
loader
之前添加此loader
,以將結(jié)果緩存到磁盤里捐腿。
yarn add -D cache-loader
- 使用
cache-loader
的配置很簡(jiǎn)單纵朋,放在其他loader
之前即可。修改Webpack
的配置如下:
注:請(qǐng)注意茄袖,保存和讀取這些緩存文件會(huì)有一些時(shí)間開銷操软,所以請(qǐng)只對(duì)性能開銷較大的// webpack.config.js module.exports = { module: { rules: [ { test: /\.ext$/, use: [ 'cache-loader', ...loaders ], include: path.resolve('src') } ] } }
loader
使用此loader
。 - 在一些性能開銷較大的
- hard-source-webpack-plugin
-
HardSourceWebpackPlugin
為模塊提供了中間緩存宪祥,緩存默認(rèn)的存放路徑是:node_modules/.cache/hard-source
聂薪。配置hard-source-webpack-plugin
后家乘,首次構(gòu)建時(shí)間并不會(huì)有太大的變化,但是從第二次開始藏澳,構(gòu)建時(shí)間大約可以減少 80%左右仁锯。yarn add -D hard-source-webpack-plugin
- 使用
// webpack.config.js var HardSourceWebpackPlugin = require('hard-source-webpack-plugin'); module.exports = { entry: // ... output: // ... plugins: [ new HardSourceWebpackPlugin() ] }
webpack5
中會(huì)內(nèi)置hard-source-webpack-plugin
。 -
- 一般來說只洒,對(duì)于靜態(tài)資源,我們都希望瀏覽器能夠進(jìn)行緩存劳坑,那樣以后進(jìn)入頁面就可以直接使用緩存資源毕谴,頁面打開速度會(huì)顯著加快,既提高了用戶的體驗(yàn)也節(jié)省了寬帶資源距芬。當(dāng)然瀏覽器緩存方法有很多種涝开,這里只簡(jiǎn)單討論下在
-
縮小構(gòu)建目標(biāo)
- 有時(shí)候我們的項(xiàng)目中會(huì)用到很多模塊翔悠,但有些模塊其實(shí)是不需要被解析的业崖。這時(shí)我們就可以通過縮小構(gòu)建目標(biāo)或者減少文件搜索范圍的方式來對(duì)構(gòu)建做適當(dāng)?shù)膬?yōu)化。
- 主要是exclude 與 include的使用:
- exclude: 不需要被解析的模塊
- include: 需要被解析的模塊
這里const path = require('path'); module.exports = { ... module: { rules: [ { test: /\.js$/, exclude: /node_modules/, // include: path.resolve('src'), use: ['babel-loader'] } ] }
babel-loader
就會(huì)排除對(duì)node_modules
下對(duì)應(yīng)js
的解析蓄愁,提升構(gòu)建速度双炕。
-
減少文件搜索范圍
- 這個(gè)主要是resolve相關(guān)的配置,用來設(shè)置模塊如何被解析撮抓。通過resolve的配置妇斤,可以幫助Webpack快速查找依賴,也可以替換對(duì)應(yīng)的依賴丹拯。
- resolve.modules:告訴
webpack
解析模塊時(shí)應(yīng)該搜索的目錄 - resolve.mainFields:當(dāng)從
npm
包中導(dǎo)入模塊時(shí)(例如站超,import * as React from 'react'
),此選項(xiàng)將 決定在package.json
中使用哪個(gè)字段導(dǎo)入模塊乖酬。根據(jù)webpack
配置中指定的target
不同死相,默認(rèn)值也會(huì)有所不同 - resolve.mainFiles:解析目錄時(shí)要使用的文件名,默認(rèn)是
index
- resolve.extensions:文件擴(kuò)展名
// webpack.config.js const path = require('path'); module.exports = { ... resolve: { alias: { react: path.resolve(__dirname, './node_modules/react/umd/react.production.min.js') }, //直接指定react搜索模塊剑刑,不設(shè)置默認(rèn)會(huì)一層層的搜尋 modules: [path.resolve(__dirname, 'node_modules')], //限定模塊路徑 extensions: ['.js'], //限定文件擴(kuò)展名 mainFields: ['main'] //限定模塊入口文件名 } ... }
- resolve.modules:告訴
- 這個(gè)主要是resolve相關(guān)的配置,用來設(shè)置模塊如何被解析撮抓。通過resolve的配置妇斤,可以幫助Webpack快速查找依賴,也可以替換對(duì)應(yīng)的依賴丹拯。
-
動(dòng)態(tài) Polyfill 服務(wù)
- 什么是
babel-polyfill
?
babel
只負(fù)責(zé)語法轉(zhuǎn)換,比如將ES6
的語法轉(zhuǎn)換成ES5
双肤。但如果有些對(duì)象施掏、方法,瀏覽器本身不支持茅糜,比如:- 全局對(duì)象:
Promise
七芭、WeakMap
等。 - 全局靜態(tài)函數(shù):
Array.from
蔑赘、Object.assign
等狸驳。 - 實(shí)例方法:比如
Array.prototype.includes
等。
此時(shí)缩赛,需要引入babel-polyfill
來模擬實(shí)現(xiàn)這些對(duì)象耙箍、方法。這種一般也稱為墊片
酥馍。
- 全局對(duì)象:
- 怎么使用
babel-polyfill
辩昆?- 使用也非常簡(jiǎn)單,在webpack.config.js文件作如下配置就可以了:
module.exports = { entry: ["@babel/polyfill", "./app/js"], };
- 使用也非常簡(jiǎn)單,在webpack.config.js文件作如下配置就可以了:
- 動(dòng)態(tài)
Polyfill
服務(wù)
image.png
每次打開頁面施无,瀏覽器都會(huì)向Polyfill Service發(fā)送請(qǐng)求辉词,Polyfill Service識(shí)別 User Agent,下發(fā)不同的 Polyfill猾骡,做到按需加載Polyfill的效果瑞躺。 - 怎么使用動(dòng)態(tài)
Polyfill
服務(wù)?//訪問url卓练,根據(jù)User Agent 直接返回瀏覽器所需的 polyfills https://polyfill.io/v3/polyfill.min.js
- 什么是
-
Scope Hoisting
什么是Scope Hoisting隘蝎?
Scope hoisting
直譯過來就是「作用域提升」。熟悉JavaScript
都應(yīng)該知道「函數(shù)提升」和「變量提升」襟企,JavaScript
會(huì)把函數(shù)和變量聲明提升到當(dāng)前作用域的頂部嘱么。「作用域提升」也類似于此顽悼,webpack
會(huì)把引入的js
文件“提升到”它的引入者頂部曼振。Scope Hoisting
可以讓Webpack
打包出來的代碼文件更小、運(yùn)行的更快蔚龙。-
啟用
Scope Hoisting
- 要在
Webpack
中使用Scope Hoisting
非常簡(jiǎn)單冰评,因?yàn)檫@是Webpack
內(nèi)置的功能,只需要配置一個(gè)插件木羹,相關(guān)代碼如下:
// webpack.config.js const webpack = require('webpack') module.exports = mode => { if (mode === 'production') { return {} } return { devtool: 'source-map', plugins: [new webpack.optimize.ModuleConcatenationPlugin()], } }
- 好處
- 代碼體積更小甲雅,因?yàn)楹瘮?shù)申明語句會(huì)產(chǎn)生大量代碼;
- 代碼在運(yùn)行時(shí)因?yàn)閯?chuàng)建的函數(shù)作用域更少了坑填,內(nèi)存開銷也隨之變小抛人。
Scope Hoisting
的實(shí)現(xiàn)原理其實(shí)很簡(jiǎn)單:分析出模塊之間的依賴關(guān)系,盡可能的把打散的模塊合并到一個(gè)函數(shù)中去脐瑰,但前提是不能造成代碼冗余妖枚。因此只有那些被引用了一次的模塊才能被合并。注意:由于
Scope Hoisting
需要分析出模塊之間的依賴關(guān)系苍在,因此源碼必須采用ES6
模塊化語句绝页,不然它將無法生效。 - 要在
webpack優(yōu)化策略
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
- 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來躬翁,“玉大人焦蘑,你說我怎么就攤上這事『蟹ⅲ” “怎么了例嘱?”我有些...
- 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)宁舰。 經(jīng)常有香客問我拼卵,道長(zhǎng),這世上最難降的妖魔是什么蛮艰? 我笑而不...
- 正文 為了忘掉前任腋腮,我火速辦了婚禮,結(jié)果婚禮上壤蚜,老公的妹妹穿的比我還像新娘即寡。我一直安慰自己,他們只是感情好袜刷,可當(dāng)我...
- 文/花漫 我一把揭開白布聪富。 她就那樣靜靜地躺著,像睡著了一般著蟹。 火紅的嫁衣襯著肌膚如雪墩蔓。 梳的紋絲不亂的頭發(fā)上,一...
- 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼嗽交!你這毒婦竟也來了卿嘲?” 一聲冷哼從身側(cè)響起,我...
- 序言:老撾萬榮一對(duì)情侶失蹤夫壁,失蹤者是張志新(化名)和其女友劉穎拾枣,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡梅肤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年司蔬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姨蝴。...
- 正文 年R本政府宣布浮梢,位于F島的核電站跛十,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏秕硝。R本人自食惡果不足惜芥映,卻給世界環(huán)境...
- 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缝裤。 院中可真熱鬧屏轰,春花似錦、人聲如沸憋飞。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽榛做。三九已至唁盏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間检眯,已是汗流浹背厘擂。 一陣腳步聲響...
- 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像避凝,于是被迫代替她去往敵國(guó)和親舞萄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...