Helpex - Trao đổi & giúp đỡ Đăng nhập

Hiểu về phát hiện thay đổi Angular 2

TL; DR Angular 2 giới thiệu một hệ thống phát hiện thay đổi được sáng tạo lại giúp giảm các chu kỳ thông báo theo hướng một chiều. Ngoài ra, tính năng phát hiện thay đổi giờ đây có thể được kiểm soát và tinh chỉnh bởi các nhà phát triển để tận dụng tối đa hiệu suất của khung.

Giới thiệu về Phát hiện Thay đổi

Angular 2 cuối cùng đã được phát hành. Bạn có thể đã nghe nói về một số thay đổi do va chạm phiên bản lớn: nó đã được viết lại hoàn toàn, TypeScript được chọn làm ngôn ngữ lựa chọn, các biểu mẫu được phát minh lại, RxJS, một bộ định tuyến hoàn toàn mới, v.v. Theo ý kiến ​​của tôi, hầu hết điều đáng giá là việc thiết kế lại hệ thống phát hiện thay đổi cốt lõi. Như bạn có thể nhớ, hiệu suất vòng lặp thông báo của AngularJS (hay còn gọi là Angular 1) có vấn đề. Bây giờ thì không.

Tại sao chúng ta cần phát hiện thay đổi?

Quan tâm làm gì? Nói chung, sức mạnh của các khung JavaScript hiện đại hoạt động giống như sau: một sự kiện thay đổi trong mô hình và buộc phải thay đổi giao diện người dùng. Đây là phát hiện thay đổi, hệ thống giám sát các sự kiện và hành động trên chúng. Một cái gì đó phải kích hoạt sự lan truyền này đến chế độ xem. Như đã đề cập trước đây, trong Angular 1, chúng tôi có các vòng lặp thông báo kiểm tra mọi tham chiếu đơn lẻ được đặt để theo dõi các thay đổi giá trị. Khi Angular phát hiện ra rằng mọi thứ đều ổn định (không có vòng lặp vô hạn, v.v.), nó đã tuyên truyền các thay đổi đối với chế độ xem. Mặc dù điều này không hiệu quả, nhưng nó đã hoạt động trong một thời gian dài. Ngoài ra, vấn đề là theo dõi các sự kiện không đồng bộ. Bạn cũng có thể sử dụng $scope.$apply(...)nếu bạn đã làm việc với Angular 1. Để hiểu tại sao nó lại cần thiết, chúng ta hãy bắt đầu lại từ đầu.

Cách hoạt động của Javascript

Thời gian chạy JavaScript hoạt động trên một công cụ phân luồng duy nhất. Bạn có thể đã nghe nói về ngăn xếp (có thể từ các ngôn ngữ lập trình khác). Hãy lấy đoạn mã dưới đây:

console.log('Hey')
setTimeout(() => {
   console.log('Hello from timeout!')
}, 1000);
console.log('Hi')

Chúng tôi sẽ thấy điều này trong một bảng điều khiển dưới dạng:

Hey
Hi
Hello from timeout!

Hơn nữa, không có gì bị chặn trong khoảng thời gian chờ một giây. Vậy công cụ JS sẽ thực hiện điều này như thế nào với một luồng duy nhất?

Mã đồng bộ

Hãy đi từng bước một. Nếu bạn có mã như thế này:

console.log('1')
console.log('2')
console.log('3')

mọi lệnh sẽ được đưa vào ngăn xếp và sẽ chạy từng lệnh một. Không có khả năng nhìn thấy 3 trước 2 hoặc 1. Vì vậy, chúng tôi sẽ kết thúc với những điều sau:

1
2
3

Mỗi lần. Mọi nơi.

Mã không đồng bộ

Nhưng hãy quay lại thời gian chờ:

console.log('1')
setTimeout(() => {
  console.log('2')
}, 0)
console.log('3')

Điều gì xảy ra bây giờ? Trên ngăn xếp, chúng ta sẽ có:

