热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:非常全面的webpack之前端性能优化的实现分享

本文由编程笔记#小编为大家整理,主要介绍了非常全面的webpack之前端性能优化的实现分享相关的知识,希望对你有一定的参考价值。
本文由编程笔记#小编为大家整理,主要介绍了非常全面的webpack之前端性能优化的实现分享相关的知识,希望对你有一定的参考价值。






来源 | https://www.cnblogs.com/ssh-007/p/7944491.html


最近在用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'



},






非常全面的webpack之前端性能优化的实现分享


非常全面的webpack之前端性能优化的实现分享


 这就是优化前后的对比。


然后我们要到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' //日志级别。可以是'信息','警告','错误'或'沉默'。



})






非常全面的webpack之前端性能优化的实现分享


 


非常全面的webpack之前端性能优化的实现分享


我们会看到,没用externals和用了externals后所有的js中都不会出现类似echarts和lodash的库出现(就算你import一万次他都不会打包一次,厉害吧~~)。


对于externals再说两点——


1.externals中的key是import中使用的














import lodash from "_";



import echarts from "echarts";






2.externals中的value是window下调用的


非常全面的webpack之前端性能优化的实现分享


然后我们再来聊聊为什么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会是这样子。




非常全面的webpack之前端性能优化的实现分享


大家会看到我们这两个文件会有公共的部分,比如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对应页面,按需加载)


非常全面的webpack之前端性能优化的实现分享


 所以我们就把name改为names,就是上面那个配置。
因为使用这个插件,我们会把公共代码抽到第一个name中,把runtime放到最后一个name中,也就是我们所谓的“manifest”文件。


并且这个文件会比较小,通常都是2kb左右,所以build后会生成一个script标签,但这样的话就多了一个http请求,所以我们可以用另外一个插件(InlineManifestWebpackPlugin)将manifest.js内联进去。
就会长这样子


非常全面的webpack之前端性能优化的实现分享


再回到我们的CommonsChunkPlugin,现在我们随便改任何已存在的文件,vendor.js的hash都不会变,是的,貌似这就实现了持久化缓存。



但是当我们新增一个模块,并且在入口文件中import一下,我们的vendor就会跟main一起变。



很奇怪对吧,我们明明已经做了自己的源码跟第三方库分离,为什么vendor还会变(到现在应该没有任何一篇博客对此进行详细的说明)。
下面我就详细的给大家解释下我的看法,如果大家发现有不对的地方还请指正。


再解释为什么之前,我们先简单了解下webpack的打包规则。


webpack一个entry对应一个bundle,这个bundle包括入口文件和其依赖的模块。
其他按需加载的则打包成其他的bundle。



还有一个比较重要的文件时manifest,它是最先加载的,负责打包其他的bundle并按需加载和执行。


manifest是一个自执行函数,熟悉angular的同学看第一行应该很了解,因为anguar1.3版本的源码中启动就是angular.bootstrap,对,这里也是一样。



里面的modules变量就是对应模块函数,它是webpack处理的基本单位,就是说对应打包前的一个文件


非常全面的webpack之前端性能优化的实现分享


这是js源文件, 


非常全面的webpack之前端性能优化的实现分享


非常全面的webpack之前端性能优化的实现分享


这是打包后的文件,


非常全面的webpack之前端性能优化的实现分享


 所有的模块函数索引都是连续的(每个js文件生成一个trunkid!




),像这种 /* 4 */ 对应的就是js文件,他通过打包就变成了一个个trunkid,仔细看会看到咱们打包前js文件里的export和require依赖都会统一转换成webpack模块。
咱们说的webpackJsonp就是除manifest之外打包其他的文件的函数体。


简单说下main吧,这个图的trunkid是连续的,为了在一张图上显示,我截掉了trunk3-7.


非常全面的webpack之前端性能优化的实现分享


这里面一共有三个参数,第一个是我当前文件的trunkid,它是唯一标识符,就是指main的trunkid,第二个就是打包的所有文件的模块函数,第三个是我要立即执行的trunkid模块函数。


ok,介绍这些就足够了。


然后我们再回过头来看看为什么我们所谓的commonschunkPlugin会变。
刚才说过,有几个js就有几个trunkid。


所以当我们新加一个js并引入到main入口时,webpack再次打包,我的main文件会多一个模块函数,刚刚说过trunkid是依次递增的而且不会重复。



所以对应的vendor的id会+1,就是这么细微的变化导致hash变了。



