通过将Angular库分为几部分来提高SPA性能

哈Ha! 我向您介绍Kevin Kreuzer的文章“通过将Angular库分成多个块来改善SPA性能”的翻译。


Angular是一个很好的框架。 我们都爱他<3。


同时使Angular成功和美观的因素之一是广泛的社区及其承载的价值。 有许多会议,博客,会议,当然还有Angular库。


多亏了Angular CLI,库现在很容易创建。 它们非常适合在多个应用程序之间共享代码。


由于它们可以在许多地方使用,因此性能至关重要。 性能不佳的库可能会使多个应用程序变慢!


前端具有不同类型的性能。 运行时-性能和初始负载。 在本文中,我们将重点关注初始负载。


通过为一家大型企业提供并支持各种库和用户界面框架,我遇到了一些不太明显的陷阱和解决方法。 我认为值得分享其中的一些。


“这是一个简单的库。它不会影响性能,对吗?”


让我们从将使用Angular CLI创建的简单库开始。 如果您从未创建过Angular库,那么阅读以下文章可能会有所帮助:


图片


一旦使用CLI配置了许多项目工作区,就可以开始添加代码。


该库称为howdy ,其唯一目的是用您的名字打招呼或告诉您当地时间。 它包含两个模块,每个模块都有一个。 一种模式欢迎,另一种说话时间。


图片


只是一个常规的Angular and Component模块,该模块在Input绑定的顶部具有一个name属性并显示它。


图片


HowdyTimeComponent负责使用第三方矩量库显示时间。


太好了! 我们的howdy准备好发布! 它是如此简单。 她将不会影响表现,对吗?


你好图书馆消费


现在我们有了一个howdy库! 不利用这一点是可耻的。 要使用howdy库,我们将使用Angular CLI创建一个新的SPA。


ng new greeting-app 

由于我们对性能感兴趣,因此我们还要安装称为webpack-bundle-analyzer的dev依赖


 npm i -D webpack-bundle-analyzer 

Webpack-bundle-analyzer允许您使用交互式,可伸缩的树图可视化webpack输出文件的大小。


分析我们的软件包的最好方法是将以下分析脚本添加到我们的package.json中


 "analyze": "ng build --prod --stats-json && webpack-bundle-analyzer ./dist/greeting-app/stats-es2015.json" 

如果我们运行此命令,Angular将进行生产构建,并输出stats-es2015.json ,然后将由webpack-bundle-anlyzer选择并呈现


图片


由于我们尚未编写任何代码,因此我们的主程序包主要由Angular组成。 我们还可以看到zone.js包含在我们的polyfill包中。


通常,我们的应用程序大小现在为207 KB


但是我们还没有包括我们的Howdy库! 让我们继续吧。


 npm i howdy 

我们安装了howdy库,因为我们想承载带有名称的问候语。 我们对时间的展示不感兴趣。 因此,我们将仅使用HowdyNameModule模块,而不包括HowdyTimeModule


图片


在这里需要注意的重要一点是,我们仅导入 HowdyNameModule 。 让我们再次运行分析脚本。


图片


哇! 太酷了! 我们从207 KB切换到511.15 KB。 大小增加了一倍以上。 真是...


一看就足以找到罪魁祸首。 瞬间是巨大的! 它带来了其主要的实现代码和所有区域设置。


当然,可以用其他包(例如date-fnsmoment-mini)替换一下moment 。 但是问题是不同的。 为什么他甚至在那里? 记住,我们只导入了HowdyNameModule ,而不是HodwyTimeModule 。 我以为发生树抖动时 ,只有未使用的模块会抖动吗? 这是怎么回事?


摇树不一定能消除一切


为使Tree Shaking发生,Angular构建启动了许多高级优化。 但是,尽管HowdyTimeModule不存在,但工具包中存在此刻
问题是当下的包装方式。 让我们快速浏览一下node_modules文件夹中的moment.js文件。


图片


由于moment可以在许多地方使用,例如Node JS后端,Angular应用程序或普通的JavaScript,因此它捆绑在UMD中,而不是作为ES模块。


链接的UMD库包装在IFFE函数中,这意味着无法使用ModuleConcatenation,程序集优化工具无法确定将使用此代码还是有副作用。


简而言之,此类模块可防止Angular启动更高级的优化工具包。


不幸的是,我们无法控制这一时刻的完成方式。 这是否意味着我们不得不忍受包装的庞大尺寸?


胜利的次要切入点


我们无法控制力矩的产生方式。 但是我们可以管理我们的图书馆。 确实,有一种方法可以防止这种情况。 次要切入点!


目前几乎所有Angular库都使用ng-packagr打包。 ng-packagr允许您将ng-package.jsonpublic-api结合使用,最终将成为应用程序的入口点。


顾名思义,其他入口点允许您为应用程序指定多个入口点。


听起来不错! 如何激活辅助入口点?


使用ng-packagr动态检测辅助入口点。 ng-packagrpackage.json文件主文件夹的子目录中查找package.json文件


