博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JS/CSS体积减少了67%,我们是如何做到的?
阅读量:6955 次
发布时间:2019-06-27

本文共 4841 字,大约阅读时间需要 16 分钟。

Fider(Fider是一款开源的产品反馈搜集平台)团队一直致力于优化应用程序的体验。作为一个使用React构建的web应用程序,他们主要关注如何减少JS和CSS代码的体积。在这篇文章中,Fider团队分享了他们的学习体会。通过掌握这些概念和建议,你也可以针对自己的Web应用做出类似的优化。

\"\"

Fider的前端由React和Webpack构建,所以下面的主题非常适用于使用相同技术栈的前端团队,但是这些概念也可以应用于其他技术栈。Fider本身是一个开源项目,所以你实际上可以在项目地址上提交PR或者查看源码。

1. Bundle可视化分析器

webpack-bundle-analyzer是一个Webpack插件,它可以将你所使用的Bundle生成为一个可交互、可缩放Treemap。通过这个插件,你可以看到Bundle中哪个Module占用体积最大。

如果你不知道产生问题的根本原因,你怎么能解决它呢?

下图是webpack-bundle-analyzer生成结果的示例。

\"\"

在考虑Bundle体积前,先用webpack-bundle-analyzer进行分析,是一个很好的习惯。

2.长期缓存

Long term caching是指告知浏览器长时间缓存一个文件,比如3个月甚至1年。它之所以非常重要,是因为这项特性可以确保再次返回同一页面浏览的用户,不需要重复下载相同的JS/CSS文件。

浏览器是根据文件的完整路径名来缓存文件的。因此,假如你需要强制用户下载新版本的Bundle,首先必须重命名这个Bundle。幸运的是,Webpack提供了一个功能,可以根据一定规则来更新Bundle的名称,从而解决这个问题。

长久以来,Fider团队一直把Chunkhash作为Webpack编译输出的配置。但是在99%的需要Long Term Caching的场景中,最好的选择Contenthash。它将根据JS/CSS的内容生成散列,因此只有内容发生改变,Bundle的命名才会变化,从而尽可能的利用Long Term Caching的优势。

尽管这种技术不会减少Bundle的大小,但它确实有助于减少用户下载Bundle的时间。如果某个Bundle没有更改,用户就无需再次下载它。

更多相关信息可以从Webpack官方文档中获取:

3.公共Bundle

长久以来,许多团队在实践中,都会将所有的NPM Package构建成一个单独的Bundle。这与Long Term Caching相结合时非常有用。

相比我们应用自身的代码,NPM Package的改动往往要小得多。因此我们可以把这些包单独打包成一个Bundle,从而避免用户重复下载这些NPM Package。这些由Npm Package构建成的Bundle通常称为Vendor Bundle。

但是我们可以做的更好。如果你自己也有一部分很少更改的代码呢?

也许你已经有了一些在一段时间之前创建,并且迄今为止很少修改的组件,如Button,Grid,Toggle 等,那么这些组件就可以作为Common Bundle的候选。

你可以看看PR #636:

在这个PR中,我们就是把一些特定文件夹中的Module单独构建成了一个Common Bundle。

这将确保,除非我们更改这些公共组件,否则用户就不需要重新下载它们。

4.路由级别代码分割

Code Splitting是当前的热门话题。代码分割在过去不是一件简单的事,但是随着工具和框架的长足发展,现在进行Code Splitting相对而言简单得多。

一种常见的情况是,即使用户只是需要查看主页,应用程序也会推送一个包含所有JS/CSS的Bundle。这些JS/CSS可以用来渲染应用程序内的所有页面。因为我们已经推送了所有的JS/CSS代码,所以不用去关心用户是否会去访问站点内的其他页面,比如设置页面。Fider这么做已经很久了,但是现在我们已经做出了改变。

Code Splitting的思想是生成一个MainBundle以及多个更小的Bundle(通常一个路由对应一个)。我们唯一分发给所有用户的是Main Bundle,然后由Main Bundle异步下载当前页面渲染所需的Bundle。

