前言
Moon目前實(shí)現(xiàn)的內(nèi)置指令有八個(gè):if/show/for/on/model/html/literal/mask掘鄙,也可以讓用戶自定義指令鲫趁。
beforeGenerate ->duringPropGenerate->afterGenerate
所有指令都是在generateProps或generateNode里調(diào)用,分別按beforeGenerate ->duringPropGenerate->afterGenerate 的狀態(tài)執(zhí)行指令邏輯戈二,我們節(jié)選相關(guān)的代碼段:
let beforeGenerate = null;
for(propKey in props) {
const prop = props[propKey];
const name = prop.name;
if((specialDirective = specialDirectives[name]) !== undefined
&& (beforeGenerate = specialDirective.beforeGenerate) !== undefined) {
beforeGenerate(prop, node, parent, state);
}
}
顧名思義,這個(gè)beforeGenerate 值得就是compile過程中g(shù)enerate執(zhí)行之前祟偷,這時(shí)ast還沒有轉(zhuǎn)換成code讥耗。我們可以看到,傳入了四個(gè)參數(shù):prop, node, parent, state望伦,從state參數(shù)我們可以聯(lián)想到ast的state變量林说,所以更加確認(rèn)這是ast層面的操作無疑了。
let afterGenerate = null;
let duringPropGenerate = null;
for(propKey in props) {
const prop = props[propKey];
const name = prop.name;
if((specialDirective = specialDirectives[name]) !== undefined) {
if((afterGenerate = specialDirective.afterGenerate) !== undefined) {
specialDirectivesAfter[name] = {
prop: prop,
afterGenerate: afterGenerate
};
hasSpecialDirectivesAfter = true;
}
if((duringPropGenerate = specialDirective.duringPropGenerate) !== undefined) {
propsCode += duringPropGenerate(prop, node, state);
}
node.meta.shouldRender = true;
}
......
if (specialDirectivesAfter !== null) {
var specialDirectiveAfter;
for (var specialDirectiveKey in specialDirectivesAfter) {
specialDirectiveAfter = specialDirectivesAfter[specialDirectiveKey];
call = specialDirectiveAfter.afterGenerate(specialDirectiveAfter.prop, call, node, state);
}
}
}
而在afterGenerate和 duringPropGenerate這兩個(gè)時(shí)期屯伞,code已經(jīng)生成了或者正在生成中腿箩,操作的就是code了。這時(shí)我們發(fā)現(xiàn)duringPropGenerate比beforeGenerate少傳了個(gè)parent參數(shù)劣摇,因?yàn)闃浣Y(jié)構(gòu)中才有父元素嘛~
而afterGenerate又比duringPropGenerate多傳了個(gè)call參數(shù)(實(shí)際上它是一段code)珠移,這更加說明了afterGenerate是在generate完code后再往上面加code的過程。
內(nèi)置指令
m-if
m-if在afterGenerate里生成一個(gè)三元表達(dá)式末融,若條件為真就生成vnode的code钧惧,否則是空的VNode:
specialDirectives["m-if"] = {
afterGenerate: function(prop, code, vnode, state) {
var value = prop.value;
compileTemplateExpression(value, state.dependencies);
return (value + " ? " + code + " : " + emptyVNode);
}
}
m-show
m-show也是生成一個(gè)三元表達(dá)式,但它是直接修改el.style.display勾习,和那三個(gè)過程無關(guān):
directives["m-show"] = function(el, val, vnode) {
el.style.display = (val ? '' : 'none');
}
m-for
m-for在beforeGenerate里設(shè)置flatten數(shù)組的標(biāo)志位,在afterGenerate里迭代生成code:
specialDirectives["m-for"] = {
beforeGenerate: function(prop, vnode, parentVNode, state) {
// Setup Deep Flag to Flatten Array
parentVNode.deep = true;
},
afterGenerate: function(prop, code, vnode, state) {
// Get dependencies
var dependencies = state.dependencies;
// Get Parts
var parts = prop.value.split(" in ");
// Aliases
var aliases = parts[0].split(",");
// The Iteratable
var iteratable = parts[1];
compileTemplateExpression(iteratable, dependencies);
// Get any parameters
var params = aliases.join(",");
// Add aliases to scope
for (var i = 0; i < aliases.length; i++) {
var aliasIndex = dependencies.indexOf(aliases[i]);
if (aliasIndex !== -1) {
dependencies.splice(aliasIndex, 1);
}
}
// Use the renderLoop runtime helper
// 等同于return `Moon.renderLoop(${iteratable}, function(${params}) {return${code};})`
return ("Moon.renderLoop(" + iteratable + ", function(" + params + ") { return " + code + "; })");
}
}
我們可以發(fā)現(xiàn)它會把形似"m-if=(item,key) in iteratable"中的item/key當(dāng)做params解析出來浓瞪,然后返回一份由renderLoop和code生成的新code。
renderLoop
這里用到了renderLoop來迭代生成code:
Moon.renderLoop = function(iteratable, item) {
var items = null;
if (Array.isArray(iteratable)) {
items = new Array(iteratable.length);
// Iterate through the array
for (var i = 0; i < iteratable.length; i++) {
items[i] = item(iteratable[i], i);
}
} else if (typeof iteratable === "object") {
items = [];
// Iterate through the object
for (var key in iteratable) {
items.push(item(iteratable[key], key));
}
} else if (typeof iteratable === "number") {
items = new Array(iteratable);
// Repeat a certain amount of times
for (var i$1 = 0; i$1 < iteratable; i$1++) {
items[i$1] = item(i$1 + 1, i$1);
}
}
return items;
}
很明顯巧婶,它對應(yīng)著我們api中使用for指令時(shí)的三種可迭代情況:數(shù)組乾颁、對象、數(shù)字艺栈。就像lodash封裝數(shù)組和對象為集合方法一樣英岭,針對數(shù)組和數(shù)字,就使用for循環(huán)迭代湿右,針對對象就使用for...in迭代巴席。
另外我們也明顯地感受到了這位作者不是前端出身的,因?yàn)樗鼡?dān)心i$1會覆蓋i變量所以才這樣命名的诅需,實(shí)際上js的var可以重復(fù)聲明的漾唉。
m-on
m-on在beforeGenerate里獲取事件名和回調(diào)以及事件修飾符(如prevent)荧库,然后生成調(diào)用事件的代碼并添加監(jiān)聽回調(diào):
specialDirectives["m-on"] = {
beforeGenerate: function(prop, vnode, parentVNode, state) {
// Extract Event, Modifiers, and Parameters
var value = prop.value;
var meta = prop.meta;
var methodToCall = value;
var rawModifiers = meta.arg.split(".");
var eventType = rawModifiers.shift();
var params = "event";
var rawParams = methodToCall.split("(");
if (rawParams.length > 1) {
// Custom parameters detected, update method to call, and generated parameter code
methodToCall = rawParams.shift();
params = rawParams.join("(").slice(0, -1);
compileTemplateExpression(params, state.dependencies);
}
// Generate any modifiers
var modifiers = "";
for (var i = 0; i < rawModifiers.length; i++) {
var eventModifierCode = eventModifiersCode[rawModifiers[i]];
if (eventModifierCode === undefined) {
modifiers += "if(Moon.renderEventModifier(event.keyCode, \"" + (rawModifiers[i]) + "\") === false) {return null;};"
} else {
modifiers += eventModifierCode;
}
}
// Final event listener code
var code = "function(event) {" + modifiers + "instance.callMethod(\"" + methodToCall + "\", [" + params + "])}";
addEventListenerCodeToVNode(eventType, code, vnode);
}
}
m-model
m-model在beforeGenerate里獲取依賴、根據(jù)html類型綁定事件赵刑,然后動態(tài)檢測依賴的getter分衫,生成代碼(靠set改變值),添加事件監(jiān)聽:
specialDirectives["m-model"] = {
beforeGenerate: function(prop, vnode, parentVNode, state) {
......
}
因?yàn)閷?shí)際上model指令是bind指令和on指令的結(jié)合語法糖般此,所以只能適用于特殊的表單元素:input蚪战、checkbox、radio等铐懊,而Moon并沒有實(shí)現(xiàn)bind指令邀桑,所以在這里實(shí)現(xiàn)地頗為復(fù)雜,不過我們可以大致地知道bind value或者checked值它用getter和setter的方式實(shí)現(xiàn)了科乎,on的部分監(jiān)聽change事件壁畸。
m-html
m-html對應(yīng)v-html,它會在beforeGenerate里設(shè)置先通過compileTemplateExpression收集依賴,再設(shè)置innerHTML:
specialDirectives["m-html"] = {
beforeGenerate: function(prop, vnode, parentVNode, state) {
var value = prop.value;
var dom = vnode.props.dom;
if (dom === undefined) {
vnode.props.dom = dom = {};
}
compileTemplateExpression(value, state.dependencies);
dom.innerHTML = "(\"\" + " + value + ")";
}
}
它沒有返回值,而是直接操作的dom茅茂。
m-literal
m-literal對應(yīng)v-text捏萍,在duringPropGenerate里先收集依賴,再通過renderClass生成帶樣式的文本:
specialDirectives["m-literal"] = {
duringPropGenerate: function(prop, vnode, state) {
var propName = prop.meta.arg;
var propValue = prop.value;
compileTemplateExpression(propValue, state.dependencies);
if (state.hasAttrs === false) {
state.hasAttrs = true;
}
if (propName === "class") {
// Detected class, use runtime class render helper
return ("\"class\": Moon.renderClass(" + propValue + "), ");
} else {
// Default literal attribute
return ("\"" + propName + "\": " + propValue + ", ");
}
}
};
如果帶樣式空闲,就通過renderClass生成樣式代碼令杈,否則直接一段文本代碼。
renderClass
它負(fù)責(zé)生成樣式中class的code:
Moon.renderClass = function(classNames) {
if (typeof classNames === "string") {
// If they are a string, no need for any more processing
return classNames;
}
var renderedClassNames = "";
if (Array.isArray(classNames)) {
// It's an array, so go through them all and generate a string
for (var i = 0; i < classNames.length; i++) {
renderedClassNames += (Moon.renderClass(classNames[i])) + " ";
}
} else if (typeof classNames === "object") {
// It's an object, so to through and render them to a string if the corresponding condition is truthy
for (var className in classNames) {
if (classNames[className]) {
renderedClassNames += className + " ";
}
}
}
// Remove trailing space and return
renderedClassNames = renderedClassNames.slice(0, -1);
return renderedClassNames;
}
可以看出來它同樣是分了三種情況:class是字符串碴倾、數(shù)組和對象的情況逗噩。如果是字符串直接返回class名,如果是數(shù)組就遞歸拼接跌榔,如果是對象(形似{'active':isTrue})就根據(jù)值的真假拼接是鍵的class名异雁。
m-mask
m-mask目前是空的,按字面意思是面具指令矫户,猜測是類似v-mask的輸入自動格式化指令片迅。
自定義指令
用戶可以自定義一個(gè)指令,它存儲在directives里皆辽。
比如我們定義一個(gè)m-my指令,讓一個(gè)input元素在頁面加載的時(shí)候自動聚焦:
Moon.directive('my',
function(node, vnode, parentVNode) {
node.focus()
}
)
它會先把m-my存儲到directives里
Moon.directive = function(name, action) {
directives["m-" + name] = action;
}
在diffProps的里面會執(zhí)行m-my對應(yīng)的函數(shù):
從這里可以看出柑蛇,它傳參數(shù)的方式和三種過程都不一樣。
總結(jié)
指令在angular中出現(xiàn)的比vue略早一些驱闷,在ng里指令還分結(jié)構(gòu)性指令和非結(jié)構(gòu)性指令耻台。但在moon中,指令也分為操作ast或code和不操作的指令空另。