webpack打包优化

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

定位体积大的模块

要想对打包体积进行优化,首先得找到体积大的模块,在这里我们可以使用webpack插件webpack-bundle-analyzer来查看整个项目的体积结构对比,它是以treemap的形式展现出来,很形象直观,还有一些具体的交互形式。既可以查看你项目中用到的所有依赖,也可以直观看到各个模块体积在整个项目中的占比。

该插件的使用方法可以直接通过npm install webpack-bundle-analyzer --save-dev安装,并在webpack的配置信息中的plugins: [new BundleAnalyzerPlugin()]中添加即可。对于vue-cli中的配置方式,默认是安装了该插件,但是没有启用,找到config/index.js文件在build下面会有bundleAnalyzerReport的配置,默认是process.env.npm_config_report,这里建议在package.json的scripts中添加一行"analyz": "npm_config_report=true npm run build",这样每次想启用该插件时只需要npm run analyze即可。

提取公共模块

对于webpack,它在模块化打包上有两点是其核心功能,一是它支持大量的模块类型,无论是TypeScript、CoffeeScript还是sass、stylus等语言它都支持,二是它可以通过配置来控制打包文件的粒度,这个下面会讲到。

在开发中我们往往会将所有的依赖库单独提取出来,而不与我们的项目代码混在一起,这时我们会用到一个webpack自带的插件CommonsChunkPlugin,从名字上就可以看出它是一个提取公共模块的插件。从它的文档中可以看出可以传入一个对象最为参数,在使用中常用的三个参数分别为:

name(names)

minChunks

chunks

name好理解,指的就是最后打包文件的名字,而如果使用的是names的话,传入的必须是一个字符串数组。minChunks如果传入的是一个数字的话,指的是如果该模块被其他模块的引用次数达到了这个数值的话该模块就会被打包。如果传入的是一个函数的话,其返回值必须是布尔类型来指明这个模块是否应该被打包进公共模块。而chunks则会指定一个字符串数组,如果设置了该参数,则打包的时候只会从其中指定的模块中提取公共子模块。

// webpack配置如下

module.exports = {

  entry: {  },      output: {   }, 

  plugins: [ 

    new CommonsChunkPlugin({ 

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

      minChunks:2

    }) 

  ] };

最终的打包结果是:jquery被打包到jquery.js,vue被打包到vue.js,common.js打包的是公共模块(chunk1和chunk2)。使用该插件打包时,会将满足minChunks的模块打包到name数组的第一个块里,然后数组后面的依次打包,首先从entry中找,如果没有则产生一个空块。name数组中最后一个块打包的是webpack的runtime代码,在使用的时候必须先加载该块。

new webpack.optimize.CommonsChunkPlugin({

  name: 'manifest',

  chunks: ['vendor']

}),

移除不必要的文件

在项目中我通过方法一定位到几处体积占用较大的库,其中一个便是moment.js这个日期处理库。对于一个日期处理的功能,为何这个库会占用如此大的体积,仔细查看发现当引用这个库的时候,所有的locale文件都被引入,而这些文件甚至在整个库的体积中占了大部分,因此当webpack打包时移除这部分内容会让打包文件的体积有所减小。

webpack自带的两个库可以实现这个功能:IgnorePluginContextReplacementPlugin

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的体积大致能缩减为原来的四分之一。

模块化引入

在项目中我使用了lodash这个很常用的工具库,然而在代码定位的时候发现这个库也占了不少的体积。仔细想想,我们在使用这类工具库的时候往往只使用到了其中的很少的一部分功能,但却把整个库都引入了。因此这里也可以进一步优化,只引用需要的部分。

import {chain, cloneDeep} from 'lodash';

// 可以改写为

import chain from 'lodash/chain';

import cloneDeep from 'lodash/cloneDeep';

这样就可以只打包我们需要的部分功能。

通过CDN引用

