[转] webpack之前端性能优化(史上最全,不断更新中。。。)
最近在用webpack优化首屏加载性能,通过几种插件之后我们上线前后的速度快了一倍,在此就简单的分享下吧,先上个优化前后首屏渲染的对比图。

可以看到总下载时间从3800ms缩短到1600ms。
我们在用webpack时一般都会选择多入口文件吧,为的就是将自己的源码跟第三方库代码分离。这是之前的代码,

entry: {
entry: './src/main.js',
vendor: ['vue', 'vue-router', 'vuex', 'element-ui','echarts']
},
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
}

echarts非常大,所以打包时的vendor.js大概为1.2MB(经过gzip压缩之后),而且首页没有用到echarts,所以我之后使用了externals将第三方库以cdn的方式去引入,下面是优化过的代码

entry: {
entry: './src/main.js',
vendor: ['vue', 'vue-router', 'vuex', 'element-ui']
},
// 这里的output为base中的output,不是生产的output
output: {
path: config.build.assetsRoot,
filename: '[name].js',
libraryTarget: "umd",
publicPath: process.env.NODE_ENV === 'production' ?
config.build.assetsPublicPath : config.dev.assetsPublicPath
},
externals: {
echarts: 'echarts',
_: 'lodash'
},



这就是优化前后的对比。
然后我们要到html中以script标签的形式去引externals中的cdn。之后就可以在相应的文件中import了,他的好处是不管你在多少vue文件中引用多少次,他都不会打包到所有的trunk(这里的trunk'指的是按需加载,一会详细说明)中,这是用webpack-bundle-analyzer插件展示的效果。

var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
new BundleAnalyzerPlugin({
// 可以是`server`,`static`或`disabled`。
// 在`server`模式下,分析器将启动HTTP服务器来显示软件包报告。
// 在“静态”模式下,会生成带有报告的单个HTML文件。
// 在`disabled`模式下,你可以使用这个插件来将`generateStatsFile`设置为`true`来生成Webpack Stats JSON文件。
analyzerMode: 'server',
// 将在“服务器”模式下使用的主机启动HTTP服务器。
analyzerHost: '127.0.0.1',
// 将在“服务器”模式下使用的端口启动HTTP服务器。
analyzerPort: 8888,
// 路径捆绑,将在`static`模式下生成的报告文件。
// 相对于捆绑输出目录。
reportFilename: 'report.html',
// 模块大小默认显示在报告中。
// 应该是`stat`,`parsed`或者`gzip`中的一个。
// 有关更多信息,请参见“定义”一节。
defaultSizes: 'parsed',
// 在默认浏览器中自动打开报告
openAnalyzer: true,
// 如果为true,则Webpack Stats JSON文件将在bundle输出目录中生成
generateStatsFile: false,
// 如果`generateStatsFile`为`true`,将会生成Webpack Stats JSON文件的名字。
// 相对于捆绑输出目录。
statsFilename: 'stats.json',
// stats.toJson()方法的选项。
// 例如,您可以使用`source:false`选项排除统计文件中模块的来源。
// 在这里查看更多选项:https: //github.com/webpack/webpack/blob/webpack-1/lib/Stats.js#L21
statsOptions: null,
logLevel: 'info' //日志级别。可以是'信息','警告','错误'或'沉默'。
})



我们会看到,没用externals和用了externals后所有的js中都不会出现类似echarts和lodash的库出现(就算你import一万次他都不会打包一次,厉害吧~~)。
对于externals再说两点——
1.externals中的key是import中使用的
import lodash from "_";
import echarts from "echarts";
2.externals中的value是window下调用的

然后我们再来聊聊为什么output使用trunkhash不用trunk,这是为了持久化缓存。简单说下两者的区别——
trunk:每次build之后的版本,就是说所有的build之后的文件hash值一致,比如我只改了一个文件,最后所有的文件hash都会变,这样所有的文件都不会走cache,这样缓存就失去了意义。
trunkhash:根据每个文件生成不同的hash值,当文件变化时hash会改变且只会改变相应的文件
然后我们肯定是要用到CommonsChunkPlugin,这个插件是用来抽取公共代码的,基本上99%的配置都是长这样子或者类似这样子用两个不同的commonschunkPlugin,但这从某方面来说并没有实现真正意义上的持久化缓存,这个一会我会通过webpack打包原理来详细解释其中的原因。。。。。。
new webpack.optimize.CommonsChunkPlugin({
names: ['vendor','manifest']
})
在没用这个插件之前,我们的main.js和vendor.js会是这样子。。。

大家会看到我们这两个文件会有公共的部分,比如vue和element-ui,所以我们要抽取公共代码到vendor中,所以我们可以先这样配置
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
}),
但这样的话虽然可以提取公共代码,但我们会把runtime(webpack运行时的代码,一会在打包原理中会再次提到)也放到vendor中,这里面会维护一个trunk的文件列表,类似于这样,就是说我们改任意的代码,这个table里面的hash会变,所以vendor的hash也会变
,所以这没有实现真正的持久化缓存。这个hash table是按需缓存的打包出来的trunk包,一般都是通过require.ensure(就是vue-router中配置的page对应页面,按需加载)