console.log
setTimeout
console.log

Bí quyết ở đây là cách thức setTimeouthoạt động và nó thực sự là gì. Có, nó sẽ được gọi như một hành động đồng bộ bình thường, nhưng tất cả những gì mà công cụ JS làm là đưa bánh xe cho một thứ khác. Có một loạt các API trình duyệt không phải là một phần của quy trình đơn luồng này. Và có một thứ gọi là vòng lặp sự kiện. Vòng lặp sự kiện này lần lượt đi qua các hướng dẫn ngăn xếp và nếu nó trống, thì nó sẽ chuyển đến hàng đợi gọi lại . Tham chiếu đến setTimeoutmã là ở đó. Sau khi gọi lại xong, mã sẽ chuyển đến ngăn xếp.

Nó có nghĩa là gì? Hai điều:

  • Mọi thứ bên trong lệnh gọi lại không đồng bộ (như trong setTimeout) sẽ được chạy sau bất kỳ mã đồng bộ nào khác; đây là lý do tại sao hack setTimeout(() => {}, 0)hoạt động như vậy .
  • Chúng tôi không có cách nào để đảm bảo 1000ms chính xác là 1000ms (nhưng chúng tôi biết rằng nó ít nhất là 1000ms).

Để hiểu đầy đủ về vòng lặp sự kiện và những gì đang diễn ra trong trình duyệt, tôi khuyến khích bạn xem qua bài nói chuyện này của Philip Roberts .

Cách các khu vực liên quan đến phát hiện thay đổi

Làm thế nào để tất cả những điều này liên quan đến Angular và phát hiện thay đổi? Theo dõi các đối tượng với mã đồng bộ là khá dễ dàng. Tuy nhiên, khi nói đến mã không đồng bộ, mọi thứ trở nên phức tạp. Đó là bởi vì góc 1 buộc chúng tôi phải sử dụng $scope.$apply(...)mỗi lần một hành động không đồng bộ đã được thực hiện hoặc sử dụng các đường góc làm những hành động không đồng bộ: $timeout, $http, và vân vân. Vấn đề là, nếu một cái gì đó được thực hiện bên ngoài bộ điều khiển (thậm chí là một thay đổi hoàn toàn hợp lệ đối với đối tượng tham chiếu), Angular không biết về nó, vì vậy nó không kích hoạt bất kỳ sự kiện nào để phản ánh các thay đổi đối với giao diện người dùng.

Mặt khác, bây giờ chúng ta có Angular 2. Nó đã loại bỏ tất cả những thứ được kết nối với các chu kỳ tiêu hóa và bây giờ sử dụng Zones . Các khu vực có thể theo dõi ngữ cảnh của các hành động không đồng bộ bằng cách vá chúng (tức là ghi đè chúng bằng mã riêng của nó), sau đó gọi hành động mong muốn nhưng kèm theo một số thông tin bổ sung. Thông tin bổ sung này là bối cảnh. Bằng cách này, Angular sẽ biết hành động không đồng bộ được gọi từ thành phần nào.

Chiến thắng lớn của phương pháp này là chúng ta có thể sử dụng các API của trình duyệt một cách nguyên bản và Angular sẽ biết điều gì đang xảy ra mà không buộc chúng ta phải thông báo thủ công về việc thay đổi đã xảy ra. Hạn chế là Zones ghi đè các hành động không đồng bộ, đây là một loại giải pháp khó hiểu và có thể ảnh hưởng đến mã (hiện có) khác nếu chúng tôi không chỉ dựa vào Angular trong ứng dụng.

Nhưng chính xác thì Angular được thông báo về sự thay đổi như thế nào? Angular sử dụng phiên bản Zone được gọi của riêng nó NgZone, nó chuyển tiếp các hành động không đồng bộ đã hoàn thành với onTurnDonesự kiện. Phát hiện thay đổi góc đợi sự kiện thực hiện phát hiện thay đổi và kiểm tra những gì cần được cập nhật trong giao diện người dùng. Đó là hành vi cốt lõi.

