世间自有公道,付出总有回报,说到不如做到,要做就做好,步步高!

【译】Angular 开发44条“军规”

Angular是一个很棒的框架,非常适合开发大型应用程序,以便在网络上获得最高性能。但有时我们作为开发人员最终会做一些导致应用程序性能不佳的事情。

在这篇文章中,我将Angular特定的最佳实践放在一起,以获得最佳的加载时间和运行时性能.此外,除了Angular之外,我还提到了Web开发中的一些一般最佳实践。

加载性能 (Load Time Performance)

1. AOT: 与在浏览器中完成编译的JIT编译相反,AOT在构建过程中编译了大部分代码(也称为脱机编译),从而减少了客户端浏览器上的大部分处理开销。使用angular-cli只需指定“aot”标志(如果存在prod标志,则不需要aot标志)并启用AOT。

2. Tree-shaking: 这是删除未使用的代码的过程,从而导致较小的构建大小。如果使用angular-cli,则默认启用Tree-Shaking。

3. Uglify: 这是使用各种代码转换(如修改,删除空格,删除注释等)来减少代码大小的过程。对于webpack,使用uglify插件和angular-cli指定“prod”标志来执行uglification过程。

4. Google Closure Compiler: 这是Google用于其产品的编译器,与Webpack uglify相比,通过执行更具侵略性的缩小,可以产生更小的捆绑包大小。即使Angular团队没有正式支持,您也可以使用闭包编译器来查看此实现。

**5. Webpack 4: ** 与Webpack 3相比,使用Webpack 4(及更高版本)进行angular-cli或自定义webpack构建会产生更小的构建大小.Webpack 4具有模式选项,允许您指定优化类型(生产或开发),而无需编写任何显式配置为您提供目标环境的最佳结果。此外,Webpack 4的构建时间比早期版本快得多(60%到98%),从而缩短了开发时间。

6. Prod flag: 对于产品,build时在angular-cli应用程序中指定“prod”标志。它将启用各种构建优化,如uglify,AOT,删除源映射,服务工作者(如果启用)生成更小的构建大小。

7. Build-optimizer flag: 如果您使用angular-cli,请确保为生产版本指定“build-optimizer”标志。这将禁用供应商块,并将导致更小的代码。

**8. Lazy loading: ** 延迟加载是一种机制,我们只加载当前所需的模块,而不是加载完整的应用程序,从而减少初始加载时间。简单来说,它意味着“不要加载你不需要的东西”。

9. Server side rendering: 在服务器上渲染应用程序的第一页(使用Node.js,.Net,PHP)并将其作为静态页面提供,可以实现即时渲染,从而大大提高了感知性能,速度和整体用户体验。您可以使用Angular Universal执行服务器端呈现。

10. Progressive Web App: PWA使您的应用程序加载速度更快,它为您的应用程序提供了离线功能,并提供接近原生的应用程序体验,从而大大提高了用户的整体感知性能。

11. Ivy Render Engine: Angular团队最近宣布了一个名为Ivy的新渲染引擎。与现有引擎相比,它的捆绑尺寸小得多,具有改进的调试体验。虽然它仍然没有生产就绪,但你仍然可以在你的应用程序中尝试它。您可以查看此ng-conf主题演讲以获取更多详细信息。

**12. Updating Angular and angular-cli: ** 定期更新Angular和angular-cli可以为您带来许多性能优化,错误修复,新功能,安全性等优势。

13. RxJS 6: RxJS 6使整个库更具tree-shakable 特性,从而减少了最终的束大小。但是,它有一些突破性的变化,例如操作符链接是不可能的,管道()函数(有助于更好的树摇动)被引入以添加运算符。他们还重命名了一些运营商。

14. Service worker cache: 如果您已将应用程序配置为支持Progressive Web App,请确保在PWA配置JSON中指定所有必需的静态资源。这些静态文件将缓存在客户端的浏览器中,使第二次加载速度更快。

15. Cache-control header: Cache-control header控制谁在什么条件下缓存响应以及多长时间,从而消除了缓存资源的网络往返的需要。

16. Third party packages: 查看您正在使用的第三方软件包,看看是否有更好和更小的替代方案,因为它可能会减少构建的最终大小。

17. Unnecessary use of third-party packages: 如果您包含第三方软件包只是为了实现一个小功能,可以使用JavaScript或Angular本地轻松完成,那么您将为您的应用程序添加不必要的大小开销,这可能很容易保存。例如,如果你只是为了做一个简单的对象过滤而包含Lodash那么完全没必要,因为你可以在JavaScript中本地做同样的事情。