好酷! 让我们通过添加以下文件来利用我们的howdy库中的辅助入口点。


图片


对于每个模块,我们添加了index.tspackage.jsonpublic_api.ts


  • index.ts在那里,仅指向public_api ,在导入期间很有用。
  • public_api导出模块中的所有模块和组件。
  • package.json包含特定的ng-packagr配置 。 在我们的例子中,这足以指定entryFile

Package.json也可能包含其他属性,例如cssUrl等。请注意,这些属性的范围仅是当前子项目。

如果现在运行程序集,我们将得到三个块。 howdy.jshowdy-src-lib-name.jshowdy-src-lib-time.js


Howdy-src-lib-name.js现在仅包含与HowdyNameModule相关的代码,而howdy-src-lib-time.js现在仅包含特定于HowdyTimeModule的代码。


但是,让我们看一下howdy.js


图片


howdy.js片段仍包含HowdyNameComponentHowdyTimeComponent 。 这意味着即使我们仅导入HowdyNameModule 我们也能把握时机


如果我们想用这种方法摆脱HowdyTimeModule ,我们需要使用深度导入。 所以我们不是从howdy.js导入,而是直接从howdy-src-lib-time.js导入。
不推荐什么! 大量进口是危险的,应始终避免!

我们如何解决这些问题? 即使使用标准导入,我们如何保证也可以删除HowdyTimeModule ? 好吧,我们需要建立一种创建howdy.js的方法


使用“路标”


这个想法是从howdy.js块中删除代码,而是让它充当一种“路标”“指针”,将您指向其他块。


因此,让我们仔细看一下src / public_api.ts


 /* * Public API Surface of howdy */ export * from './lib/name/howdy-name.component'; export * from './lib/name/howdy-name.module'; export * from './lib/time/howdy-time.component'; export * from './lib/time/howdy-time.module'; 

这些行负责在howdy.js块中包括名称时间在内的所有内容。 我们需要从howdydy.js中删除代码,并使其指向包含实现的其他片段。 让我们更改其内容。


 / * Public API Surface of howdy */ export * from 'howdy/src/lib/name'; export * from 'howdy/src/lib/time'; 

我们没有输出实际的实现,而是指示了各个部分的相对路径。 进行此更改后, howdy.js仅指向其他软件包,并且不包含任何“真实”代码。
让我们运行ng并分析dist文件夹。


图片


Howdy.js现在充当指向包含实现的片段的“路标”“指针”。 howdy-src-lib-name.js块仅包含名称文件夹中的代码, howdy-src-lib-time.js文件仅包含time文件夹中的代码。


用替代品完成包装


让我们更新欢迎应用程序中的howdy包,然后重新运行分析脚本。


图片


好酷 软件包大小现在为170.94 KB 。 比最初略高。 让我们看看Howdy模块在最终捆绑包中的外观。


图片


太好了! 这项调整使我们可以将占用包装尺寸的SPA保持在较小的水平。 SPA只满足他们的需求!


将辅助入口点与延迟加载结合使用时,它们非常好。 如果我们在延迟加载的模块中使用HowdyTimeModule ,则力矩 最终会在延迟加载的块中结束,而不是基本结束。

真实经验


上面的例子很简单。


但是,在现有公司项目中引入辅助入口点之后,一切都会有所不同。 您必须处理更大的复杂性,而ng-packagr的错误消息并不总是有用。


最可能的是,您将需要在tsconfig.json文件中配置一些导入路径或指定一些路径。 您还将遇到一个块中的模块,这些模块使用另一块中的模块。


但是请相信我,一旦您处理了这个负担,就值得了。


在广泛的公司环境中,我们发现包含我们提供的某些库之后,新创建的SPA的包装大小爆炸了。


在某个时候,它甚至达到了5 MB 。 每个SPA都会收到时刻@ swimlane /数据表以及他甚至没有使用的其他信息。 我们开始专注于优化这种包装尺寸。


我们从date-fns中删除了力矩 ,并开始使用辅助入口点。 目前,我们为新创建的SPA收到了662KB的主体单元,其中包括几个库。 这仍然很多,但是我们还没有完成。 优化尚未完成-我们可以进一步减少数据包的大小。


看到我们在哪里,来自哪里,这真是太酷了。


但是,尽管事实上在上面的示例中使用其他入口点非常容易,但是在更重要的项目中很难想象它们。


结论


在优化包装尺寸方面,Angular做得很棒。 尽管优化的组装步骤非常复杂,但它们无法撼动树木。


ESModules以外的格式打包的模块不能摇晃。


因此,作为库的创建者,当我们包含第三方库时,我们必须仔细监视库对包大小的影响。
我们无法控制第三方库的包装。 但是我们可以很好地控制图书馆的包装。


子部分为我们提供了一种很好的方式来分几部分交付我们的库。 这些零件可以在优化Angulars的装配过程中摇动(可摇树)。 通过这种方法,即使包装不正确的第三方库也只有在使用时才包含在最终包装中。

Source: https://habr.com/ru/post/zh-CN482646/


All Articles