Tận dụng tính năng phát hiện thay đổi trong ứng dụng của bạn

Mọi thứ được mô tả ở trên đang diễn ra dưới mui xe. Điều quan trọng không kém là chúng ta có thể tận dụng nó như thế nào. Không giống như Angular 1, Angular 2 cho chúng ta khả năng kiểm soát phát hiện thay đổi. Tuy nhiên, nhóm Angular tuyên bố rằng ngay cả khi không có bất kỳ điều chỉnh hiệu suất nào, nó nhanh hơn 3 đến 10 lần so với trước đó và đối với hầu hết các ứng dụng, điều này sẽ đủ nhanh. Nhưng nó có thể nhanh hơn nhiều. Hãy xem một ví dụ.

  <head>
    <base href="." />
    <title>angular2 playground</title>
    <link rel="stylesheet" href="style.css" />
    <script src="https://unpkg.com/zone.js@0.6.21/dist/zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
    <script src="https://unpkg.com/typescript@1.8.10/lib/typescript.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
  </head>

  <body>
    <my-app>
      loading...
    </my-app>
  </body>

</html>

Đây là một vấn đề rất điển hình: hiển thị một danh sách. Có một thành phần chứa danh sách các thành phần khác có một số dữ liệu đầu vào. Nói chung, chúng ta có một vùng chứa với dữ liệu và một thành phần câm chỉ để hiển thị một mục danh sách duy nhất. Không có gì lạ mắt ở đây, chỉ là cái và ngOnChange. Những gì đang được thực hiện ở đây? ngOnChangephản ứng trên mọi thay đổi đầu vào và getter thêm ghi nhật ký bổ sung mỗi lần rowDatađược tìm nạp. Lưu ý rằng chúng tôi không sử dụng nó ở bất kỳ đâu bên ngoài mẫu.

Điều này có nghĩa là getter được kích hoạt bởi chính Angular. Và đoán xem điều gì sẽ xảy ra? Chúng tôi có một thay đổi duy nhất trên đầu vào, nhưng có hàng trăm nhật ký getter ở đó.

Tại sao vậy?

Angular được thông báo về sự thay đổi từ một số thành phần và phải kiểm tra xem điều đó ảnh hưởng như thế nào đến trạng thái hiện tại, vì vậy nó sẽ kiểm tra tất cả các giá trị cho sự thay đổi. Trên thực tế, nhóm cho biết họ có thể thực hiện hàng nghìn lần kiểm tra như vậy trong mili giây, nhưng nó vẫn lãng phí thời gian và thậm chí có thể gây hại cho ứng dụng dựa trên dữ liệu lớn của chúng tôi.

Tính bất biến

Điều thú vị về hệ thống phát hiện thay đổi mới là giờ đây chúng ta có thể điều chỉnh nó. Hãy tạm dừng Angular và xem xét đoạn mã sau:

const users = [{
  name: 'John',
  age: 27
}, {
  name: 'Anna',
  age: 23
}]

users.push({
  name: 'Max',
  age: 30
})

Điều quan trọng nhất ở đây là constkhai báo. Nếu userslà hằng số, chúng ta có thể sửa đổi nó như thế nào? Đó là cách JavaScript hoạt động! Điều này constngăn chúng tôi sửa đổi một tham chiếu đến đối tượng cụ thể trong JavaScript. Những gì pushphương thức Arraythực sự đang làm là thêm một đối tượng khác vào mảng hiện có (không có thay đổi tham chiếu). Hãy đi đến một ví dụ rất điển hình khác:

const user = {
  name: 'Max',
  age: 30
}

user.age = 31

Điều tương tự cũng được áp dụng. Mặc dù chúng ta không thể sửa đổi toàn bộ đối tượng để biến nó thành một đối tượng khác (thay đổi tham chiếu), chúng ta vẫn có thể thay đổi một phần của đối tượng!

