webpack打包優(yōu)化

https://juejin.im/post/5b1e303b6fb9a01e605fd0b3

定位體積大的模塊

要想對打包體積進(jìn)行優(yōu)化,首先得找到體積大的模塊,在這里我們可以使用webpack插件webpack-bundle-analyzer來查看整個(gè)項(xiàng)目的體積結(jié)構(gòu)對比,它是以treemap的形式展現(xiàn)出來,很形象直觀,還有一些具體的交互形式。既可以查看你項(xiàng)目中用到的所有依賴,也可以直觀看到各個(gè)模塊體積在整個(gè)項(xiàng)目中的占比。

該插件的使用方法可以直接通過npm install webpack-bundle-analyzer --save-dev安裝,并在webpack的配置信息中的plugins: [new BundleAnalyzerPlugin()]中添加即可。對于vue-cli中的配置方式,默認(rèn)是安裝了該插件,但是沒有啟用,找到config/index.js文件在build下面會(huì)有bundleAnalyzerReport的配置,默認(rèn)是process.env.npm_config_report,這里建議在package.json的scripts中添加一行"analyz": "npm_config_report=true npm run build",這樣每次想啟用該插件時(shí)只需要npm run analyze即可。

提取公共模塊

對于webpack,它在模塊化打包上有兩點(diǎn)是其核心功能,一是它支持大量的模塊類型,無論是TypeScript、CoffeeScript還是sass、stylus等語言它都支持,二是它可以通過配置來控制打包文件的粒度,這個(gè)下面會(huì)講到。

在開發(fā)中我們往往會(huì)將所有的依賴庫單獨(dú)提取出來,而不與我們的項(xiàng)目代碼混在一起,這時(shí)我們會(huì)用到一個(gè)webpack自帶的插件CommonsChunkPlugin,從名字上就可以看出它是一個(gè)提取公共模塊的插件。從它的文檔中可以看出可以傳入一個(gè)對象最為參數(shù),在使用中常用的三個(gè)參數(shù)分別為:

name(names)

minChunks

chunks

name好理解,指的就是最后打包文件的名字,而如果使用的是names的話,傳入的必須是一個(gè)字符串?dāng)?shù)組。minChunks如果傳入的是一個(gè)數(shù)字的話,指的是如果該模塊被其他模塊的引用次數(shù)達(dá)到了這個(gè)數(shù)值的話該模塊就會(huì)被打包。如果傳入的是一個(gè)函數(shù)的話,其返回值必須是布爾類型來指明這個(gè)模塊是否應(yīng)該被打包進(jìn)公共模塊。而chunks則會(huì)指定一個(gè)字符串?dāng)?shù)組,如果設(shè)置了該參數(shù),則打包的時(shí)候只會(huì)從其中指定的模塊中提取公共子模塊。

// webpack配置如下

module.exports = {

? entry: {? },? ? ? output: {? ?},?

? plugins: [?

? ? new CommonsChunkPlugin({?

? ? ? name: ["common","jquery","vue","load"],?

? ? ? minChunks:2

? ? })?

? ] };

最終的打包結(jié)果是:jquery被打包到j(luò)query.js,vue被打包到vue.js,common.js打包的是公共模塊(chunk1和chunk2)。使用該插件打包時(shí),會(huì)將滿足minChunks的模塊打包到name數(shù)組的第一個(gè)塊里,然后數(shù)組后面的依次打包,首先從entry中找,如果沒有則產(chǎn)生一個(gè)空塊。name數(shù)組中最后一個(gè)塊打包的是webpack的runtime代碼,在使用的時(shí)候必須先加載該塊。

new webpack.optimize.CommonsChunkPlugin({

? name: 'manifest',

? chunks: ['vendor']

}),

移除不必要的文件

