树锯在树干和小块的树的例证,与附近的手锯。

负责任的JavaScript:第二部分

您和其余的开发团队热情地游说了公司的老化网站的总架构。您的请求被管理层听到 - 甚至到了C-Suite-谁 - 谁给了绿灯。毕业,你和团队开始使用设计,复制和IA团队。在长时间,您正在敲击新的代码。

文章继续如下

它以无辜的人开始了NPM安装这里和一个NPM安装在那里。然而,在你还没知道之前,你安装的产品依赖关系就像一个本科生,在一个keg站之后,没有任何照顾。

然后你推出了。

Unlike the aftermath of most copious boozings, the agony didn’t start the morning after., no. It came months later in the ghastly form of low-grade nausea and headache of product owners and middle management wondering why conversions and revenue were both down since the launch. It then hit a fever pitch when the CTO came back from a weekend at the cabin and wondered why the site loaded so slowly on their phone—if it indeed ever loaded at all.

每个人都很开心。现在一个是快乐的。欢迎使用您的第一个JavaScript挂起。

不是你的错#第2节

当你在与恶毒的宿醉搏斗时,“我告诉过你的”是当之无愧的,如果挑起斗志,谴责你甚至可以在一个国家里战斗。

谈到JavaScript Hoblovers时,戴机有很多责任。然而,指向手指是浪费时间。今天的网络景观要求我们比我们的竞争对手更快地迭代。这种压力意味着我们可能会利用任何可以尽可能富有成效的手段。means we’re more likely—but not necessarily doomed—to build apps with more overhead, and possibly use patterns that can hurt performance and accessibility.

Web开发并不容易。这是一个很长的游戏,我们很少在第一次尝试开始。然而,在网络上工作的最佳部分是我们没有在开始时让它完美。我们可以在事实之后进行改进,这就是第二部分this series在这里。完美是一种很长的方式。目前,让我们通过改进您的网站,呃,拍摄javascript宿醉的边缘,脚本uation在里面short term.

Round up the usual suspects#第3节

它似乎是死记硬背,但值得通过基本优化列表。对于大型开发团队而言,这并不罕见 - 特别是那些在许多存储库中工作或不使用优化的样品板来忽略它们的人。

摇动那棵树木#第4节

First, make sure your toolchain is configured to perform树摇晃。如果树摇动是新的,我写道去年的指导你可以咨询。简单的是树抖动是一个过程,其中Codebase中未使用的导出不会在生产捆绑包中打包。

Tree shaking is available out of the box with modern bundlers such as网客,卷起, orParcelGrunt或者吞咽- 那不是捆绑机,但是任务运行者- 不要为你做这件事。任务跑步者没有建立一个依赖关系图就像捆绑者一样。相反,它们对您使用任意数量的插件向它们提供的文件执行离散任务。任务运行者能够使用插件扩展以使用Bundler来处理JavaScript。如果以这种方式扩展任务跑步者对您有问题,则可能需要手动审核并删除未使用的代码。

For tree shaking to be effective, the following must be true:

  1. 应用程序逻辑和项目中安装的包必须以ES6模块。Tree shakingcommonjs.模块实际上是不可能的。
  2. 您的捆绑包必须将ES6模块转换为构建时的另一个模块格式。如果这发生在使用Babel的工具箱中,@babel/预设环境配置必须指定模块:false防止ES6代码转换为CommonJs。

在你的建造期间没有发生偶然的偶然偶尔树摇晃,可以帮助工作可能有所帮助。当然,其有效性在逐案的基础上变化。它还取决于您导入的模块是否介绍side effects,这可能会影响Bundler摇动未使用的出口的能力。

拆分该代码#第5节

你很有可能会用某种形式code splitting, but it’s worth re-evaluating how you’re doing it. No matterhow你是拆分代码,有两个问题总是值得问自己:

  1. 你是吗重复数据删除公共代码之间入口点?
  2. 你是懒惰加载你合理的所有功能吗?动态进口()?

These are important because reducing redundant code is essential to performance. Lazy loading functionality also improves performance by lowering the initial JavaScript footprint on a given page. On the redundancy front, using an analysis tool such as捆绑伙伴可以帮助您了解您是否有问题。

The Bundle Buddy utility demonstrating how much code is shared between bundles of JavaScript.
捆绑伙伴能够examine your webpack compilation statistics and determine how much code is shared between your bundles.

Where lazy loading is concerned, it can be a bit difficult to know where to start looking for opportunities. When I look for opportunities in existing projects, I’ll search for user interaction points throughout the codebase, such as click and keyboard events, and similar candidates. Any code that requires a user interaction to run is a potentially good candidate for dynamic进口()