非常全面的webpack之前端性能优化的实现分享


非常全面的webpack之前端性能优化的实现分享


非常全面的webpack之前端性能优化的实现分享


 大家仔细看,这两个vendor都是10272行,唯一的不同就是我要自执行这个vendor库,这里我引用的jquery,所以这个文件只有jquery,自执行肯定要有模块函数,trunkid+1,所以hash会变。



我们再好好回忆一下,其实这也说明了这个插件的意义,我就是要抽出公共的库,OK,这个插件做到了,但是因为webpack打包机制,不同文件生成不同turnkid,所以这是美中不足的一点。



再回想一下,我们一般是不会随便修改main.js的,所以从另一角度上来说这就是实现了持久化缓存。



但我如果就是想保持vendor的hash不变要怎么办呢?


非常全面的webpack之前端性能优化的实现分享


这段代码就可以实现,没错,如果你对vue-cli了如指掌,这就是vue-cli的官方demo,至于为什么可以,这个我后续会跟大家解释(实在是写不动了。


)。


最后再给大家介绍一个超级好用的东西,就是cdn。
我们现在的需求是想让图片走cdn,让js走线上路径,但官方的解释是通过修改config文件做cdn变化,这样做的话我的所有输出都会走cdn,那所有的ajax请求就跨域了呀。


非常全面的webpack之前端性能优化的实现分享


一开始我的解决方案是,在源文件中挨个替换,这样会比较慢,更重要的是,cdn图片也是有hash值的,当我以后替换图片时,还得重新改相应的hash。
有什么方法能让他自动去获取hash呢。


没错,我们需要在url-loader中单独配置cdn,做到js访问线上路径,静态资源使用cdn,两者互不影响。


非常全面的webpack之前端性能优化的实现分享


简单提醒一下,url-loader不能检测到js中的background,所以我们凡是在js中引用的地址,必须在外面先import这张图片,url-loader才会解析并打包。


本文完~









推荐阅读
  • Vue基础一、什么是Vue1.1概念Vue(读音vjuː,类似于view)是一套用于构建用户界面的渐进式JavaScript框架,与其它大型框架不 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
  • 用Vue实现的Demo商品管理效果图及实现代码
    本文介绍了一个使用Vue实现的Demo商品管理的效果图及实现代码。 ... [详细]
  • 本文讨论了将HashRouter改为Router后,页面全部变为空白页且没有报错的问题。作者提到了在实际部署中需要在服务端进行配置以避免刷新404的问题,并分享了route/index.js中hash模式的配置。文章还提到了在vueJs项目中遇到过类似的问题。 ... [详细]
  • 【前端工具】nodejs+npm+vue 安装(windows)
    预备先看看这几个是干嘛的,相互的关系是啥。nodejs是语言,类比到php。npm是个包管理,类比到composer。vue是个框架&# ... [详细]
  • Allegro总结:1.防焊层(SolderMask):又称绿油层,PCB非布线层,用于制成丝网印板,将不需要焊接的地方涂上防焊剂.在防焊层上预留的焊盘大小要比实际的焊盘大一些,其差值一般 ... [详细]
  • vue使用
    关键词: ... [详细]
  • Windows7企业版怎样存储安全新功能详解
    本文介绍了电脑公司发布的GHOST WIN7 SP1 X64 通用特别版 V2019.12,软件大小为5.71 GB,支持简体中文,属于国产软件,免费使用。文章还提到了用户评分和软件分类为Win7系统,运行环境为Windows。同时,文章还介绍了平台检测结果,无插件,通过了360、腾讯、金山和瑞星的检测。此外,文章还提到了本地下载文件大小为5.71 GB,需要先下载高速下载器才能进行高速下载。最后,文章详细解释了Windows7企业版的存储安全新功能。 ... [详细]
  • HSRP热备份路由器协议的应用及配置
    本文介绍了HSRP热备份路由器协议的应用及配置方法,包括设计目标、工作原理、配置命令等。通过HSRP协议,可以实现在主动路由器故障时自动切换到备份路由器,保证网络连通性。此外,还介绍了R1和R2路由器的配置方法以及Sw1和Sw2交换机的配置方法,最后还介绍了测试连通性和路由追踪的方法。 ... [详细]
  • vuecli安装错误的记录
    错误是由于想升级vue-cli引起的npmERR!Invalidtagname@vue-cli:TagsmaynothaveanycharactersthatencodeURICo ... [详细]
author-avatar
王永星2012
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有