如何提高 Angular2 应用程序的加载性能?

Posted

技术标签:

【中文标题】如何提高 Angular2 应用程序的加载性能?【英文标题】:How can I improve load performance of Angular2 apps? 【发布时间】:2017-04-15 03:36:38 【问题描述】:

Angular2 应用加载缓慢,如何提高加载性能?

我使用 Angular2,打字稿和 html5。

目前我的应用需要 4 秒才能加载。 我使用 Firebase 托管并使用 cloudflare。

我正在做的事情/信息:

我已压缩图像。 我缩小 css 我缩小了 js。 我在脚本中使用异步。 我的脚本在我的 . 脚本大约 700kb 我用谷歌速度测试,得到了 65% 我使用了我使用的库的缩小版本,例如引导等。 使用 systemjs。 这是我正在使用的种子应用程序:https://github.com/mgechev/angular-seed

流程:

应用加载时会显示蓝屏(这是引导 css),然后 4 秒后应用加载并运行得非常快。但加载需要 4 秒。似乎 systemjs 缩小到的 app.js 文件正在减慢整个应用程序的速度,并且显示视图的速度不够快。

这是我的网站速度测试: https://www.webpagetest.org/result/161206_F5_N87/

这是我的网站:

https://thepoolcover.co.uk/

如果您需要有关我的应用的更多信息以及我可以做的任何其他事情,请告诉我。

【问题讨论】:

您是如何构建应用程序的?用 AOT?你在使用延迟加载吗? 我使用 gulp 构建 prod 应用 捆绑或 AoT 编译或两者兼而有之,如果您使用 Webpack 2(beta 版),它会进行树抖动。我有一个 7MB 的开发负载和一个使用 Webpack 2 的 1MB(非 AoT)产品负载。 @GünterZöchbauer 我正在使用 systemjs,这个种子应用程序:github.com/mgechev/angular-seed 我的 app.js 文件似乎所有 js 都被缩小到大约 700kb 并且在编译和显示时真的很慢查看负载 除了您已经尝试过的方法和下面的所有出色答案之外,您可以做的一件相对简单的事情是从多个主机加载各种资源以提高并行度。浏览器通常会限制它们发出的并发请求的数量,不仅是总数,还包括主机。我认为默认情况下每个主机同时请求 4 个,但我相信现在更多了。但无论限制是多少,您都可以通过使用多个主机提供服务来有效地增加它(直到浏览器最大值)。 【参考方案1】:

单页应用程序在加载时通​​常需要更多时间,因为它一次加载所有必要的东西。

我也遇到了同样的问题,我的团队使用以下方法将我们的项目从 8 秒加载到 2 秒优化。

    延迟加载模块:延迟加载模块有助于减少启动时间。通过延迟加载,我们的应用程序不需要一次加载所有内容,它只需要加载用户在应用程序首次加载时期望看到的内容。延迟加载的模块只有在用户导航到他们的路线时才会被加载。 Angular2 在其最终版本 RC5 中引入了模块。 请参阅下面的分步指南。

    Aot 编译: 使用 AoT,浏览器会下载应用程序的预编译版本。浏览器加载可执行代码,因此它可以立即呈现应用程序,而无需等待先编译应用程序。

    它减少了负载大小: 如果应用程序已经编译,则无需下载 Angular 编译器。编译器大约是 Angular 本身的一半,因此省略它会大大减少应用程序的负载。欲了解更多信息,请参阅this。

    Webpack : Webpack 是一种流行的模块打包器,它是一种将应用程序源代码打包成方便的块并将该代码从服务器加载到浏览器中的工具。您可以使用 webpack 配置您的 Angular 2 Web 应用程序(请参阅this guide)。

    从 index.html 中删除脚本、样式表: 删除所有 index.html 中不需要的脚本和样式表。您可以通过调用服务在组件本身中动态加载这些脚本。

    创建一个文件 script.service.ts 可以根据需要为该组件加载任何脚本

\script.service.ts

import  Injectable  from '@angular/core';
declare var document: any;