所以我们就把name改为names,就是上面那个配置。因为使用这个插件,我们会把公共代码抽到第一个name中,把runtime放到最后一个name中,也就是我们所谓的“manifest”文件。
并且这个文件会比较小,通常都是2kb左右,所以build后会生成一个script标签,但这样的话就多了一个http请求,所以我们可以用另外一个插件(InlineManifestWebpackPlugin)将manifest.js内联进去。就会长这样子

再回到我们的CommonsChunkPlugin,现在我们随便改任何已存在的文件,vendor.js的hash都不会变,是的,貌似这就实现了持久化缓存。但是当我们新增一个模块,并且在入口文件中import一下,我们的vendor就会跟main一起变。很奇怪对吧,我们明明已经做了自己的源码跟第三方库分离,为什么vendor还会变(到现在应该没有任何一篇博客对此进行详细的说明)。下面我就详细的给大家解释下我的看法,如果大家发现有不对的地方还请指正。
再解释为什么之前,我们先简单了解下webpack的打包规则。
webpack一个entry对应一个bundle,这个bundle包括入口文件和其依赖的模块。其他按需加载的则打包成其他的bundle。还有一个比较重要的文件时manifest,它是最先加载的,负责打包其他的bundle并按需加载和执行。
manifest是一个自执行函数,熟悉angular的同学看第一行应该很了解,因为anguar1.3版本的源码中启动就是angular.bootstrap,对,这里也是一样。里面的modules变量就是对应模块函数,它是webpack处理的基本单位,就是说对应打包前的一个文件

这是js源文件,


这是打包后的文件,

所有的模块函数索引都是连续的(每个js文件生成一个trunkid!!!!!),像这种 /* 4 */ 对应的就是js文件,他通过打包就变成了一个个trunkid,仔细看会看到咱们打包前js文件里的export和require依赖都会统一转换成webpack模块。咱们说的webpackJsonp就是除manifest之外打包其他的文件的函数体。
简单说下main吧,这个图的trunkid是连续的,为了在一张图上显示,我截掉了trunk3-7.

这里面一共有三个参数,第一个是我当前文件的trunkid,它是唯一标识符,就是指main的trunkid,第二个就是打包的所有文件的模块函数,第三个是我要立即执行的trunkid模块函数。
ok,介绍这些就足够了。
然后我们再回过头来看看为什么我们所谓的commonschunkPlugin会变。刚才说过,有几个js就有几个trunkid。
所以当我们新加一个js并引入到main入口时,webpack再次打包,我的main文件会多一个模块函数,刚刚说过trunkid是依次递增的而且不会重复。所以对应的vendor的id会+1,就是这么细微的变化导致hash变了。



大家仔细看,这两个vendor都是10272行,唯一的不同就是我要自执行这个vendor库,这里我引用的jquery,所以这个文件只有jquery,自执行肯定要有模块函数,trunkid+1,所以hash会变。我们再好好回忆一下,其实这也说明了这个插件的意义,我就是要抽出公共的库,OK,这个插件做到了,但是因为webpack打包机制,不同文件生成不同turnkid,所以这是美中不足的一点。再回想一下,我们一般是不会随便修改main.js的,所以从另一角度上来说这就是实现了持久化缓存。但我如果就是想保持vendor的hash不变要怎么办呢?

这段代码就可以实现,没错,如果你对vue-cli了如指掌,这就是vue-cli的官方demo,至于为什么可以,这个我后续会跟大家解释(实在是写不动了。。。)。
最后再给大家介绍一个超级好用的东西,就是cdn。我们现在的需求是想让图片走cdn,让js走线上路径,但官方的解释是通过修改config文件做cdn变化,这样做的话我的所有输出都会走cdn,那所有的ajax请求就跨域了呀。

一开始我的解决方案是,在源文件中挨个替换,这样会比较慢,更重要的是,cdn图片也是有hash值的,当我以后替换图片时,还得重新改相应的hash。有什么方法能让他自动去获取hash呢。
没错,我们需要在url-loader中单独配置cdn,做到js访问线上路径,静态资源使用cdn,两者互不影响。

