Bạn có thể sử dụng tải trước preloadingStrategy: PreloadAllModules trong Angular

3rd May 2022
Table of contents

Tôi biết rằng khi một ứng dụng có nhiều bộ phận và thành phần thì tốt hơn là nên tách chúng thành các mô-đun được tải chậm để người dùng xem trang chủ của ứng dụng một cách nhanh chóng.

Vấn đề là tôi nhận thấy rằng điều hướng đến thành phần của mô-đun được tải lười biếng cho thấy một số độ trễ giữa tương tác của người dùng (nhấp vào nút / menu) và khi thành phần được hiển thị (so với thành phần không được tải chậm).

Có cách nào để tải trước một mô-đun được tải chậm theo cách thủ công không? vì vậy, giả sử người dùng nhìn thấy màn hình chính và nếu không có gì được thực hiện trong 3 giây thì hãy tải một số mô-đun ứng dụng quan trọng của tôi ở chế độ nền.

Bạn có thể sử dụng preload chiến lược chỉ cho một mô-đun bằng cách thiết lập data: { preload: true } thay vì tất cả các mô-đun đang sử dụng PreloadAllModules.

{
  path: 'crisis-center',
  loadChildren: () => import('./crisis-center/crisis-center.module').then(mod => mod.CrisisCenterModule),
  data: { preload: true }
},

Hoặc bạn có thể sử dụng tải trước

Bạn phải thêm nó như thế này:

RouterModule.forRoot(appRoutes, {preloadingStrategy : PreloadAllModules } )

Thí dụ:

import { RouterModule, Routes, PreloadAllModules } from '@angular/router';

const appRoutes: Routes = [
  { path: 'employees', loadChildren: './employee/employee.module#EmployeeModule' },
  { path: 'home', component: HomeComponent },
  { path: '', redirectTo: '/home', pathMatch: 'full' },
  { path: '**', component: PageNotFoundComponent }
];
 
@NgModule({
  imports: [ RouterModule.forRoot(appRoutes, {preloadingStrategy : PreloadAllModules } )],
  exports: [ RouterModule ]
})
export class AppRoutingModule { }

Một số giải pháp tối ưu khác

1. Tối ưu hóa main bundle với Lazy Loading

Khi chúng ta build app ở production không cùng với lazy load rất có thể chúng ta sẽ thấy các tệp này được tạo trong thư mục dist:

  • polyfills.js
  • scripts.js
  • runtime.js
  • styles.css
  • main.js

polyfills.js là để làm cho ứng dụng của chúng ta tương thích với các trình duyệt khác nhau. Bởi vì chúng ta viết mã với các tính năng mới nhất và không phải tất cả các trình duyệt đều hỗ trợ các tính năng đó.

scripts.js chứa script mà chúng ta khai báo trong phần script của angular.json file.

"styles": [
  "src/styles.css",
  "src/my-custom.css"
],

main.js chứa tất cả các code của chúng ta bao gồm components (ts, html and css codes), pipes, directives, services và tất cả modules đã import (gồm cả các thư viện thứ 3).

Như bạn có thể thấy, tệp main.js sẽ ngày càng lớn hơn, đây là một vấn đề vì để xem trang web trình duyệt trang web cần tải xuống tệp main.js, thực thi và hiển thị trên trang, việc đó không chỉ có thể là thách thức đối với thiết bị di động người dùng có internet chậm, mà cho cả trên Desktop.

Cách dễ nhất để giải quyết vấn đề này là chia ứng dụng của bạn thành nhiều lazy modules. Khi chúng tôi sử dụng các lazy modules cho mỗi module, angular tạo ra chunk của nó và nó sẽ được tải cho đến khi cần (thường là bằng cách kích hoạt một roue).

Để chứng minh điều này, tôi đã tạo ra hai component, app.component và second.component. Cả hai đều ở trong app.module nên không có lazyload module gì ở đây.

App.component rất đơn giản và có hai nút để điều hướng đến Second.component và quay lại App.component.

Tuy nhiên, component thứ 2 chứa văn bản rất lớn (khoảng 1 mb) trong template.

Bởi vì không có laziness, khi chúng tôi xây dựng ứng dụng của mình, chúng tôi nhận được main.js lớn chứa tất cả code từ app.component và second.component.