18. defer attribute: 添加defer属性到script 标签中将推迟加载脚本(同步),直到文档未被解析,从而使您的网站更快地交互。对于angular-cli app,目前无法在构建期间自动添加,您必须在构建后手动执行此操作。

19. async attribute: 就像defer属性一样,异步延迟了脚本的加载,直到文档没有被解析,但没有遵守脚本的加载顺序。将其与谷歌分析脚本一起使用的最佳示例,该脚本通常独立于任何其他脚本。

20. Gzip compression: Gzip压缩可以大大减小响应主体的大小,从而提高Web应用程序的速度。确保在后端启用了gzip压缩。对于express.js,您可以添加压缩中间件。

22. Preload and Prefetch attributes: 这些属性有助于尽快加载静态资源,从而缩短第一次有意义的绘制时间。Preload和Prefetch几乎相似,唯一的区别是Preload资源具有更高的优先级。因此,对于初始呈现必不可少的资产使用Preload,并对站点加载后所需的资源使用Prefetch(将来需要跨页面)。您可以在此处详细了解这些属性

23. Updating Third Party Packages: 确保您定期更新第三方软件包。很多时候,较新的软件包可能包含许多性能改进,包括较小的体积和其他构建时性能优化(例如RxJS 6)。此外,通过定期更新软件包,您可以获得许多与错误修复,安全漏洞修复,与软件包兼容性相关的修复等相关的改进。

24. Compressing images: 在不损失大部分质量的情况下压缩图像是一个好主意,从而节省了通过网络传输的字节,从而缩短了构建时间。有许多工具可以实现这一目标。名为TinyPNG的VS Code扩展可用于压缩Jpeg和PNG图像,而不会损失太多质量。

25. Remove unused fonts: 删除的未使用的字体文件,可以减少网络请求。

26. Slow DNS and SSL: 有时您的DNS和SSL提供商可能是加载时间缓慢的原因。因此,请确保DNS和SSL正确配置。

运行时性能 (Run Time Performance)

27. Change Detection: 默认情况下,对每个异步事件,Angular通过对整个组件树执行更改检测来执行脏检查。对于大中型应用程序来说,这种脏检查可能会耗费大量计算量。您可以通过将“ChangeDetectionStrategy”设置为“OnPush”来大幅减少更改检测。 OnPush策略促进了不可变数据结构的使用。

28. Detach Change Detector: 我们可以完全从变化检测中分离组件,从而使开发人员能够通知Angular何时何地执行变更检测。

import {AfterViewInit, ChangeDetectorRef} from '@angular/core';
@Component(…)
class AppComponent implements AfterViewInit {

    constructor(private cdr: ChangeDetectorRef) {}
    ngAfterViewInit() {
        // We only want to detach the change detectors after change detection has been
        // performed for the first time
        this.cdr.detach();
    }

    update() {
        // Run change detection only for this component when update() method is called.
        this.cdr.detectChanges();
    }
}

29. Web Workers: 所有浏览器中的JavaScript实现都是单线程的,从而使整个应用程序在单个线程上运行。这种单线程执行大大降低了复杂应用程序的帧速率,因为UI绘制和JS执行都由同一个线程处理。由于Angular默认避免直接DOM操作,因此可以在单独的Web工作线程中运行整个Angular应用程序,从而保持主线程可以自由处理UI呈现。查看这篇文章,了解如何在web worker中运行angular-cli应用程序。但是,有许多npm包试图直接访问DOM,从而在工作进程中运行整个应用程序时产生问题。如果您只想在Web工作线程下运行一段代码,请查看此npm包

30. Webassembly: Webassembly是一种低级程序集,就像一种支持接近原生性能的语言。 WebAssembly旨在通过利用各种平台上可用的通用硬件功能以本机速度执行。您可以利用Webassembly使用Webassembly运行一些Web应用程序代码。看一下使用angular-cli和wasm创建的这个演示应用程序。但是,您应该意识到ism仍然是新的并且使用它有时可能会很棘手,因为现在它只有4种支持的数据类型(2个浮点和2个整数点)。到目前为止,对于大多数情况来说,与内联JS相比,使用它来执行一小段代码时,wasm的性能优势并不是那么大。正确评估您想要移动到哪个代码。

31. trackBy: 默认情况下,* ngFor通过引用标识对象唯一性。如果通过更新对象的内容来破坏对象引用,Angular将完全删除相关的DOM节点并再次重新创建它,即使所需的实际更改仅适用于DOM节点的一小部分。使用trackBy可以轻松解决此问题。