简单提醒一下,url-loader不能检测到js中的background,所以我们凡是在js中引用的地址,必须在外面先import这张图片,url-loader才会解析并打包。
今天就先到这里吧,改天继续。。。。。
[转] webpack之前端性能优化(史上最全,不断更新中。。。)的更多相关文章
- SDK 上报信息 史上最全 持续更新
SDK 上报信息 史上最全 持续更新 接入SDK总会遇到各种需求,有些SDK巴不得把玩家信息全部上报到他们服务器! 以下是我接SDK遇到的, 欢迎大家补全. 上报事件 注册(按道理这个应该是SDK的功 ...
- webpack之前端性能优化(史上最全,不断更新中。。。)
最近在用webpack优化首屏加载性能,通过几种插件之后我们上线前后的速度快了一倍,在此就简单的分享下吧,先上个优化前后首屏渲染的对比图. 可以看到总下载时间从3800ms缩短到1600ms. 我们在 ...
- 史上最全Java集合中List,Set以及Map等集合体系详解
一.概述 List , Set, Map都是接口,前两个继承至collection接口,Map为独立接口 Set下有HashSet,LinkedHashSet,TreeSet List下有ArrayL ...
- 史上最全Beyond Compare中文文档.CHM (根据官网整理编辑)
截图如下: 下载地址: 链接:https://pan.baidu.com/s/1Ml98_biT1leXxGLr9fqI_w 提取码:p8jg
- 前端性能优化:Add Expires headers
前端性能优化:Add Expires headers Expires headers 是什么? Expires headers:直接翻译是过期头.Expires headers 告诉浏览器是否应该从服 ...
- 【前端构建】WebPack实例与前端性能优化
计划把微信的文章也搬一份上来. 这篇主要介绍一下我在玩Webpack过程中的心得.通过实例介绍WebPack的安装,插件使用及加载策略.感受构建工具给前端优化工作带来的便利. 壹 | Fisrt 曾几 ...
- WebPack实例与前端性能优化
[前端构建]WebPack实例与前端性能优化 计划把微信的文章也搬一份上来. 这篇主要介绍一下我在玩Webpack过程中的心得.通过实例介绍WebPack的安装,插件使用及加载策略.感受构建工具给 ...
- 前端性能优化--图片处理(Css Sprites 与 base64)
前言: 近期研究着前端性能的优化方面的知识,并以博客记之.之前有相同系列的文章(前端性能优化--图片懒加载(lazyload image)),这次继续是关于图片的处理,css sprites 和 ba ...
- 前端性能优化之gzip
前言: 如果你是个前端开发人员,你肯定知道线上环境要把js,css,图片等压缩,尽量减少文件的大小,提升响应速度,特别是对移动端,这个非常重要.常用的前端性能优化方法有如下几种 一.减少http请求 ...
随机推荐
- Perf -- Linux下的系统性能调优工具,第 1 部分【转】
转自:https://www.ibm.com/developerworks/cn/linux/l-cn-perf1/ Perf 简介 Perf 是用来进行软件性能分析的工具. 通过它,应用程序可以利用 ...
- vim块编辑删除、插入、替换【转】
删除列 1.光标定位到要操作的地方. 2.CTRL+v 进入“可视 块”模式,选取这一列操作多少行. 3.d 删除. 插入列 插入操作的话知识稍有区别.例如我们在每一行前都插入"() & ...
- 通过全备+relaylog同步恢复被drop的库或表【转】
MySQL 中drop 等高危误操作后恢复方法 实验目的: 本次实验以恢复drop操作为例,使用不同方法进行误操作的数据恢复. 方法: 利用master同步 伪master+Binlog+同步 利用b ...
- flask与数据库连接相关操作
---恢复内容开始--- 首先要安装 flask-sqlalchemy 数据库连接设置 在flask-SQLAlchemy中,数据库使用URL指定,而且程序使用的数据库必须保存到flask配置对象的 ...
- MySQL之路 ——2、步履维艰的建表
1.首先,在windows下,不区分大小写.Linux下可能要区分,具体参考下面文章 mysql表名忽略大小写问题记录 2.用command line client 每句以分号结尾. 3.Navica ...
- C#代码处理前台html标签拼接
之前一篇文章是写,JavaScript处理特殊字符拼接时截断问题.最近在处理公司老软件兼容性升级时碰到的一个类似的问题,这次是后台拼接字符串,前台.aspx页面显示的.中间走了两次弯路,在此记录一下. ...
- Crypto++入门学习笔记(DES、AES、RSA、SHA-256)
最先附上 下载地址 背景(只是个人感想,技术上不对后面的内容构成知识性障碍,可以skip): 最近,基于某些原因和需要,笔者需要去了解一下Crypto++库,然后对一些数据进行一些加密解密的操作. 笔 ...
- 001_谈阿里核心业务监控平台SunFire的技术架构
<1>阿里全球运行指挥中心(GOC)的SunFire出品 <2>在2016年双11全球购物狂欢节中,天猫全天交易额1207亿元,前30分钟每秒交易峰值17.5万笔,每秒支付峰值 ...
- T-SQL删除存储过程
使用T-SQL脚本删除存储过程 语法: --声明数据库引用use 数据库名称;go --判断是否存在存储过程,如果存在则删除if exists(select * from sys.procedures ...
- 如何使用Jquery直接导入记事本的内容
直接上代码 <!DOCTYPE html> <html> <head> <title> </title> </head> < ...