Mari kita bincangkan tentang pengesanan perubahan dalam Angular melalui contoh

青灯夜游
Lepaskan: 2022-02-08 10:38:36
ke hadapan
1574 orang telah melayarinya

Artikel ini akan membawa anda melalui pengesanan perubahan dalam Angular Kami akan mulakan dengan contoh kecil, dan kemudian secara beransur-ansur membincangkan pengesanan perubahan secara mendalam.

Mari kita bincangkan tentang pengesanan perubahan dalam Angular melalui contoh

Pengesanan perubahan dalam Angular ialah mekanisme yang digunakan untuk menyegerakkan keadaan UI aplikasi dengan keadaan data. Apabila logik aplikasi mengubah data komponen, nilai terikat pada sifat DOM dalam paparan juga berubah. Pengesan perubahan bertanggungjawab untuk mengemas kini paparan untuk mencerminkan model data semasa. [Tutorial berkaitan yang disyorkan: "tutorial sudut"]

Apa yang anda pelajari di atas kertas adalah cetek, tetapi anda tahu bahawa anda perlu melakukannya secara terperinci. Untuk memudahkan pembaca memahami, artikel ini dimulakan dengan contoh kecil dan kemudian dikembangkan langkah demi langkah. Contohnya adalah seperti berikut:

// app.component.ts
import { Component } from '@angular/core';
@Component({  selector: 'app-root', 
    templateUrl: './app.component.html', 
    styleUrls: ['./app.component.css']
})
export class AppComponent {
    title = 'aa';
    handleClick() {
        this.title = 'bb';
    }}
// app.componnet.html
<div (click)="handleClick()">{{title}}</div>
Salin selepas log masuk

Contohnya agak mudah, iaitu mengikat acara klik pada elemen div Mengklik pada elemen akan mengubah nilai pembolehubah title, dan paparan antara muka juga akan dikemas kini dengan sewajarnya. Bagaimanakah rangka kerja mengetahui masa ia perlu mengemas kini paparan, dan bagaimana ia mengemas kini paparan? Mari kita ketahui.

Apabila kita mengklik pada elemen div, fungsi handleClick akan dilaksanakan. Jadi bagaimanakah fungsi ini dicetuskan dalam aplikasi Sudut? Jika anda telah membaca artikel saya sebelum ini tentang pengenalan zone.js, anda akan tahu bahawa peristiwa klik dalam aplikasi Angular telah diambil alih oleh zone.js. Berdasarkan jawapan ini, jelas bahawa pelaksanaan mesti dicetuskan oleh zone.js pada mulanya, tetapi di sini kita perlu menganalisis lagi hubungan panggilan langsung dan mengembangkannya lapisan demi lapisan. Kod yang paling hampir dengan panggilan fungsi handleClick ialah kod berikut:

function wrapListener(listenerFn, ...) {
    return function wrapListenerIn_markDirtyAndPreventDefault(e) {
        let result = executeListenerWithErrorHandling(listenerFn, ...);
    }
}
Salin selepas log masuk

Dalam kod di atas, fungsi listenerFn menghala ke handleClick, tetapi ia juga merupakan parameter bagi wrapListener fungsi. Dalam contoh, elemen terikat pada acara klik Produk kompilasi templat yang berkaitan mungkin seperti berikut:

function AppComponent_Template(rf, ctx) { 
    ...... 
    i0["ɵɵlistener"]("click", function AppComponent_Template_div_click_0_listener() {
    return ctx.handleClick();
    })
}
Salin selepas log masuk

Apabila memuatkan aplikasi buat kali pertama, ia akan melaksanakan renderView dalam urutan, dan kemudian laksanakan executeTemplate, dan kemudian fungsi templat di atas akan dicetuskan Dengan cara ini, fungsi klik elemen dihantar ke parameter listenerFn. Pada ketika ini, kami memahami bahawa sumber pencetus fungsi klik ialah zone.js, tetapi penghantaran fungsi klik sebenar dilaksanakan oleh Angular Jadi bagaimanakah hubungan zone.js dan Angular? zone.js akan mengatur tugasan untuk setiap acara tak segerak Berdasarkan contoh dalam artikel ini, invokeTask dipanggil oleh kod berikut:

function forkInnerZoneWithAngularBehavior(zone) {
    zone._inner = zone._inner.fork({
    name: &#39;angular&#39;,
    properties: { &#39;isAngularZone&#39;: true },
    onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
        try {
            onEnter(zone);
            return delegate.invokeTask(target, task, ...);
        }
        finally {
            onLeave(zone);
        }
    }
    })
}
Salin selepas log masuk

Anda akan berasa biasa apabila anda melihatnya, kerana dalam Terdapat coretan kod yang serupa dalam artikel yang diperkenalkan oleh zone.js sebelum ini. Fungsi forkInnerZoneWithAngularBehavior dipanggil oleh pembina kelas NgZone. Setakat ini kami telah memperkenalkan NgZone, protagonis pengesanan perubahan Sudut, yang merupakan enkapsulasi ringkas zone.js.

Sekarang kita tahu bagaimana fungsi klik dalam contoh dilaksanakan, jika data aplikasi berubah selepas fungsi itu dilaksanakan, bagaimanakah paparan boleh dikemas kini dalam masa? Mari kembali ke fungsi forkInnerZoneWithAngularBehavior yang dinyatakan di atas dalam blok pernyataan try finally, melaksanakan fungsi invokeTask akhirnya akan melaksanakan fungsi onLeave(zone). Analisis lanjut dapat melihat bahawa fungsi onLeave akhirnya memanggil fungsi checkStable:

function checkStable(zone) {
    zone.onMicrotaskEmpty.emit(null);
}
Salin selepas log masuk

Sejajar dengan itu, acara ApplicationRef ini dilanggan dalam kelas emit pembina:

class ApplicationRef {
    /** @internal */
    constructor() {
        this._zone.onMicrotaskEmpty.subscribe({
            next: () => {
                this._zone.run(() => {
                    this.tick();
                });
            }        
        }); 
}
Salin selepas log masuk

Dalam fungsi panggil balik berkaitan langganan, adakah this.tick() kelihatan biasa? Jika anda telah membaca artikel saya sebelum ini tentang fungsi kitaran hayat Angular, maka anda pasti akan mempunyai tanggapan bahawa ia adalah panggilan utama untuk mencetuskan kemas kini paparan. Walaupun fungsi ini disebut dalam artikel pengenalan kitaran hayat, fokus artikel ini adalah pengesanan perubahan Oleh itu, walaupun fungsinya sama, fokusnya telah berubah sedikit. this.tickJujukan panggilan yang berkaitan adalah kira-kira seperti ini:

this.tick() ->
view.detectChanges() -> 
renderComponentOrTemplate() ->
refreshView()
Salin selepas log masuk

Di sini refreshView adalah lebih penting dan dianalisis secara berasingan:

function refreshView(tView, lView, templateFn, context) {
    ......
    if (templateFn !== null) {
        // 关键代码1
        executeTemplate(tView, lView, templateFn, ...);  }
    ......
    if (components !== null) {
        // 关键代码2
        refreshChildComponents(lView, components);
    }
}
Salin selepas log masuk

Dalam proses ini, refreshView fungsi akan dipanggil Kali kedua, kali pertama anda masukkan ialah cawangan kod kunci 2, dan kemudian anda memanggil fungsi berikut untuk memasukkan semula fungsi refreshView:

refreshChildComponents() ->
refreshChildComponents() ->
refreshComponent() ->
refreshView()
Salin selepas log masuk

Kali kedua anda masukkan fungsi refreshView ialah kod kunci 1 Bercabang, iaitu, fungsi executeTemplate dilaksanakan. Apa yang akhirnya dilaksanakan oleh fungsi ini ialah fungsi AppComponent_Template dalam produk kompilasi templat:

function AppComponent_Template(rf, ctx) {
    if (rf & 1) {
        // 条件分支1
        i0["ɵɵelementStart"](0, "div", 0);
        i0["ɵɵlistener"]("click", function AppComponent_Template_div_click_0_listener() {
            return ctx.handleClick();
        });
        i0["ɵɵtext"](1);
        i0["ɵɵelementEnd"]();
    }
    if (rf & 2) {
        // 条件分支2
        i0["ɵɵadvance"](1);
        i0["ɵɵtextInterpolate"](ctx.title);
    }
}
Salin selepas log masuk

Jika masih ada pembaca yang tidak pasti dari mana datangnya fungsi dalam produk kompilasi templat di atas, adalah disyorkan untuk membaca artikel sebelumnya tentang suntikan kebergantungan Artikel yang menerangkan prinsip tidak akan diulang kerana keterbatasan ruang. Pada masa ini, fungsi AppComponent_Template melaksanakan kod dalam cawangan bersyarat 2. Fungsi fungsi ɵɵadvance adalah untuk mengemas kini nilai indeks yang berkaitan untuk memastikan elemen yang betul ditemui. Tumpuan di sini adalah pada fungsi ɵɵtextInterpolate, yang akhirnya memanggil fungsi ɵɵtextInterpolate1:

function ɵɵtextInterpolate1(prefix, v0, suffix) {
    const lView = getLView();
    // 关键代码1
    const interpolated = interpolation1(lView, prefix, v0, suffix);
    if (interpolated !== NO_CHANGE) {
        // 关键代码2
        textBindingInternal(lView, getSelectedIndex(), interpolated);
    }
    return ɵɵtextInterpolate1;
}
Salin selepas log masuk

值得指出的是,该函数名末尾是数字1,这是因为还有类似的ɵɵtextInterpolate2ɵɵtextInterpolate3等等,Angular 内部根据插值表达式的数量调用不同的专用函数,本文示例中文本节点的插值表达式数量为1,因此实际调用的是ɵɵtextInterpolate1函数。该函数主要做了两件事,关键代码1作用是比较插值表达式值有没有更新,关键代码2则是更新文本节点的值。先来看看关键代码1的函数interpolation1,它最终调用的是:

function bindingUpdated(lView, bindingIndex, value) {
    const oldValue = lView[bindingIndex];
    if (Object.is(oldValue, value)) {
        return false;
    }
    else {
        lView[bindingIndex] = value;
        return true;
    }
}
Salin selepas log masuk

变更检测前的文本节点值称之为oldValue, 该值存储在lView中,lView我在之前的文章中也提到过,忘记了的读者可以去看看lView的作用。bindingUpdated首先会比较新值和旧值,比较的方法便是Object.is。如果新值旧值没有变化,则返回false。如果有变化,则更新lView中存储的值,并返回true。关键代码2的函数textBindingInternal最终调用的是下述函数:

function updateTextNode(renderer, rNode, value) {
    ngDevMode && ngDevMode.rendererSetText++;
    isProceduralRenderer(renderer) ? renderer.setValue(rNode, value) : rNode.textContent = value;
}
Salin selepas log masuk

走完上述流程,我们点击div元素时,界面显示内容便会由aa变为bb,即完成了从应用数据的变更到 UI 状态的同步更新,这便是 Angular 最基本的变更检测过程了。

因篇幅限制,本文所举示例比较简单,但 Angular 的变更检测还有很多没有讲到。比如,如果应用是由若干个组件组成的,父子组件间的变更检测如何进行,以及如何通过策略优化变更检测等等。如果有对这方面感兴趣的朋友,欢迎关注我的个人公众号【朱玉洁的博客】,后续将在那里分享更多前端知识。

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

Atas ialah kandungan terperinci Mari kita bincangkan tentang pengesanan perubahan dalam Angular melalui contoh. 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!