Bây giờ trong network tab của chrome dev tools , chúng ta có thể thấy rằng main.js quá lớn (1,3 mb)

Vấn đề là hầu hết thời gian người dùng truy cập trang chính, thay vì một trang cụ thể, vì vậy việc load tất cả code của các trang khác không phải là giải pháp tốt nhất. Chúng ta có thể tạo lazy module cho component thứ hai và module đó sẽ chỉ được kích hoạt khi cần thiết (tức là chỉ khi người dùng điều hướng đến trang đó). Điều này cung cấp cho chúng tôi main.js rất nhỏ và do đó tải trang đầu tiên rất nhanh.

Khi chúng tôi sử dụng lazy loading, sau quá trình build, tệp mới sẽ được tạo ra, ví dụ như 4.386205799sfghe4.js. Đó là chunk của lazy module và nó đã nhanh hơn việc không sử dụng lazy khá nhiều. Bây giờ khi chúng tôi mở ứng dụng, chúng tôi thấy rằng main.js rất nhỏ (266 kb).

Và chỉ khi chúng tôi điều hướng đến trang thứ hai, chúng tôi mới thấy tệp mới (1 mb) được tải

Tuy nhiên, tải từng phần theo cách như vậy cũng sẽ ảnh hưởng đến hiệu suất vì khi điều hướng sẽ chậm hơn. May mắn thay Agular cung cấp một cách để giải quyết vấn đề này với PreloadingStrargety. Chúng ta có thể nói Angular để tải main module (main.js) và nó được tải và thực thi đầy đủ, chỉ sau khi tải các lazy module khác trong backgroud, vì vậy khi người dùng điều hướng đến các lazy page, mọi thứ đã được tải xuống. Ví dụ:

import { PreloadAllModules, RouterModule } from '@angular/router';
RouterModule.forRoot(
[
 {
    path: 'second',
    loadChildren: './second/second.module#SecondModule'
 }
], {preloadingStrategy: PreloadAllModules})

Vì vậy, luôn luôn xem xét sử dụng càng nhiều mô-đun lười biếng càng tốt với một số chiến lược tải trước. Điều này sẽ giữ cho main.js của bạn nhỏ, có nghĩa là tải xuống và kết xuất trang chính nhanh hơn.

Vì vậy, luôn luôn xem xét sử dụng càng nhiều lazy module càng tốt với một số chiến lược tải trước. Điều này sẽ giữ cho main.js của bạn nhỏ, có nghĩa là tải xuống và kết xuất trang chính nhanh hơn.

2. Debug bundles with Webpack Bundle Analyzer

Ngay cả khi sau khi chia logic ứng dụng thành nhiều lazy module, bạn sẽ có được (main bundle) lớn(lơn theo định nghĩa cá nhân tôi xem xét lớn hơn 1 mb cho các ứng dụng cỡ trung bình). Bạn có thể tối ưu hóa hơn nữa bằng cách sử dụng Webpack Bundle Analyzer. Gói npm này cho phép trực quan hóa kích thước của các tệp đầu ra webpack với một treemap có thể tương tác. Trước hết cài đặt plugin như dev dependency trong dự án Angular của bạn

npm install --save-dev webpack-bundle-analyzer

Sau đó sửa đổi pack.json file của bạn bằng cách thêm dòng này trong script section

"bundle-report": "ng build --prod --stats-json && webpack-bundle-analyzer dist/stats.json"

Lưu ý rằng dist / stats.json có thể khác trong dự án của bạn. Ví dụ: nếu các bundle files của bạn được tạo trong dist / browser, bạn cần sửa đổi lệnh là dist / browser / stats.json Cuối cùng chạy

npm run bundle-report

Điều này sẽ tạo ra bản production build với số liệu thống kê về từng bundle và với sự trợ giúp của bộ phân tích bundle webpack, chúng ta có thể hình dung điều đó với treemap có thể phóng to.

Từ đây chúng ta có thể thấy những module/file nào được sử dụng trong bundle. Điều này giúp ích rất nhiều khi chúng ta có thể thấy một cách trực quan những gì được include mà không cần phải ở đó.

3. Tạo ra các shared modules nhỏ