这看起来很复杂,但是多亏了React和Webpack这对组合,Code Splitting已经不是什么难事了。对于React \u0026lt;= 16.5的用户,我们推荐来做Code Splitting。如果你已经在使用React 16.6,那么你可以使用response .lazy(),它是这个版本新增的特性。

5.按需载入外部依赖

通过使用Webpack Bundle Analyzer,我们注意到,在Vendor Bundle中引入了response-toastify。response-toastify是我们用于弹出消息提示的toaster库。在Fider中,我们很少弹出消息,那么把response-toastify直接引入Bundle就不是非常合理。假如用户并不需要显示弹出消息,那为什么要向他们推送这30kB的JavaScript代码呢?

这个问题和第四节的问题很相似,但是这里我们讨论不再是路由,这是在多个路由中使用的某个特性。你能在feature级别上进行Code Splitting吗?

是的,完全可以!

简而言之,你需要做的就是从静态导入切换到动态导入。

// beforeimport { toast } from \u0026quot;./toastify\u0026quot;;toast(\u0026quot;Hello World\u0026quot;);// afterimport(\u0026quot;./toastify\u0026quot;).then(module =\u0026gt; {  module.toast(\u0026quot;Hello World\u0026quot;);});

这样,Webpack会将toastify模块及其NPM依赖项单独打包。之后浏览器就会在需要toast的时候下载对应的Bundle。如果您已经配置了Long term caching,那么在第二个toaster调用时就无需再次下载。下面这个动图就显示了这个过程。

\"\"

你可以去PR #645上了解更多关于此功能的实现细节:

6.Font Awesome和“摇树”

Tree Shaking是只指从Module中导入你需要的东西,然后丢弃其余部分的过程。Webpack在生产模式下时默认启用这个功能。

通常使用Font Awesome的方法是导入一个外部字体文件和一个CSS,然后将字体上的每个字符或者图标映射形成一个CSS类。带来的结果是,即使我们只是使用图标A、B和C,用户也要下载这个外部字体和一个包含600多个图标定义的CSS。

幸运的是,我们找到了response-icons,这是一个NPM Pakage,包含了SVG格式的Font Awsome和其他图标包!通过它,我们可以将这些图标导出成一个ES Module格式的React组件。

然后你可以只导入你需要的图标,Webpack会从Bundle中删除其他图标。结果呢?不仅我们的CSS文件小了近68kB,同时我们也不需要下载外部字体了。这是Fider团队在减少CSS体积上的最大贡献。

可以在这个PR中看到更多实现细节 PR #631:

7.使用更小的NPM包

“NPM就像一个乐高商店,里面装满了积木,你可以随意挑选你喜欢的。你不需要为安装Package付费。但是这些Package会占用应用程序的字节大小,你的用户会为此买单。所以请做出明智的选择。”

在使用Bundle Analyzer时,我们发现,单单markdown-it就占用了Vendor Bundle体积的40%。于是我们就想在NPM上寻找一个可替代的Markdown解析器,它必须更小、更好维护且能满足我们的需要。

在安装任意NPM Package之前,我们使用bundlephobia.com来分析它的大小。我们只需要修改少量API,就能从markdown-it切换到markit。这最终帮助Fider减少了大约63KB的Vendor Bundle的体积。

可以在这个PR中看到更多实现细节PR #643:

在添加大体积的Package之前,请三思。你真的需要它吗?您的团队有更简单的实现吗?如果没有,你能否找到另一个Package,能用更少的代码完成相同的工作? 如果还是不行,你可以像第5节使用react-toastify一样,首先添加这个NPM Package,然后根据需要进行异步加载。

8.Main Bundle优化至关重要

假如你用路由级别的Code Splitting来创建一个应用,并且已经部署在生产环境上供用户使用了。假如现在你对Dashboard路由组件做出了更改,那么Webpack只会重新生成Dashboard 路由对应的那一份Bundle?是这样吗?

事实上并非如此。