@Injectable()
export class Script 

  loadScript(path: string) 
    //load script
    return new Promise((resolve, reject) => 
      let script = document.createElement('script');
      script.type = 'text/javascript';
      script.src = path;
      if (script.readyState)   //IE
        script.onreadystatechange = () => 
          if (script.readyState === "loaded" || script.readyState === "complete") 
            script.onreadystatechange = null;
            resolve( loaded: true, status: 'Loaded' );
          
        ;
       else   //Others
          script.onload = () => 
            resolve( loaded: true, status: 'Loaded' );
          ;
      ;
      script.onerror = (error: any) => resolve( loaded: false, status: 'Loaded' );
      document.getElementsByTagName('head')[0].appendChild(script);
    );
  

这只是一个动态加载脚本的示例代码,您可以根据需要自行定制和优化。 对于样式表,您应该使用 styleUrl 将其加载到组件中。

    使用浏览器缓存:当您使用浏览器缓存时,您的网页文件将存储在浏览器缓存中。对于重复访问者,您的页面将加载得更快,共享相同资源的其他页面也会如此。欲了解更多信息https://varvy.com/pagespeed/leverage-browser-caching.html

    最小化 app.component.ts 中的代码: 最小化 app.component.ts 中的代码,这些代码始终在应用加载或重新加载时运行。

    在应用初始化时设置数据:如果您在项目或组件中多次使用相同的 api 调用, 或者你依赖于多个组件中的相同数据,而不是多次调用 api 你可以做的是保存 数据作为应用程序初始化服务中的对象。该服务将在整个项目中充当单例,而您 无需调用 api 即可访问该数据。


逐步延迟加载模块

    模块化结构:我们必须将我们的应用程序分成单独的模块。例如,一个应用程序可能有一个用户端和一个管理端,每个端都有自己不同的组件和路由,所以我们将这两个端分成模块 admin.module.ts 和 user.module.ts。

    根模块:每个 Angular 应用程序都有一个根模块类。按照惯例,它是一个名为 AppModule 的类,位于一个名为 app.module.ts 的文件中,该模块将导入上述两个模块以及用于引导的 AppComponent。您还可以根据需要声明多个组件。 app.module.ts 中的示例代码:

\app.module.ts

import  NgModule  from '@angular/core';
import  UserModule  from './user/user.module';
import  AdminModule  from './admin/admin.module';
import  AppComponent  from './app.component';
import  LoginComponent  from './login.component';

@NgModule(
  imports: [UserModule, AdminModule],
  declarations: [AppComponent, LoginComponent],
  bootstrap: [AppComponent]
)
export class AppModule  
    路线:现在您可以在路线中指定如下内容

\app.router.ts

import  ModuleWithProviders  from '@angular/core';
import  Routes, RouterModule  from '@angular/router';
import  LoginComponent  from './login.component';

const routes: Routes = [
   path: 'login', component: 'LoginComponent' , //eager loaded
   path: 'admin', loadChildren: './admin/admin.module#AdminModule' , // Lazy loaded module
   path: 'user', loadChildren: './user/user.module#UserModule'   //lazy loaded module
];

现在当应用程序加载时,它只会加载 LoginComponent 和 AppComponent 代码。这些模块只会在我们访问 /admin 或 /user 路由时加载。因此,它会减少加载到浏览器中的有效负载大小,从而实现快速加载。

    嵌套模块: 就像 app.module 一样,每个模块都有自己的一组组件和路由。随着您的项目变得越来越大,将模块嵌套在模块内是优化的最佳方式,因为我们可以在需要时延迟加载这些模块。

请注意

以上代码仅供说明,请参考完整示例 https://angular-2-training-book.rangle.io/handout/modules/lazy-loading-module.html

【讨论】:

我在问题顶部添加了有关我的应用的更多信息 您能否添加有关如何添加延迟加载模块的步骤,例如我要遵循的具体步骤? @AngularM 你可以参考这个以获得关于延迟加载的帮助:angular.io/docs/ts/latest/guide/ngmodule.html 我现在已经给出了一些关于如何逐步利用模块延迟加载的解释。如果你想要一个示例项目来实现,我可以创建一个 github repo 告诉我,因为如果我在这里做这篇文章会变得很大。要实现延迟加载,您应该了解 Angular 2 最终版本中的模块和路由器概念。 @VikashDahiya 我正在尝试延迟加载。我在模块构造函数中放了一个 console.log 。当应用程序实例化时,它也会实例化惰性模块,这意味着它们会在应用程序实例化时被下载、加载。你能重新检查一下这是否是延迟加载的作用吗?【参考方案2】:

“代码拆分”怎么样。

来自 Webpack 网站:

“对于大型 Web 应用程序,将所有代码放在一个文件中效率不高,尤其是在某些情况下才需要某些代码块时。Webpack 具有将代码库拆分为按需加载的“块”的功能. 其他一些打包工具称它们为“层”、“汇总”或“片段”。此功能称为“代码拆分”。

链接在这里:

https://webpack.github.io/docs/code-splitting.html

请注意,Angular CLI 使用 Webpack。

此外,请确保如果您的应用通过数据调用进行引导,您不会在等待这些调用返回时阻止组件的渲染。承诺、异步等。

【讨论】:

你有不需要 webpack 的解决方案吗?我试过异步和延迟 在不了解您如何引导您的应用程序的细节的情况下,不可能提供除一般指南之外的任何内容;正在加载什么资产,正在进行什么数据调用,是否在访问任何外部 API,是否进行任何类型的绘图,重复布置大量项目等。除非您正在做一些非常明显的事情,否则可能需要一些真正的时间来弄清楚。在这一点上我能做的最好的就是说,看看开发者工具中的时间线,看​​看是什么阻碍了它。 似乎是导致问题的 cloudflare。我进行了速度测试。您是否曾遇到过 cloudflare 问题或对它们了解很多? 蒂姆。当您说:“另外,请确保如果您的应用程序使用数据调用进行引导,您不会在等待这些调用返回时阻止组件的渲染。承诺、异步等。”你是说 app.component 吗?我有他们的电话。这些会影响应用程序的加载吗? 当然。如果您的应用程序在开始渲染第一个组件(应用程序)之前正在等待数据返回,那么您的应用程序将一直处于空白状态,直到该数据调用返回。【参考方案3】:

如果不亲自操作整个代码库和后端,就很难诊断出您遇到的确切问题(正如其他人所建议的那样,问题可能根本不是角度问题)。

话虽如此,我强烈建议您开始使用angular-cli。它是由 Angular 团队设计的,目的是在一个易于使用的命令行界面中完成您需要做的所有事情。所以我的回答是基于 angular-cli 的使用。

以下是优化 ng2 项目以进行生产的一般操作:

1) 提前 (AoT) 编译 - 捆绑/缩小/摇树

看,忘记配置一堆 gulp 任务来完成所有这些事情的麻烦。 angular-cli 只需简单一步即可处理 Bundling/Minification/Tree-shaking/AOT 编译:

ng build -prod -aot

这将在您的“dist”文件夹中生成以下 js 文件:

inline.d41d8cd98f00b204e980.bundle.js
vendor.d41d8cd98f00b204e980.bundle.js
main.d41d8cd98f00b204e980.bundle.js
styles.4cec2bc5d44c66b4929ab2bb9c4d8efa.bundle.css
您还将获得这些文件的压缩版本以进行更多优化:
inline.d41d8cd98f00b204e980.bundle.js.gz
vendor.d41d8cd98f00b204e980.bundle.js.gz
main.d41d8cd98f00b204e980.bundle.js.gz

Angular 的 AOT 编译会自动对您的代码进行“摇树”并删除任何未使用的引用。例如,您可能在项目中使用了 lodash,但您可能只使用了几个 lodash 函数; tree shaking 将修剪掉最终构建中不需要的所有未使用的 lodash 部分。最重要的是,AOT 编译将预编译您的所有代码和视图,这意味着浏览器需要更少的时间来启动 ng2 应用程序。 Click here 了解有关 Angular 的 AOT 编译的更多信息。