Phía trên đã cho chúng ta thấy thực tiễn tốt nhất để có các shared module cho việc DRY, nhưng đôi khi shared module cũng có thể ngày càng lớn hơn. Ví dụ: nếu chúng ta có SharedModule chứa nhiều mudule / component / pipe, việc import module đó trong app.module sẽ tăng kích thước gói của main.js, bởi vì chúng tôi sẽ import không chỉ những gì main module cần, mà còn tất cả những thứ không cần thiết khác đi kèm với SharedModule. Để tránh điều này, chúng ta có thể tạo một shared module khác,

HomeSharedModule sẽ chỉ chứa các thành phần mà main module và các component của nó cần.   Có nhiều shared module tốt hơn một shared module lớn.

4. Sử dụng Lazy Loading cho các hình ảnh không hiển thị ngay trên trang

Khi chúng tôi tải trang chính của mình lần đầu tiên, chúng tôi có thể có hình ảnh không hiển thị cho người dùng nhay. Người dùng phải cuộn xuống để xem hình ảnh. Tuy nhiên, hình ảnh được tải xuống ngay lập tức khi chúng tôi tải trang và nếu chúng tôi có nhiều hình ảnh, điều này thực sự có thể ảnh hưởng đến hiệu suất. Để giải quyết vấn đề này, chúng ta có thể Lazy load hình ảnh, chỉ tải khi người dùng xem đến chúng.

5. Sử dụng virtual scrolling cho danh sách lớn

Phiên bản 7 của Franework giới thiệu virtual scrolling trong CDK. Virtual scrolling loads và unloads các phần tử từ DOM dựa trên các phần hiển thị của danh sách, làm cho ứng dụng của chúng ta cực kỳ nhanh.

6. Sử dụng FOUT thay vì FOIT cho font

Trong hầu hết các trang web, chúng tôi thấy phông chữ đẹp và đã qua custom, thay vì phông chữ thông thường. Tuy nhiên, sử dụng custom hoặc phông chữ được cung cấp bởi một dịch vụ khác yêu cầu trình duyệt tải xuống và phân tích phông chữ đó khi người dùng truy cập trang của chúng tôi. Có hai kịch bản: điều gì sẽ xảy ra nếu chúng tôi sử dụng custom font được cung cấp bởi các dịch vụ bên thứ 3 như Google Fonts

Trình duyệt chờ để tải xuống phông chữ, phân tích cú pháp và chỉ sau đó hiển thị văn bản trên trang. Văn bản trên trang sẽ vô hình cho đến khi phông chữ được tải xuống và phân tích cú pháp. Đây là FOIT, hoặc Flash của invisible text.

Trình duyệt ban đầu hiển thị văn bản ở phông chữ thông thường và cũng cố gắng tìm nạp các kiểu phông chữ bên ngoài. Khi được tải xuống và phân tích cú pháp, nó sẽ trao đổi phông chữ thông thường với phông chữ tùy chỉnh của chúng tôi. Văn bản trên trang sẽ được hiển thị với phông chữ thông thường, trong khi đó nếu trình duyệt sẽ tải xuống và phân tích phông chữ bên ngoài, phông chữ sẽ được hoán đổi. Đây là FOUT hoặc Flash của unstyled text.

Hầu hết các trình duyệt sử dụng FOIT và chỉ Internet Explorer sử dụng FOUT. Để khắc phục điều này, chúng ta có thể sử dụng mô tả hiển thị phông chữ cho @ font-face và báo cho trình duyệt biết nếu chúng ta muốn đi với phông chữ thông thường và sau đó trao đổi hoặc để văn bản ẩn.

Bạn thấy bài viết này như thế nào?
1 reaction

Add new comment

Image CAPTCHA
Enter the characters shown in the image.
Câu nói tâm đắc: “Điều tuyệt với nhất trong cuộc sống là làm được những việc mà người khác tin là không thể!”

Related Articles

Chào mọi người, cho mình hỏi ngu phát là mình có thẻ html như span hoặc p và set contenteditable = "true"

Rất cảm ơn mọi người đã đến buổi Meet up ngày hôm nay và lắnng nghe bài nói của mình.

Cty em tuyển senior fullstack mà 5/6 ứng viên bỏ cuộc không làm được bài này

Không chạy code (pseudo code thôi), các bạn nghĩ là 2 logs này giống nhau không? Nghĩa là sẽ log TestDir instance và TestComp instance?