鐵人賽最後一天!
今天要跟各位分享的呢,是昨天與前天 Banana in a box 系列的延伸,關於 Angular 怎麼處理 event 與 property binding。
因為是延伸的內容,所以我會繼續以昨天的程式碼作為範例!
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵdefineComponent"]({
// ... 略
consts: [[3, "sliderValue", "sliderValueChange"]],
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](
0,
"app-slider-wrapper",
0
);
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵlistener"](
"sliderValueChange",
function AppComponent_Template_app_slider_wrapper_sliderValueChange_0_listener(
$event
) {
return (ctx.slider = $event);
}
);
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](1, "div");
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](2, "label");
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵtext"](3, "Slider value: ");
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementStart"](4, "label");
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵtext"](5);
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵelementEnd"]();
}
if (rf & 2) {
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵproperty"](
"sliderValue",
ctx.slider
);
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵadvance"](5);
_angular_core__WEBPACK_IMPORTED_MODULE_0__["ɵɵtextInterpolate"](
ctx.slider
);
}
}
});
↑ Block 1
首先從編譯後的程式碼可以看到有 ɵɵlistener
這個 instruction,而這個 instruction 的實作內容如下:
export function ɵɵlistener(
eventName: string,
listenerFn: (e?: any) => any,
useCapture = false,
eventTargetResolver?: GlobalTargetResolver
): typeof ɵɵlistener {
const lView = getLView();
const tView = getTView();
const tNode = getCurrentTNode()!;
listenerInternal(
tView,
lView,
lView[RENDERER],
tNode,
eventName,
listenerFn,
useCapture,
eventTargetResolver
);
return ɵɵlistener;
}
↑ Block 2
ɵɵlistener 傳入的參數除了 eventName(本例是 sliderValueChange)、listenerFn 之外,還可以傳入 useCapture 與 eventTargetResolver,後兩者除非是有特殊需求,否則不會用到。
將 eventName 與 listenerFn 傳入之後 Angular 會透過 getLView
、getTView
、getCurrentTNode
方法來取得當前的 LView、TView 與 TNode。
最後則是呼叫 listenerInternal
函式(Link)並將相關的資訊傳遞給它,來做以下的事:
ObjectOrientedRenderer3
還是 ProceduralRenderer3
,不同的 renderer 會有不同的處理方式。Angular 會在 update block 的時候呼叫 ɵɵproperty instruction 來處理 property 相關的事情,以下是它的實作內容(Link):
export function ɵɵproperty<T>(
propName: string, value: T, sanitizer?: SanitizerFn|null): typeof ɵɵproperty {
const lView = getLView();
const bindingIndex = nextBindingIndex();
if (bindingUpdated(lView, bindingIndex, value)) {
const tView = getTView();
const tNode = getSelectedTNode();
elementPropertyInternal(
tView, tNode, lView, propName, value, lView[RENDERER], sanitizer, false);
ngDevMode && storePropertyBindingMetadata(tView.data, tNode, propName, bindingIndex);
}
return ɵɵproperty;
}
↑ Block 3
ɵɵproperty 在執行更新的操作之前,會先透過 bindingUpdated
函式來確認傳入的值跟先前在 lView 內紀錄的值是否相同,如果有變動的話才會執行 elementPropertyInternal 函式來更新 property,否則的話就直接回傳 ɵɵproperty instruction,讓 Angular 可以透過 ɵɵproperty('a', 'v1')('b', 'v2') 這樣的方法來設定 property binding。
以上就是今天的「你可以不用知道的 Angular」!簡潔扼要的說明了 Angular 是怎麼實作 event binding 與 property binding。
30 天,有夠累。
主要是因為自己籌備期間因為剛開始翻閱 Angular 原始碼,艱澀難懂到讓我一直發懶,所以直到正式開賽前一天才覺得應該擬定每天的內容,接著就開始了每天絞盡腦汁產出一篇的生活。其中有幾篇真的是困難重重,像是最開頭的 Dependency Injection 系列,那個原始碼我真的是遲遲無法參悟阿……
總之感謝各位讀者這 30 天的陪伴,因為比賽時間限制,所以我做了一些艱難的抉擇,像是不小心忽略部分說明之類的。
發佈在鐵人賽的這系列未來不會有其他更新,但我會在個人的 Medium(這裡)逐步將內容完善!熱烈歡迎有興趣的讀者跟我一起轉移陣地噢!
再次感謝!
恭喜寫出 compiler 等級文章的大大完賽!我以為會看 source code 已經很強了,能看懂 compile 後的程式碼,根本神之領域 XD
看到頭幾天的文章我就決定投降了呢...
這境界 高
雖然已經私下恭喜過了
但是就是要佔個樓 在恭喜一次!!太強大了!
EP 根本就是 Excellent Peter !!!!!! (硬要XDDDD)