@Component({
    selector: 'app', 
    template: `<ul>
                    <li *ngFor="let item of items; trackBy: trackById">{{item.name}}</li>
               </ul>`
})
class AppComponent {
    Items = [
        {
            id: 1,
            name: 'item 1'
        }, {
            id: 2,
            name: 'item 2'
        },
        ...
    ];
    trackById(index, item) {
        return item.id;
    }
}

32. Pure Pipes: 在“@Pipe”装饰器中,您可以将“pure”标志指定为true。此标志表示管道不依赖于任何外部或全局状态,并且没有副作用。这使Angular能够缓存管道已被调用的所有输入参数的输出,从而允许重用值而不是重新计算。这可以导致在许多情况下执行的重复操作的大量减少,从而极大地改善了性能。

33. Avoid complex computations in the template: 避免在HTML模板中进行复杂的计算(在模板中调用一些组件方法),而是利用纯管道,从而利用Angular缓存,从而避免重复操作或者如果无法使用管道,请参阅机会预先计算值,然后直接绑定值,而不是在模板中调用组件方法。

34. enableProdMode: 调用“enableProdMode()”可避免Angular对变更检测执行其他检查。

35. AOT Compilation: AOT不仅可以提高构建时间性能,还可以提高应用程序的运行时性能。

36. Optimize Events: 较慢的DOM事件会阻止更改检测,直到事件未完成。例如,如果模板中的click事件在组件中处理,并且组件本身正在调用服务方法来处理它,则在从服务返回控件之前,更改检测将不会完成。如果您的服务花费更多时间来执行预期的操作,那么它最终会减慢更改检测的速度。查看优化逻辑的机会以改善持续时间,或者如果可能,尝试将服务逻辑移动到单独的Web工作线程或在需要时使用wasm。

37. Unsubscribing Observables: Observable可能会造成内存泄漏问题。所以最好在不再需要时取消订阅它们。但是,您不必取消订阅所有使用的可观察对象。在可观察完成之前销毁的组件内创建订阅时,需要显式取消订阅。

38. Observable share() operator: 如果您已在多个位置/组件订阅了observable,则即使数据重复,每个订阅也会尝试生成数据。我们可以使用“share()”运算符避免跨订阅处理重复数据。

import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
import {share} from 'rxjs/operators';
@Injectable()
export class AppService {
    data: Observable < any >;
    constructor(private http : HttpClient) {
        this.data = this.http.get<any> ('apiUrl').pipe(share());
    }
    getData() {
        return this.data;
    }
}

39. Progressive Web Apps: PWA不仅为您提供加载时间优化,还提供运行时优化,使您的应用程序更具响应性,交互性,快速,流畅的动画,离线支持等。

40. Updating Third Party Packages: 再次定期更新您的第三方软件包也可能会带来更好的运行时性能。

41. console.log(): 在生产代码中使用console.log()语句可能是一个坏主意,因为它会降低应用程序的性能,并且还使用console.log()记录对象会产生内存泄漏问题。当浏览器的控制台窗口打开时,console.log()执行速度会进一步降低很多倍,从而显着影响网站的性能。最好从生产代码中完全删除console.log()语句,或者至少具有特定于环境的条件日志记录。

42. Global Variables: 使用全局变量有许多缺点,其中之一就是内存泄漏。在重新加载窗口或关闭选项卡之前,不会清除全局范围中定义的变量,因此如果不打算在整个应用程序中使用全局变量,则会导致内存泄漏。如果由于某种原因你想拥有全局变量,那么在Angular中有更好的方法

43.Event listeners: 向DOM节点添加事件侦听器可能会造成内存泄漏问题。如果您忘记在指令的$ destroy事件中删除侦听器,它将保留对DOM节点的引用,即使它已从文档中删除。然后,DOM树将成为“分离的DOM树”并将泄漏。现代JS引擎能够为您找出大部分情况并删除侦听器,但更复杂的树层次结构甚至可以挑战最佳GC。

44. Bad Third Party Packages: 如果一个糟糕的第三方软件包有性能问题(内存泄漏,昂贵的js执行,安全性等)将最终影响您的应用程序的性能。因此,建议在使用前正确审查任何第三方包。

所以这些是编写高性能Angular应用程序时应该遵循的所有提示。希望这可以帮助您微调您的Angular应用程序。另外,请确保使用Chrome / Edge / Firefox JavaScript分析工具,堆快照比较,Chrome Lighthouse等不同工具对您的应用进行适当的性能分析和审核,以正确判断究竟是什么原因导致问题。

原文

posted @ 2019-07-26 19:02  疯狂秀才  阅读(527)  评论(0编辑  收藏  举报