2) 应用的延迟加载部分 如果您进一步将应用程序划分为不同的部分,则无需在应用程序首次加载时加载每个部分。您可以为您的应用程序指定不同的模块,然后可以将它们捆绑(通过 angular-cli aot 编译器)到不同的块中。阅读here 以了解如何将您的项目组织成模块,您可以将这些模块编译成仅在需要时加载的卡盘。 Angular-cli 将为您管理这些块的创建。

3) Angular 通用 现在,如果您真的想让加载时间非常快,那么您将需要考虑实现Angular Universal,这是您在服务器上编译初始视图的时候。我没有使用 Angular Universal,因为我已经能够通过第 1 步和第 2 步实现快速加载时间。但它是 ng2 工具集中的一个令人兴奋的选项。请记住,您不是在服务器上编译或运行 ng2 应用程序,而是在服务器端编译初始视图,以便用户快速收到 html 的震动,因此用户认为加载时间非常快(即使满载仍然会落后一点)。此步骤不排除对其他步骤的需要。作为奖励,Angular Universal 也应该有助于 SEO。

4) 二次捆绑

如果我不使用延迟加载,我通常会继续进一步捆绑从 AOT 编译生成的分发文件。因此,我创建了一个 ma​​in.bundle.js 文件,它包含 inline.js、vendor.js 和 main.js 文件。我为此使用 gulp。

【讨论】:

angular-cli 目前不支持aot 见github.com/angular/angular-cli/issues/1732。选项是存在的,但对于许多项目它不起作用。 我使用 AOT 编译和 angular-cli 已经一个多月了。我承认,您确实需要清理/调试代码以消除预编译错误,但它对我来说工作得很好。 仅供参考,我刚刚在 angular2 的“英雄之旅”应用上进行了 AOT 编译,使用延迟加载的版本等没有问题... @shusson angular-cli 很好地支持 AOT 和延迟加载。唯一不起作用的是,如果您有自定义装饰器(例如,使用 @ngrx/effects 时)。确实,海关装饰员被剥夺了...... @brando @maxime 它对你们有用,但基于 github.com/angular/angular-cli/issues/1732 有很多人遇到问题。我认为 angular-cli 很棒,但在解决该问题之前,我们应该谨慎向人们推荐 angular-cli aot 构建。【参考方案4】:

因为它的 SPA 和 angular 2 init 太慢了。而已。加上 RXjs,大量的 polifills 等。AOT 可以提供帮助,但很多 angular2 库不能使用它。角度通用真的有帮助

【讨论】:

我没有服务器端。我的应用程序只有前端。有其他选择吗? 目前我只知道一个好的解决方案:aot。前面写的警告。 + 懒加载。但影响不大。为了加快开发速度,我使用@angularclass/hmr(来自此示例github.com/AngularClass/angular2-webpack-starter)。它真的非常快并且记住你所有的输入数据 我目前用的是aot【参考方案5】:

使用以下命令构建您的 Angular 应用以获得最大的优化优势。

ng build --prod --aot --optimization --build-optimizer --vendor-chunk --common-chunk --extract-licenses --extract-css --source-map=false

基本上,您将在 aot 模式下构建并使用 treeshaking 并进行一些优化。

优化:启用构建输出的优化。

vendor-chunk:使用仅包含供应商库的单独捆绑包。

common-chunk:使用单独的包,其中包含跨多个包使用的代码。 extract-css:从全局样式中提取 css 到 css 文件而不是 js 文件中。 build-optimizer:在使用 'aot' 选项时启用 @angular-devkit/build-optimizer 优化。 source-map=false:删除源映射也会减小包大小

【讨论】:

【参考方案6】:

尝试通过运行 ng serve --sourcemap=false 来禁用源映射

【讨论】:

以上是关于如何提高 Angular2 应用程序的加载性能?的主要内容,如果未能解决你的问题,请参考以下文章

提高 JavaScript 性能的 12 个技巧

为了提高性能,如何动态加载JS文件

如何提高大型文本文件的数据加载性能[重复]

从数据库加载大数据并转换为 JSON 时如何提高性能

前端开发,页面加载速度性能优化,如何提高web页面加载速度

如何提高jQuery的性能 ?