当然,按需加载脚本会带来交互明显延迟的可能性,因为交互所需的脚本必须先下载。如果数据使用不值得关注,请考虑使用rel = prefetch.资源提示以低优先级加载此类脚本,不会对关键资源进行带宽来争斗。Support forrel = prefetch.很好,但如果没有支持的话,没有任何东西会破坏,因为这种浏览器会忽略标记他们不明白。

外部化第三方托管代码#第6节

理想情况下,您应该尽可能多地自主机站点的依赖项。如果你因为某种原因must从第三方加载依赖,mark them as externals在您的Bundler的配置中。未能这样做可能意味着您的网站的访问者将下载本地托管的代码the same code from a third party.

让我们看看一个假设的情况,这可能会伤害你:说你的网站从公共CDN加载Lodash。您还在您的本地开发项目中安装了Lodash。但是,如果您未将Lodash标记为外部,则您的生产代码将最终加载它的第三方副本此外to the bundled, locally hosted copy.

这可能seemlike common knowledge if you know your way around bundlers, but I’ve seen it get overlooked. It’s worth your time to check twice.

如果您不确信自己托管您的第三方依赖关系,那么考虑添加DNS预取,predonnect., or possibly even预装为他们提示。这样做可以降低你的网站互动时间如果JavaScript对呈现站点的内容至关重要速度指数

Smaller alternatives for less overhead#第7节

userland javascript.就像一个淫秽的糖果商店,我们作为开发者对大量的开源产品感到敬畏。框架和库允许我们扩展应用程序,以快速完成各种可能需要大量时间和精力的工作。

虽然我个人更倾向于在项目中积极地减少客户端框架和库的使用,但它们的价值是令人信服的。但是,我们当涉及到我们安装的内容时,有责任有点霍尔。当我们已经构建和发货取决于运行的已安装代码的旋转,我们已接受基线成本,只有该代码的维护者可以实际地址地址。正确的?

也许,但再一次,也许不是。这取决于所使用的依赖关系。例如,反应非常流行,但是罚款是一个超小型主要共享相同API并保留与许多React附加组件兼容性的替代方案。Luxon日期fns是更紧凑的替代品时刻.JS.,即不完全小小的

图书馆如Lodash提供许多有用的方法。然而,其中一些易于使用本地ES6。洛奇的袖珍的方法例如,可使用筛选array method还有更多可以替换不用太多的努力,也不需要拉大的实用程序库。

无论您的首选工具是什么,想法都是如此:做一些研究,看看是否有较小的替代方案,或者母语的功能可以做诀窍。您可能会惊讶于,您可能会努力降低应用程序的开销的努力。

Differentially serve your scripts#第8节

很有可能您在工具链中使用Babel将ES6源转换为可以在较旧浏览器上运行的代码。这是否意味着我们注定要为那些不需要它们的浏览器提供巨大的捆绑包,直到老浏览器完全消失?当然不是! 差异服务通过生成两个不同版本的ES6源帮助我们解决这一问题:

  • 捆绑一个,其中包含您的网站所需的所有变换和聚算盘在旧浏览器上工作。你现在可能已经在这个捆绑包装了。
  • 捆绑二,其中包含一点到没有因为它针对现代浏览器,所以它的转换和多重填充。这是你可能没有服务的包然而

实现这一点有点涉及。I’ve written a guide on one way you can do it,所以这里没有必要深度潜水。这是长短的,您可以修改构建配置以生成其他但较小版本的网站的JavaScript代码,并仅为现代浏览器提供服务。最好的部分是节省这些节省,而不会牺牲您已经提供的任何功能或功能。根据您的应用程序代码,节省可能非常重要。

A webpack-bundle-analyzer analysis of a project’s legacy bundle (left) versus one for a modern bundle (right).查看全尺寸的图像

Thesimplest pattern为了向各自的平台提供这些捆绑包是简短的。它还在现代浏览器中致敬:

//www.ashacc205.com/js/app.mjs.<! - 旧版浏览器加载此文件: - >//www.ashacc205.com/js/app.js

不幸的是,有一个带有这种模式的警告:像IE 11甚至相对现代的遗留浏览器,如边缘版本15到18-Will下载两个都bundles. If this is an acceptable trade-off for you, then worry no further.

另一方面,如果您关注的话,您将需要一个解决方法旧浏览器的性能含义下载两组捆绑包。这是一种使用脚本注入的潜在解决方案(而不是脚本tags above) to avoid double downloads on affected browsers:

