模塊
在 ./src/modules
裏面,定義了一系列的模塊 , 這些模塊定義了相應的鉤子 。這些鉤子會在 patch
的不同階段觸發,以完成相應模塊的功能處理
瞭解生命週期更多的內容,請查看 鉤子
主要的模塊有 :
- attributes.ts
- class.ts
- dataset.ts
- eventlisteners.ts
- hero.ts
- module.ts
- props.ts
- style.ts
其中 attributes
class
dataset
props
四個比較簡單,都是定義了 create
update
兩個鉤子,
eventlisteners
hero
style
這三個模塊就複雜一點。
另外 module.ts
只是定義了這些模塊所用到的一些鉤子
// 定義模塊的鉤子
export interface Module {
pre: PreHook;
create: CreateHook;
update: UpdateHook;
destroy: DestroyHook;
remove: RemoveHook;
post: PostHook;
}
接下來我們來看看其他的模塊
attributes 模塊
文件位置 : ./src/modules/attributes.ts
我們先拉到最後
// 創建以及更新的鉤子
export const attributesModule = {
create: updateAttrs,
update: updateAttrs
} as Module;
export default attributesModule;
attributesModule
導出了兩個方法, 都是調用了 updateAttrs
。
這個表示,在創建元素的時候,以及更新的時候,都會觸發這兩個鉤子,來更新 attribute
。
updateAttrs
updateAttrs
主要接受兩個參數,oldVnode
、vnode
。
主要邏輯如下:
- 遍歷新
vnode
所有的屬性,判斷在oldVnode
中是否相等,修改不相等的屬性 - 刪除不存在於
vnode
的屬性
代碼如下:
/**
* 更新屬性
*/
function updateAttrs(oldVnode: VNode, vnode: VNode): void {
var key: string,
elm: Element = vnode.elm as Element,
oldAttrs = (oldVnode.data as VNodeData).attrs,
attrs = (vnode.data as VNodeData).attrs;
if (!oldAttrs && !attrs) return;
if (oldAttrs === attrs) return;
oldAttrs = oldAttrs || {};
attrs = attrs || {};
// update modified attributes, add new attributes
// 遍歷新的屬性,修改不相等的
for (key in attrs) {
const cur = attrs[key];
const old = oldAttrs[key];
if (old !== cur) {
if (cur === true) {
elm.setAttribute(key, '');
} else if (cur === false) {
elm.removeAttribute(key);
} else {
if (key.charCodeAt(0) !== xChar) {
// 如果不是 x 開頭
elm.setAttribute(key, cur);
} else if (key.charCodeAt(3) === colonChar) {
// Assume xml namespace
elm.setAttributeNS(xmlNS, key, cur);
} else if (key.charCodeAt(5) === colonChar) {
// Assume xlink namespace
elm.setAttributeNS(xlinkNS, key, cur);
} else {
elm.setAttribute(key, cur);
}
}
}
}
// remove removed attributes
// use `in` operator since the previous `for` iteration uses it (.i.e. add even attributes with undefined value)
// the other option is to remove all attributes with value == undefined
// 刪除多餘的屬性
for (key in oldAttrs) {
if (!(key in attrs)) {
elm.removeAttribute(key);
}
}
}
class 模塊
文件位置 : ./src/modules/class.ts
與 attribute
類似 , class
也是定義了 create
和 update
兩個鉤子,統一由 updateClass
處理
這塊邏輯比較簡單 ,直接看代碼吧
function updateClass(oldVnode: VNode, vnode: VNode): void {
var cur: any,
name: string,
elm: Element = vnode.elm as Element,
oldClass = (oldVnode.data as VNodeData).class,
klass = (vnode.data as VNodeData).class;
// 新老的 className 都沒有
if (!oldClass && !klass) return;
// 新老的 className 沒變
if (oldClass === klass) return;
oldClass = oldClass || {};
klass = klass || {};
// 刪除不存在與新的 classList 的 className
for (name in oldClass) {
if (!klass[name]) {
elm.classList.remove(name);
}
}
// 新增 或刪除 class
for (name in klass) {
cur = klass[name];
if (cur !== oldClass[name]) {
(elm.classList as any)[cur ? 'add' : 'remove'](name);
}
}
}
dataset 模塊
文件位置 : ./src/modules/dataset.ts
與 attribute
類似 , dataset
也是定義了 create
和 update
兩個鉤子,統一由 updateDataset
處理
這塊邏輯比較簡單 ,直接看代碼吧
const CAPS_REGEX = /[A-Z]/g;
/**
* 更新或創建 dataset
*/
function updateDataset(oldVnode: VNode, vnode: VNode): void {
let elm: HTMLElement = vnode.elm as HTMLElement,
oldDataset = (oldVnode.data as VNodeData).dataset,
dataset = (vnode.data as VNodeData).dataset,
key: string;
// 不變的情況下不處理
if (!oldDataset && !dataset) return;
if (oldDataset === dataset) return;
oldDataset = oldDataset || {};
dataset = dataset || {};
const d = elm.dataset;
// 刪除多餘的 dataset
for (key in oldDataset) {
if (!dataset[key]) {
if (d) {
if (key in d) {
delete d[key];
}
} else {
// 將駝峯式改爲中劃線分割 eg: userName ----> user-name
elm.removeAttribute(
'data-' + key.replace(CAPS_REGEX, '-$&').toLowerCase()
);
}
}
}
// 修改有變化的 dataset
for (key in dataset) {
if (oldDataset[key] !== dataset[key]) {
if (d) {
d[key] = dataset[key];
} else {
elm.setAttribute(
// 將駝峯式改爲中劃線分割 eg: userName ----> user-name
'data-' + key.replace(CAPS_REGEX, '-$&').toLowerCase(),
dataset[key]
);
}
}
}
}
props 模塊
文件位置 : ./src/modules/props.ts
與 attribute
類似 , props
也是定義了 create
和 update
兩個鉤子,統一由 updateProps
處理
這塊邏輯比較簡單 ,直接看代碼吧
function updateProps(oldVnode: VNode, vnode: VNode): void {
var key: string,
cur: any,
old: any,
elm = vnode.elm,
oldProps = (oldVnode.data as VNodeData).props,
props = (vnode.data as VNodeData).props;
if (!oldProps && !props) return;
if (oldProps === props) return;
oldProps = oldProps || {};
props = props || {};
// 刪除多餘的屬性
for (key in oldProps) {
if (!props[key]) {
delete (elm as any)[key];
}
}
// 添加新增的屬性
for (key in props) {
cur = props[key];
old = oldProps[key];
// key爲value的情況,再判斷是否value有變化
// key不爲value的情況,直接更新
if (old !== cur && (key !== 'value' || (elm as any)[key] !== cur)) {
(elm as any)[key] = cur;
}
}
}
eventlisteners 模塊
eventlisteners 這一塊內容稍微多一點,故將其獨立出來一個章節。 傳送門 : 事件
style 模塊
待續。。。
hero 模塊
待續。。。