如果应用程序代码发生某些改变,Webpack总是会重新生成Main Bundle。原因是Main Bundle是指向其他Bundle的指针。如果某个Bundle的散列值发生更改,那么Main Bundle也必须发生改变,以指向拥有新散列值的Dashboard Bundle。

因此,如果您的Main Bundle不仅包含这些指针,还包含许多常见组件,如按钮、切换、网格和选项卡。那么由于Main Bundle发生了变化,就会导致用户重复下载一些完全没有更改的东西。

因此,你首先需要使用Webpack Bundle Analyzer来看看你的Main Bundle中到底有些什么。然后再应用我们前面提到的一些技术来减少Main Bundle的体积。因为Main Bundle的优化至关重要。

9.TSLib(只针对TypeScript)

将TypeScript代码编译成ES5时,TypeScript编译器会向最终输出的JavaScript文件添加一些辅助函数。这个过程确保,即使你的TypeScript中使用了某些旧浏览器不支持类(Classes ) 和生成器(Generators)等ES6特性,最后生成的代码也可与之相兼容。

在这种情况下,一旦在某个TypeScript文件中使用非ES5特性的语法,那么最后生成的文件就会包含这些Helper函数。虽然单独来看,这些Helper函数体积非常小,但是假如这些文件数量很多,那么这个空间消耗就不能被忽略了。由于Webpack无法对这些函数进行Tree Shaking,因此最终会生成一个略大的、包含多份相同的代码的Bundle。

幸运的是,一个名为tslib的NPM Package可以解决这个问题,它包含了所有TypeScript所需的Helper函数。在npm install tslib -save安装之后,我们通过在tsconfig.json上设置importHelpers: true,来让编译器从tslib包导入Helper函数,而不是重复添加。

对于一个React应用程序来说,如果大部分组件是由类构成,那么体积缩减就会非常明显。

小结

你准备好迎接了吗?考虑一下你的应用程序的所有潜在用户,可能他们目前正试图用低成本设备、较慢的网络访问它。减少Bundle的大小将直接影响我们应用程序的性能,并让用户更好地访问它。

原文链接

译者简介

邱仁博,多年运营商商业分析、数据中心数据库方向工作经验,现任职于某地市图书馆信息技术部。日常关注国内外极客新闻、前后端技术。海外知识搬运工。

更多内容,可关注前端之巅(ID:frontshow)

\"\"

转载地址:http://hytil.baihongyu.com/

你可能感兴趣的文章
如何调试一个无法重现的错误?
查看>>
为了使用好Apache Flink,Yelp实现了一个连接算法
查看>>
定制你的敏捷方法:以结果为导向
查看>>
Swift 4进入最后阶段,ABI稳定性被推迟
查看>>
专访徐勇州:腾讯云全球化布局势如破竹,构建全球24小时无差别服务︱大咖访谈录...
查看>>
Visual Studio的Node.js插件:NTVS 1.0正式发布
查看>>
DevOps与持续交付实践
查看>>
前百度资深科学家技术分享:大规模机器学习与AutoML
查看>>
AI犯错谁之过?切勿盲目相信之
查看>>
Blazor正式成为Microsoft官方.NET 和WebAssembly项目
查看>>
IBM发布Open Liberty 18.0.0.4,支持MicroProfile 2.1和反应性扩展框架
查看>>
Adaptive Execution让Spark SQL更高效更好用
查看>>
基于Flink的超大规模在线实时反欺诈系统的建设与实践
查看>>
Studio 3T:MongoDB SQL探究
查看>>
Java SE 12扩展Switch语句/表达式完整指南
查看>>
MySQL曝设计缺陷,多家公司被窃取文件
查看>>
CRI Shimv2:一种 Kubernetes 集成容器运行时的新思路
查看>>
Checkly如何借助Terraform实现零宕机部署
查看>>
独家揭秘:微博深度学习平台如何支撑4亿用户愉快吃瓜?
查看>>
Go 1.12发布:改进了运行时性能以及模块支持
查看>>