对于一些必要的库,但又无法对该库进行更好的体积优化的话,可以尝试通过外部引入的方式来减小打包文件的体积。采用该方法只需要在cdn站点找到需要引用的库的外部链接,以及对webpack进行简单配置即可。

// 在html中添加script引用

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

// 这里externals的key指的是使用时需要require的包名,value指的是该库通过script引入后在全局注册的变量名

externals: {

  jquery: 'jQuery'

}

// 使用方法

require('jquery')

通过DLLPlugin 和 DLLReferencePlugin 拆分文件

如果项目过大,打包的时间会相当的长,如果频繁更新上线则会不断对代码进行编译打包,浪费很多时间。这时我们便可以将那些不常更新的框架和库(如vue.js等)进行单独的编译打包,这样每次开发上线就只需要对我们的开发文件进行编译打包,这样可以极大地省去不必要的打包时间。而这种方法需要DLLPlugin和DLLReferencePlugin两个插件的配合来完成。

DLLPlugin

在使用这个插件时,我们需要单独创建一个配置文件,这里命名为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'

    })

  ]

}

这里要注意几点:

entry中写明所有要单独打包的模块

output的library属性可以将dll包暴露出来

DLLPlugin的配置中,path指明manifest.json文件的生成路径,name暴露出dll的函数名

运行该配置文件便可生成打包文件和manifest.json文件。

DLLReferencePlugin

对于该插件的配置,不需要像上面一样单独写配置文件,只需要在生产配置文件中添加如下代码:

new webpack.DllReferencePlugin({     

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

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

}),

然后运行webpack,发现打包的速度得到了极大地提升,也不用每次更新代码的时候重复编译打包这些依赖库了。

其他

对于webpack的打包优化我大致就总结了上面的一些方法,而为了让页面更快的加载,有更好的用户体验,我们并不只是从打包上优化,也可以有其他方面的优化,这里我也简单提一下我使用过的方法。

开启Gzip压缩

开启gzip压缩可以减少HTTP传输的数据量和时间,从而减少客户端请求的响应时间,由于降低了请求时间,页面的加载速度也会得到提升,会有更快的渲染速度,极大地改善了用户体验。由于现在基本上所有的主流浏览器都支持Gzip的压缩方式,只需要对服务器进行相关设置即可,这里就不具体讲如何配置服务器。

压缩混淆代码

我们平常也会对代码进行压缩混淆,可以通过UglifyJS等工具来对js代码进行压缩,同时可以去掉不必要的空格、注释、console信息等,也可以有效的减小代码体积。

总结

本文到这里就结束了,主要是对webpack的打包优化部分做了些讲解,当然能力和时间有限,只研究了部分方法,可能会有其他更多的优化方法,无论是从编译打包的体积还是速度上都能有更好的优化。接触了一段时间的webpack发现作为一个打包工具实在是过于复杂,无论从开始的官方文档还是到新的高级特性,都很难去完全掌握,还得需要自己不断去实践去深入研究才行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容

  • 写在开头 先说说为什么要写这篇文章, 最初的原因是组里的小朋友们看了webpack文档后, 表情都是这样的: (摘...
    Lefter阅读 5,285评论 4 31
  • ## 框架和库的区别?> 框架(framework):一套完整的软件设计架构和**解决方案**。> > 库(lib...
    Rui_bdad阅读 2,906评论 1 4
  • 目录第1章 webpack简介 11.1 webpack是什么? 11.2 官网地址 21.3 为什么使用 web...
    lemonzoey阅读 1,734评论 0 1
  • 明天就开学了,所以我赶时间写了这篇文章。我们是7月20多放假放到9月2日,9月3开学。这一个暑假发生了许多事情...
    许少青阅读 488评论 0 0
  • Librdkafka用纯C写成,作者在C API基础上作了C++的简单封装; 说到C, 自然里面离不开大量的指针操...
    扫帚的影子阅读 1,807评论 0 1