在項(xiàng)目中我通過方法一定位到幾處體積占用較大的庫,其中一個(gè)便是moment.js這個(gè)日期處理庫。對于一個(gè)日期處理的功能,為何這個(gè)庫會(huì)占用如此大的體積,仔細(xì)查看發(fā)現(xiàn)當(dāng)引用這個(gè)庫的時(shí)候,所有的locale文件都被引入,而這些文件甚至在整個(gè)庫的體積中占了大部分,因此當(dāng)webpack打包時(shí)移除這部分內(nèi)容會(huì)讓打包文件的體積有所減小。

webpack自帶的兩個(gè)庫可以實(shí)現(xiàn)這個(gè)功能:IgnorePlugin、ContextReplacementPlugin

IgnorePlugin的使用方法如下:

// 插件配置

plugins: [

? // 忽略moment.js中所有的locale文件

? new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),

],

// 使用方式

const moment = require('moment');

// 引入zh-cn locale文件

require('moment/locale/zh-cn');

moment.locale('zh-cn');

ContextReplacementPlugin的使用方法如下:

// 插件配置

plugins: [

? // 只加載locale zh-cn文件

? new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn/),

],

// 使用方式

const moment = require('moment');

moment.locale('zh-cn');

通過以上兩種方式,moment.js的體積大致能縮減為原來的四分之一。

模塊化引入

在項(xiàng)目中我使用了lodash這個(gè)很常用的工具庫,然而在代碼定位的時(shí)候發(fā)現(xiàn)這個(gè)庫也占了不少的體積。仔細(xì)想想,我們在使用這類工具庫的時(shí)候往往只使用到了其中的很少的一部分功能,但卻把整個(gè)庫都引入了。因此這里也可以進(jìn)一步優(yōu)化,只引用需要的部分。

import {chain, cloneDeep} from 'lodash';

// 可以改寫為

import chain from 'lodash/chain';

import cloneDeep from 'lodash/cloneDeep';

這樣就可以只打包我們需要的部分功能。

通過CDN引用

對于一些必要的庫,但又無法對該庫進(jìn)行更好的體積優(yōu)化的話,可以嘗試通過外部引入的方式來減小打包文件的體積。采用該方法只需要在cdn站點(diǎn)找到需要引用的庫的外部鏈接,以及對webpack進(jìn)行簡單配置即可。

// 在html中添加script引用

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

// 這里externals的key指的是使用時(shí)需要require的包名,value指的是該庫通過script引入后在全局注冊的變量名

externals: {

? jquery: 'jQuery'

}

// 使用方法

require('jquery')

通過DLLPlugin 和 DLLReferencePlugin 拆分文件

如果項(xiàng)目過大,打包的時(shí)間會(huì)相當(dāng)?shù)拈L,如果頻繁更新上線則會(huì)不斷對代碼進(jìn)行編譯打包,浪費(fèi)很多時(shí)間。這時(shí)我們便可以將那些不常更新的框架和庫(如vue.js等)進(jìn)行單獨(dú)的編譯打包,這樣每次開發(fā)上線就只需要對我們的開發(fā)文件進(jìn)行編譯打包,這樣可以極大地省去不必要的打包時(shí)間。而這種方法需要DLLPlugin和DLLReferencePlugin兩個(gè)插件的配合來完成。

DLLPlugin

在使用這個(gè)插件時(shí),我們需要單獨(dú)創(chuàng)建一個(gè)配置文件,這里命名為webpack.dll.config.js,配置如下:

module.exports = {

? entry: {

? ? lib: ['vue', 'vuex', 'vue-resource', 'vue-router']

? },

? output: {

? ? path: path.resolve(__dirname, '../dist', 'dll'),

? ? filename: '[name].js',

? ? publicPath: process.env.NODE_ENV === 'production'

? ? ? ? config.build.assetsPublicPath

? ? ? : config.dev.assetsPublicPath,

? ? library: '[name]_library'

? },

? plugins: [

? ? new webpack.DefinePlugin({

? ? ? 'process.env': '"production"'

? ? }),

? ? /**

? ? * path: manifest.json輸出文件路徑

? ? * name: dll對象名,跟output.library保持一致

? ? */

? ? new webpack.DllPlugin({

? ? ? context: __dirname,

? ? ? path: path.resolve(__dirname, '../dist/dll', 'lib.manifest.json'),

? ? ? name: '[name]_library'

? ? })

? ]

}