Đây là lý do tại sao các kiểm tra mà chúng ta đã thảo luận trước đây không tốt như vậy. Nếu bạn muốn kiểm tra xem đối tượng có giống như trước đây hay không, bạn phải kiểm tra sâu tất cả các thuộc tính của nó . Nó không hiệu quả.

Làm thế nào chúng ta có thể buộc đối tượng phải là đối tượng mới với thuộc tính đã thay đổi? Nó thực sự khá dễ dàng với đề xuất thuộc tính trải rộng Đối tượng ECMAScript mới :

const user = {
  name: 'Max',
  age: 30
}

const modifiedUser = { ...user, age: 31 }

Thay đổi chiến lược phát hiện

Phần tốt của tất cả những điều này là bây giờ chúng ta có thể nói với Angular rằng chúng ta biết những gì chúng ta đang làm . Để thay đổi hành vi phát hiện sự thay đổi, chúng ta có thể sử dụng ChangeDetectionStrategyAPI, trong đó có một giá trị rất thú vị: OnPush. Nó làm cho một thành phần được áp dụng chiến lược này chỉ nhìn vào các giá trị bên trong khi tham chiếu trên đầu vào thay đổi hoặc một số sự kiện đã được kích hoạt khỏi thành phần.

Hãy thêm OnPushchiến lược vào ví dụ trước của chúng tôi:

import {ChangeDetectionStrategy, Component, Input} from '@angular/core';

