Analisis ringkas mekanisme Pengesanan Perubahan dalam Angular

青灯夜游
Lepaskan: 2022-12-15 21:20:55
ke hadapan
2343 orang telah melayarinya

Analisis ringkas mekanisme Pengesanan Perubahan dalam Angular

Apakah Pengesanan Perubahan?

Semasa proses pembangunan aplikasi, state mewakili data yang perlu dipaparkan pada aplikasi. Apabila keadaan berubah, mekanisme sering diperlukan untuk mengesan keadaan berubah dan mengemas kini antara muka yang sepadan dengan sewajarnya. Mekanisme ini dipanggil mekanisme Pengesanan Perubahan. [Tutorial berkaitan yang disyorkan: "tutorial sudut"]

Dalam pembangunan WEB, mengemas kini antara muka aplikasi sebenarnya mengubah suai pepohon DOM. Memandangkan operasi DOM mahal, Pengesanan Perubahan yang tidak cekap akan menyebabkan prestasi aplikasi yang lemah. Oleh itu, kecekapan rangka kerja dalam melaksanakan mekanisme Pengesanan Perubahan sebahagian besarnya menentukan prestasinya.

Cara Pengesanan Perubahan dilaksanakan

Angular boleh mengesan apabila data komponen berubah dan kemudian memaparkan semula paparan secara automatik untuk mencerminkan perubahan tersebut. Tetapi bagaimana ia boleh melakukan ini selepas acara peringkat rendah seperti klik butang?

Melalui Zon, Angular boleh mencetuskan mekanisme Pengesanan Perubahan secara automatik.

Apakah itu Zon? Ringkasnya, Zon ialah konteks pelaksanaan, yang boleh difahami sebagai persekitaran pelaksanaan. Berbeza daripada persekitaran pelaksanaan penyemak imbas biasa, semua tugas tak segerak yang dilaksanakan dalam pautan ini dipanggil Tasks menyediakan sekumpulan cangkuk untuk Tugas ini, membolehkan pembangun "memantau" persekitaran dengan mudah.

Penyimpangan: Memandangkan Angular sangat menyokong penggunaan objek boleh diperhatikan (Boleh Diperhatikan), jika anda membangunkan aplikasi sepenuhnya berdasarkan Boleh Diperhatikan, anda boleh menggantikan Zon untuk melaksanakan fungsi menjejak timbunan panggilan , dan meningkatkan prestasi Ia lebih baik sedikit daripada menggunakan Zon.

  // Angular 在 v5.0.0-beta.8 起可以通过配置不使用 Zone 
  import { platformBrowser } from '@angular/platform-browser';
  platformBrowser().bootstrapModuleFactory(AppModuleNgFactory, { ngZone: 'noop' });
Salin selepas log masuk

Timpa mekanisme lalai penyemak imbas

Angular akan mengatasi API peringkat rendah penyemak imbas semasa permulaan, seperti addEventListener, iaitu Penyemak Imbas fungsi yang digunakan untuk mendaftarkan semua acara pelayar, termasuk pengendalian klik. Angular akan menggantikan addEventListener dengan yang setara baharu ini:

// this is the new version of addEventListener                                    
function addEventListener(eventName, callback) { 
    // call the real addEventListener                
    callRealAddEventListener(eventName, function() { 
        //first call the original callback              
        callback(...);
        // and then run Angular-specific functionality
        var changed = angular.runChangeDetection();
        if (changed) {
            angular.reRenderUIPart();
        }
    });
}
Salin selepas log masuk

Baharu addEventListener menambah lebih banyak fungsi pada mana-mana pengendali acara: bukan sahaja panggilan balik berdaftar dipanggil, tetapi Angular mempunyai Peluang untuk menjalankan perubahan pengesanan dan kemas kini UI.

Menyokong API tak segerak pelayar

Menambal mekanisme penyemak imbas biasa berikut untuk menyokong pengesanan perubahan:

  • Semua acara penyemak imbas (klik sekali, alih tetikus , penekanan kekunci, dsb.)
  • setTimeout() dan setInterval()
  • Permintaan HTTP Ajax

Malah, Zone.js menampal banyak API Pelayar lain untuk telus mencetuskan pengesanan perubahan sudut, seperti Websockets.

Satu had mekanisme ini ialah jika atas sebab tertentu Zone.js tidak menyokong API penyemak imbas tak segerak, pengesanan perubahan tidak akan dicetuskan. Ini adalah kes untuk panggil balik IndexedDB, sebagai contoh.

Bagaimana mekanisme pengesanan perubahan lalai berfungsi?

Setiap komponen Sudut mempunyai pengesan perubahan yang berkaitan, yang dicipta apabila aplikasi bermula. Contohnya:

@Component({
    selector: 'todo-item',
    template: `<span class="todo noselect" 
       (click)="onToggle()">{{todo.owner.firstname}} - {{todo.description}}
       - completed: {{todo.completed}}</span>`
})
export class TodoItem {
    @Input()
    todo:Todo;

    @Output()
    toggle = new EventEmitter<Object>();

    onToggle() {
        this.toggle.emit(this.todo);
    }
}
Salin selepas log masuk

Komponen ini akan menerima objek Todo sebagai input dan memancarkan peristiwa apabila keadaan todo ditogol.

export class Todo {
    constructor(public id: number, 
        public description: string, 
        public completed: boolean, 
        public owner: Owner) {
    }
}
Salin selepas log masuk

Kita dapat melihat bahawa Todo mempunyai sifat owner, iaitu objek dengan dua sifat: firstname dan lastname.

Apakah rupa pengesan perubahan?

Kita sebenarnya boleh melihat rupa pengesan perubahan semasa masa jalan! Untuk melihatnya, cuma tambahkan beberapa kod dalam kelas Todo untuk mencetuskan titik putus apabila harta tertentu diakses.

Apabila titik putus dipukul, kita boleh melalui jejak tindanan dan melihat pengesanan perubahan:

Analisis ringkas mekanisme Pengesanan Perubahan dalam Angular

Pendekatan ini mungkin kelihatan pelik pada mulanya, semua pembolehubah adalah penamaan yang pelik. Tetapi setelah mengkaji lebih mendalam, kami mendapati bahawa ia melakukan sesuatu yang sangat mudah: untuk setiap ungkapan yang digunakan dalam templat, ia membandingkan nilai semasa bagi sifat yang digunakan dalam ungkapan itu dengan nilai sebelumnya bagi sifat tersebut.

Jika nilai atribut sebelum dan selepas berbeza, isChanged akan ditetapkan kepada benar, itu sahaja! Cukup banyak, ia membandingkan nilai dengan menggunakan kaedah yang dipanggil looseNotIdentical().

Bagaimana pula dengan objek bersarang owner?

Kita dapat melihat dalam kod pengesan perubahan bahawa owner sifat objek bersarang juga sedang disemak untuk perbezaan. Tetapi hanya atribut firstname yang dibandingkan, bukan atribut lastname. Ini kerana template tidak digunakan dalam komponen lastname! Begitu juga, atribut id peringkat atas Todo tidak dibandingkan atas sebab yang sama.

Dengan ini, kita boleh mengatakan dengan selamat:

默认情况下,Angular Change Detection 通过检查模板表达式的值是否已更改来工作。

我们还可以得出结论:

默认情况下,Angular 不做深度对象比较来检测变化,它只考虑模板使用的属性

为什么默认情况下更改检测会这样工作?

Angular 的主要目标之一是更加透明和易于使用,因此框架用户不必费尽心思调试框架并了解内部机制即可有效地使用它。

如果 Angular 默认更改检测机制基于组件输入的参考比较而不是默认机制,那会是什么情况?即使是像 TODO 应用程序这样简单的东西也很难构建:开发人员必须非常小心地创建一个新的 Todo,而不是简单地更新属性。

OnPush 变化检测策略

如果你觉得默认模式影响了性能,我们也可以自定义 Angular 更改检测。将组件更改检测策略更新为OnPush

@Component({
    selector: &#39;todo-list&#39;,
    changeDetection: ChangeDetectionStrategy.OnPush,
    template: ...
})
export class TodoList {
    ...
}
Salin selepas log masuk

现在让我们在应用程序中添加几个按钮:一个是通过直接改变列表的第一项来切换列表的第一项,另一个是向整个列表添加一个 Todo。代码如下所示:

@Component({
    selector: &#39;app&#39;,
    template: `<div>
                    <todo-list [todos]="todos"></todo-list>
               </div>
               <button (click)="toggleFirst()">Toggle First Item</button>
               <button (click)="addTodo()">Add Todo to List</button>`
})
export class App {
    todos:Array = initialData;

    constructor() {
    }

    toggleFirst() {
        this.todos[0].completed = ! this.todos[0].completed;
    }

    addTodo() {
        let newTodos = this.todos.slice(0);
        newTodos.push( new Todo(1, "TODO 4", 
            false, new Owner("John", "Doe")));
        this.todos = newTodos;
    }
}
Salin selepas log masuk