這里要注意幾點(diǎn):

entry中寫明所有要單獨(dú)打包的模塊

output的library屬性可以將dll包暴露出來

DLLPlugin的配置中,path指明manifest.json文件的生成路徑,name暴露出dll的函數(shù)名

運(yùn)行該配置文件便可生成打包文件和manifest.json文件。

DLLReferencePlugin

對于該插件的配置,不需要像上面一樣單獨(dú)寫配置文件,只需要在生產(chǎn)配置文件中添加如下代碼:

new webpack.DllReferencePlugin({? ? ?

? context: __dirname,? ? ? ? ? ? ? ? ? // 同dll配置的路徑保持一致

? manifest: require('../dist/dll/lib.manifest.json') // manifest的位置

}),

然后運(yùn)行webpack,發(fā)現(xiàn)打包的速度得到了極大地提升,也不用每次更新代碼的時(shí)候重復(fù)編譯打包這些依賴庫了。

其他

對于webpack的打包優(yōu)化我大致就總結(jié)了上面的一些方法,而為了讓頁面更快的加載,有更好的用戶體驗(yàn),我們并不只是從打包上優(yōu)化,也可以有其他方面的優(yōu)化,這里我也簡單提一下我使用過的方法。

開啟Gzip壓縮

開啟gzip壓縮可以減少HTTP傳輸?shù)臄?shù)據(jù)量和時(shí)間,從而減少客戶端請求的響應(yīng)時(shí)間,由于降低了請求時(shí)間,頁面的加載速度也會(huì)得到提升,會(huì)有更快的渲染速度,極大地改善了用戶體驗(yàn)。由于現(xiàn)在基本上所有的主流瀏覽器都支持Gzip的壓縮方式,只需要對服務(wù)器進(jìn)行相關(guān)設(shè)置即可,這里就不具體講如何配置服務(wù)器。

壓縮混淆代碼

我們平常也會(huì)對代碼進(jìn)行壓縮混淆,可以通過UglifyJS等工具來對js代碼進(jìn)行壓縮,同時(shí)可以去掉不必要的空格、注釋、console信息等,也可以有效的減小代碼體積。

總結(jié)

本文到這里就結(jié)束了,主要是對webpack的打包優(yōu)化部分做了些講解,當(dāng)然能力和時(shí)間有限,只研究了部分方法,可能會(huì)有其他更多的優(yōu)化方法,無論是從編譯打包的體積還是速度上都能有更好的優(yōu)化。接觸了一段時(shí)間的webpack發(fā)現(xiàn)作為一個(gè)打包工具實(shí)在是過于復(fù)雜,無論從開始的官方文檔還是到新的高級特性,都很難去完全掌握,還得需要自己不斷去實(shí)踐去深入研究才行。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 寫在開頭 先說說為什么要寫這篇文章, 最初的原因是組里的小朋友們看了webpack文檔后, 表情都是這樣的: (摘...
    Lefter閱讀 5,437評論 4 31
  • ## 框架和庫的區(qū)別?> 框架(framework):一套完整的軟件設(shè)計(jì)架構(gòu)和**解決方案**。> > 庫(lib...
    Rui_bdad閱讀 3,150評論 1 4
  • 目錄第1章 webpack簡介 11.1 webpack是什么? 11.2 官網(wǎng)地址 21.3 為什么使用 web...
    lemonzoey閱讀 1,816評論 0 1
  • 明天就開學(xué)了,所以我趕時(shí)間寫了這篇文章。我們是7月20多放假放到9月2日,9月3開學(xué)。這一個(gè)暑假發(fā)生了許多事情...
    許少青閱讀 539評論 0 0
  • Librdkafka用純C寫成,作者在C API基礎(chǔ)上作了C++的簡單封裝; 說到C, 自然里面離不開大量的指針操...
    掃帚的影子閱讀 1,907評論 0 1

友情鏈接更多精彩內(nèi)容