@Component({
  selector: 'row',
  template: `
    <pre></pre>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class RowComponent {
  ...
}

Bạn có thể thử đoạn mã dưới đây trên Plunker và xem sự khác biệt.

<!DOCTYPE html>
<html>

  <head>
    <base href="." />
    <title>angular2 playground</title>
    <link rel="stylesheet" href="style.css" />
    <script src="https://unpkg.com/zone.js@0.6.21/dist/zone.js"></script>
    <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
    <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>
    <script src="https://unpkg.com/typescript@1.8.10/lib/typescript.js"></script>
    <script src="config.js"></script>
    <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
  </head>

  <body>
    <my-app>
      loading...
    </my-app>
  </body>

</html>

Cải tiến lớn là giờ đây chỉ có một cuộc gọi nhận cho một thay đổi! Chúng tôi không cần thêm gì nữa vì dữ liệu đầu vào của chúng tôi là các chuỗi đang được thay đổi, do đó tham chiếu về đầu vào sẽ thay đổi. Tham chiếu cho phần còn lại của các thành phần không thay đổi, vì vậy Angular thậm chí không nhìn vào nó.

Cấu trúc ứng dụng

Làm cách nào để chúng tôi có thể xây dựng một ứng dụng hoạt động hiệu quả? Với Angular 2, nó thực sự khá dễ dàng. Như trong tất cả các khung công tác thành phần ngày nay, bạn nên có các thành phần thông minh và ngu ngốc. Các thành phần câm, vốn chỉ dùng để hiển thị dữ liệu từ đầu vào hoặc xử lý các sự kiện của người dùng, là những tình nguyện viên lý tưởng để có OnPushchiến lược. Các thành phần thông minh đôi khi sẽ yêu cầu bạn theo dõi nhiều thứ hơn đầu vào và các sự kiện, vì vậy hãy cẩn thận với việc thiết lập OnPushchiến lược ở đó.

Bên cạnh: Sử dụng Angular 2 với Auth0

Auth0 phát hành Mã thông báo web JSON trên mỗi lần đăng nhập cho người dùng của bạn. Điều đó có nghĩa là bạn có thể có một cơ sở hạ tầng nhận dạng vững chắc, bao gồm đăng nhập một lần, quản lý người dùng, hỗ trợ cho mạng xã hội (Facebook, Github, Twitter, v.v.), doanh nghiệp (Active Directory, LDAP, SAML, v.v.) và cơ sở dữ liệu của riêng bạn của người dùng chỉ với một vài dòng mã.

Bạn có thể thêm Auth0 vào ứng dụng Angular 2 rất dễ dàng. Chỉ có một số bước đơn giản:

Bước 0: Đăng ký Auth0 và định cấu hình

Nếu bạn chưa có bất kỳ tài khoản Auth0 nào, hãy đăng ký một tài khoản ngay bây giờ để làm theo các bước khác.

Bước 1: Thêm Auth0lock vào ứng dụng của bạn

Khóa là tiện ích hộp đăng nhập tuyệt đẹp (và hoàn toàn có thể tùy chỉnh) đi kèm với Auth0. Tập lệnh cho nó có thể được đưa vào từ liên kết CDN hoặc bằng npm.

Lưu ý: Nếu bạn sử dụng npm để tải Auth0Lock, bạn sẽ cần đưa nó vào bước xây dựng của mình.


  <!-- src/client/index.html -->

  ...

  <!-- Auth0 Lock script -->
  <script src=“https://cdn.auth0.com/js/lock/10.0/lock.min.js"></script>

  <!-- Setting the right viewport -->
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

  ...

Bước 2: Thêm dịch vụ xác thực

Tốt nhất bạn nên thiết lập một dịch vụ có thể tiêm để xác thực có thể được sử dụng trên toàn ứng dụng.

Với Auth0, chúng tôi có quyền truy cập vào hồ sơ của người dùng và JWT trong lệnh lock.ongọi lại nơi chúng tôi lắng nghe authenticatedsự kiện được kích hoạt khi người dùng đăng nhập thành công và các mục này có thể được lưu trong bộ nhớ cục bộ để sử dụng sau này.

// src/client/shared/auth.service.ts

import {Injectable, NgZone} from 'angular2/core';
import {Router} from 'angular2/router';
import {AuthHttp, tokenNotExpired} from 'angular2-jwt';

// Avoid name not found warnings
declare var Auth0Lock: any;

@Injectable()
export class AuthService {
  lock = new Auth0Lock('YOUR_AUTH0_CLIENT_ID', 'YOUR_AUTH0_DOMAIN');
  refreshSubscription: any;
  user: Object;
  zoneImpl: NgZone;

  constructor(private authHttp: AuthHttp, zone: NgZone, private router: Router) {
    this.zoneImpl = zone;
    this.user = JSON.parse(localStorage.getItem('profile'));

    // Add callback for lock `authenticated` event
    var self = this;
    this.lock.on("authenticated", authResult => {
      self.lock.getProfile(authResult.idToken, (error, profile) => {

        if (error) {
          // handle error
          return;
        }

        // If authentication is successful, save the items
        // in local storage
        localStorage.setItem('profile', JSON.stringify(profile));
        localStorage.setItem('id_token', authResult.idToken);
        self.zoneImpl.run(() => self.user = profile);
      });
    });
  }

  public authenticated() {
    // Check if there's an unexpired JWT
    return tokenNotExpired();
  }

  public login() {
    // Show the Auth0 Lock widget
    this.lock.show();
  }

  public logout() {
    localStorage.removeItem('profile');
    localStorage.removeItem('id_token');
    this.zoneImpl.run(() => this.user = null);
    this.router.navigate(['Home']);
  }
}

Bước 3: Thêm Trình xử lý nhấp chuột để đăng nhập

Để có thể sử dụng phương thức "Đăng nhập" và "Đăng xuất", chúng ta cần đưa dịch vụ Auth vào app.components.tstệp.

 <!-- src/client/app.components.ts -->
import { Component } from '@angular/core';
import { AuthService } from './auth.service';

...

Trong NgModule gốc của bạn, hãy khai báo nhà cung cấp dịch vụ, như được hiển thị trong đoạn mã sau.

 <!-- src/client/app.module.ts -->

...
import { AuthService } from './auth.service';

@NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    routing,
    HttpModule
  ],
  declarations: [
    AppComponent
  ],
  providers: [
    AUTH_PROVIDERS,
    AuthService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Bây giờ, chúng tôi có thể sử dụng các phương pháp từ dịch vụ xác thực của chúng tôi trong bất kỳ thành phần nào của chúng tôi, có nghĩa là chúng tôi có thể dễ dàng thêm trình xử lý nhấp chuột vào nút "Đăng nhập" và "Đăng xuất".

  <!-- src/client/app.component.ts -->

  ...

  <button (click)="authService.login()" *ngIf="!authService.authenticated()">Log In</button>
  <button (click)="authService.logout()" *ngIf="authService.authenticated()">Log Out</button>

  ...

Sau khi người dùng đăng nhập, Mã thông báo web JSON sẽ được lưu cho họ trong bộ nhớ cục bộ. JWT này sau đó có thể được sử dụng để thực hiện các yêu cầu HTTP đã xác thực tới một API.

Bước 5: Thực hiện các yêu cầu HTTP đã xác thực

Với anuglar2-jwt , chúng tôi có thể tự động gửi các JWT của mình trong các yêu cầu HTTP. Để làm như vậy, chúng ta cần phải tiêm và sử dụng AuthHttp.

// src/client/ping/ping.component.ts

import {Component} from 'angular2/core';
import {Http} from 'angular2/http';

import {AuthHttp} from 'angular2-jwt';
import {Auth} from './auth.service';
import 'rxjs/add/operator/map';

@Component({
  selector: 'ping',
  template: `
    <h1>Send a Ping to the Server</h1>
    <button class="btn btn-primary" (click)="securedPing()" *ngIf="auth.authenticated()">Secured Ping</button>
    <h2></h2>
  `
})
export class Ping {
  API_URL: string = 'http://localhost:3001';
  message: string;

  constructor(private http: Http, private authHttp: AuthHttp, private auth: Auth) {}

  securedPing() {
    this.authHttp.get(`${this.API_URL}/secured/ping`)
      .map(res => res.json())
      .subscribe(
        data => this.message= data.text,
        error => this.message = error._body
      );
  }
}

Bước 5: Đã xong!

Đó là tất cả những gì cần làm để thêm xác thực vào ứng dụng Angular 2 của bạn với Auth0!

Kết luận

Ưu điểm về hiệu suất

Một trong những lợi thế lớn của việc sử dụng tính năng phát hiện thay đổi chặt chẽ hơn là tăng hiệu suất. Angular được sử dụng cho các ứng dụng lớn có thể xử lý nhiều dữ liệu động. Nhóm Angular đã cung cấp cho nhà phát triển các công cụ cần thiết để tinh chỉnh và cải thiện hiệu suất ngay từ đầu. Theo mặc định, mọi thay đổi sẽ được phản ánh trên giao diện người dùng, vì Angular sẽ xử lý điều đó, nhưng giá là hiệu suất thấp hơn. Mã bất biến hoặc mã phản ứng khó viết hơn nhưng dễ bảo trì và lập luận hơn. Sự lựa chọn là của bạn.

Cuối cùng thì Angular có thể được tinh chỉnh

Điều tốt là chúng tôi có quyền lựa chọn với Angular 2. Trong Angular 1, không thể thoát khỏi chu trình tiêu hóa. Ở một số điểm, việc sử dụng React hoặc một thư viện khác để kết xuất giao diện người dùng thay vì các mẫu Angular là một điều thuận lợi, vì nó quá chậm khi xử lý một lượng lớn dữ liệu động. Bây giờ, bạn có một giải pháp hoàn chỉnh với nhiều quyền kiểm soát hơn đối với hành vi nội bộ. Điều này, kết hợp với những thay đổi khác được thực hiện cho Angular 2, làm cho đường cong học tập của khung công tác dốc hơn nhưng đáng giá.

20 hữu ích 0 bình luận 16k xem chia sẻ

Có thể bạn quan tâm

loading