现在让我们看看这两个新按钮的行为:

  • 第一个按钮“切换第一项”不起作用!这是因为该toggleFirst()方法直接改变了列表中的一个元素。
    TodoList无法检测到这一点,因为它的输入参考todos没有改变
  • 第二个按钮确实有效!请注意,该方法addTodo()创建了 todo 列表的副本,然后将项目添加到副本中,最后将 todos 成员变量替换为复制的列表。这会触发更改检测,因为组件检测到其输入中的参考更改:它收到了一个新列表!
  • 在第二个按钮中,直接改变 todos 列表是行不通的!我们真的需要一个新的清单。

OnPush只是通过引用比较输入吗?

情况并非如此。当使用 OnPush 检测器时,框架将在 OnPush 组件的任何输入属性更改、触发事件或 Observable 触发事件时检查

尽管允许更好的性能,但OnPush如果与可变对象一起使用,则使用会带来很高的复杂性成本。它可能会引入难以推理和重现的错误。但是有一种方法可以使使用OnPush可行。

使用 Immutable.js 简化 Angular 应用程序的构建

如果我们只使用不可变对象和不可变列表来构建我们的应用程序,则可以OnPush透明地在任何地方使用,而不会遇到更改检测错误的风险。这是因为对于不可变对象,修改数据的唯一方法是创建一个新的不可变对象并替换之前的对象。使用不可变对象,我们可以保证:

  • 新的不可变对象将始终触发OnPush更改检测
  • 我们不会因为忘记创建对象的新副本而意外创建错误,因为修改数据的唯一方法是创建新对象

实现不可变的一个不错的选择是使用Immutable.js库。该库为构建应用程序提供了不可变原语,例如不可变对象(映射)和不可变列表。

避免变更检测循环:生产与开发模式

Angular 更改检测的重要属性之一是,与 AngularJs 不同,它强制执行单向数据流:当我们的控制器类上的数据更新时,更改检测运行并更新视图。

如何在 Angular 中触发变更检测循环?

一种方法是如果我们使用生命周期回调。例如,在TodoList组件中,我们可以触发对另一个组件的回调来更改其中一个绑定:

ngAfterViewChecked() {
    if (this.callback && this.clicked) {
        console.log("changing status ...");
        this.callback(Math.random());
    }
}
Salin selepas log masuk

控制台中将显示一条错误消息:

EXCEPTION: Expression &#39;{{message}} in App@3:20&#39; has changed after it was checked
Salin selepas log masuk

仅当我们在开发模式下运行 Angular 时才会抛出此错误消息。如果我们启用生产模式会发生什么? 在生产模式下,错误不会被抛出,问题也不会被发现。

在开发阶段始终使用开发模式会更好,因为这样可以避免问题。这种保证是以 Angular 总是运行两次变更检测为代价的,第二次检测这种情况。在生产模式下,变更检测只运行一次。

打开/关闭变化检测,并手动触发它

在某些特殊情况下,我们确实想要关闭更改检测。想象一下这样一种情况,大量数据通过 websocket 从后端到达。我们可能只想每 5 秒更新一次 UI 的某个部分。为此,我们首先将更改检测器注入到组件中:

constructor(private ref: ChangeDetectorRef) {
    ref.detach();
    setInterval(() => {
      this.ref.detectChanges();
    }, 5000);
  }
Salin selepas log masuk

正如我们所看到的,我们只是分离了变化检测器,这有效地关闭了变化检测。然后我们只需每 5 秒通过调用手动触发它detectChanges()

现在让我们快速总结一下我们需要了解的关于 Angular 变更检测的所有内容:它是什么,它是如何工作的以及可用的主要变更检测类型是什么。

概括

Angular 更改检测是一个内置的框架功能,可确保组件数据与其 HTML 模板视图之间的自动同步。

更改检测的工作原理是检测常见的浏览器事件,如鼠标点击、HTTP 请求和其他类型的事件,并确定每个组件的视图是否需要更新。

变更检测有两种类型:

  • 默认更改检测:Angular 通过比较事件发生前后的所有模板表达式值来决定是否需要更新视图,用于组件树的所有组件
  • OnPush 更改检测:这通过检测是否已通过组件输入或使用异步管道订阅的 Observable 将某些新数据显式推送到组件中来工作

Angular默认更改检测机制实际上与 AngularJs 非常相似:它比较浏览器事件之前和之后模板表达式的值,以查看是否有更改。它对所有组件都这样做。但也有一些重要的区别:

一方面,没有变化检测循环,也没有 AngularJs 中命名的摘要循环。这允许仅通过查看其模板和控制器来推理每个组件。

另一个区别是,由于变化检测器的构建方式,检测组件变化的机制要快得多。

最后,与 AngularJs 不同的是,变化检测机制是可定制的。

更多编程相关知识,请访问:编程教学!!

Atas ialah kandungan terperinci Analisis ringkas mekanisme Pengesanan Perubahan dalam Angular. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:juejin.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!