var scriptel = document.createelement(“脚本”);if(“编制Scriptel中的”Nomodule“){//设置现代脚本scriptel.src =”/js/app.ms“;scriptel.type =“模块”;} else {//设置旧版脚本scriptel.src =“/js/app.js”;scriptel.defer = true;//默认情况下=“模块”缺陷,因此请将其设置为。} //注入!Document.Body.AppendChild(Scriptel);

此脚本infers如果浏览器支持the编制attribute在里面脚本element, it understandstype=“module”. 这确保了传统浏览器只获得遗留脚本,而现代浏览器只能获得现代脚本。但是,请注意,默认情况下动态注入的脚本异步加载,因此设置异步归因于如果依赖令至关重要。

转换较少#第9节

我不是在这里垃圾禁止宝贝。这是不可或缺的,而是浪漫,它增加了一个很多of extra stuff without your ever knowing. It pays to peek under the hood to see what it’s up to. Some minor changes in your coding habits can have a positive impact on what Babel spits out.

https://twitter.com/_developit/status/1110229993999777793

To wit:默认参数are a非常您可能已经使用的便捷ES6功能:

函数记录器(message,level=“log”){console[level](message);}

The thing to pay attention to here is the等级parameter, which has a default of “log.” This means if we want to invokeconsole.log.使用这个包装函数,我们不需要指定等级。Great, right? Except when Babel transforms this function, the output looks like this:

函数记录器(消息){var level=参数。length>1&&arguments[1]!==未定义?参数[1]:“日志”;控制台[级别](消息);}

这是一个例子,尽管我们的意图是最好的,开发人员的便利性却会适得其反。我们源中的少数字节现在已经转换成much我们的生产代码中更大。Uglization也无法对其做得很多,因为无法减少参数。哦,如果你思考rest参数might be a worthy antidote, Babel’s transforms for them are even bulkier:

//源函数记录器(... args){const [级别,消息] = args;控制台[级别](消息);} //babel输出函数记录器(for(var _len = arguments.length,args = new array(_len),_key = 0; _key <_len; _key ++){args [_key] =参数[_key];} const level = args [0],消息= args [1];控制台[级别](消息);}

更糟的是,Babel甚至为具有@babel/preset-env配置targeting modern browsers,这意味着您的差异服务JavaScript中的现代捆绑包也会受到影响!你呢可以变换宽松to soften the blow—and that’s a fine idea, as they’re often quite a bit smaller than their more spec-compliant counterparts—但如果您以后从您的构建管道中删除Babel,则启用松散的变换可能会导致问题

无论您是如何决定启用松散的变换,这里都是剪切Cruft Transpiled默认参数的方法:

// Babel将无法触摸此功能记录器(消息,级别){控制台[级别||“log”](消息);}

当然,默认参数不是只要feature to be wary of. For example,spread syntaxgets transformed, as doarrow functions还有一大堆其他的东西

如果您不想完全避免这些特性,您可以通过以下几种方法来减少这些特性的影响:

  1. 如果您正在编写库,请考虑使用@babel/runtime配合@ Babel / Plugin-Transform-Runtimeto deduplicate the helper functions Babel puts into your code.
  2. 对于应用中的聚算法功能,您可以选择性地包含它们@巴贝尔/聚填充via@ Babel / Preset-Env的UseBuiltins:“用法”选项。

这完全是我的意见,但我相信最好的选择是避免在为现代浏览器产生的捆绑包中完全转移。这并不总是可能的,特别是如果你使用JSX,必须转换全部浏览器,或者如果您使用的是不广泛支持的出血边缘语言功能。在后一种情况下,可能值得询问这些特性是否确实是提供良好用户体验的必要性(它们很少)。如果你得出结论,巴贝尔必须是你的工具链的一部分,那么它值得不时地在引擎盖下面偷看,以捕捉到巴贝尔可能正在做的,你可以改进的次优的东西。

改善不是种族#第10节

当你按摩你的寺庙想知道这个被动的JavaScript宿醉时会升起,明白当我们急于在那里赶出那里的东西时,就像我们可能会受到影响一样快。随着网络开发社区在竞争名称迭代迭代时,这是值得的减慢一点点。You’ll find that by doing so, you may not be iterating as fast as your competitors, but你的产品快点比他们的。

当您接受这些建议并将它们应用于Codebase时,知道进度不会自发地发生过夜。Web开发是一份工作。当我们思考并致力于长时间的工艺时,真正有影响力的工作就完成了。专注于稳定的改善。测量,测试,重复和您的网站的用户体验将改进,并且随着时间的推移,您将通过一点逐步获得更快的位。

Special thanks to杰森米勒for tech editing this piece. Jason is the creator and one of the many maintainers of罚款, a vastly smaller alternative to React with the same API. If you use Preact,请考虑通过公开集体支持起步

暂无评论

有话要说吗?

We have turned off comments, but you can see what folks had to say before we did so.

更多来自阿拉巴马州

Webwaste

在这摘录世界范围的废物中,格里麦戈尔恩检查了臃肿网站和不必要资产的环境影响。
工业

连接点

在创意文化的摘录中,Justin Daeer通过许多方式来浏览我们,其中一个组织的文化和设计工作会彼此发挥作用。
事业