knockout執(zhí)行過(guò)程是怎么樣的呢?你是不是還很迷惑。這里我就結(jié)合我自己學(xué)習(xí)knockout的總結(jié)和自己的開(kāi)發(fā)經(jīng)驗(yàn)給大家講解一些,有源碼喲。
一、執(zhí)行流程
一、主要類分析
2.1. 在applyBindings中,創(chuàng)建bindingContext,然后執(zhí)行applyBindingsToNodeAndDescendantsInternal方法
2.2. 在applyBindinsToNodeAndDescendantsInteranl方法,主要完成當(dāng)前Node的綁定,以及子Node的綁定
function applyBindingsToNodeAndDescendantsInternal (bindingContext, nodeVerified, bindingContextMayDifferFromDomParentElement) {
var shouldBindDescendants = true;
// Perf optimisation: Apply bindings only if...
// (1) We need to store the binding context on this node (because it may differ from the DOM parent node's binding context)
// Note that we can't store binding contexts on non-elements (e.g., text nodes), as IE doesn't allow expando properties for those
// (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
var isElement = (nodeVerified.nodeType === 1);
if (isElement) // Workaround IE <= 8 HTML parsing weirdness ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);
var shouldApplyBindings = (isElement && bindingContextMayDifferFromDomParentElement) // Case (1)
|| ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified); // Case (2)
if (shouldApplyBindings)
shouldBindDescendants = applyBindingsToNodeInternal(nodeVerified, null, bindingContext, bindingContextMayDifferFromDomParentElement)['shouldBindDescendants'];
if (shouldBindDescendants && !bindingDoesNotRecurseIntoElementTypes[ko.utils.tagNameLower(nodeVerified)]) {
// We're recursing automatically into (real or virtual) child nodes without changing binding contexts. So,
// * For children of a *real* element, the binding context is certainly the same as on their DOM .parentNode,
// hence bindingContextsMayDifferFromDomParentElement is false
// * For children of a *virtual* element, we can't be sure. Evaluating .parentNode on those children may
// skip over any number of intermediate virtual elements, any of which might define a custom binding context,
// hence bindingContextsMayDifferFromDomParentElement is true
applyBindingsToDescendantsInternal(bindingContext, nodeVerified, /* bindingContextsMayDifferFromDomParentElement: */ !isElement);
}
}
2.3. 進(jìn)入applyBindingsToNodeInternal方法,其中會(huì)調(diào)用bindingProvider的getBindingsAccessors方法(用于分析和獲取bindings數(shù)據(jù),主要分析data-bind屬性)
2.4. 創(chuàng)建dependentObservable對(duì)象(依賴監(jiān)控對(duì)象)
var bindings;
if (sourceBindings && typeof sourceBindings !== 'function') {
bindings = sourceBindings;
} else {
var provider = ko.bindingProvider['instance'],
getBindings = provider['getBindingAccessors'] || getBindingsAndMakeAccessors; //自定義BingindHandler
// Get the binding from the provider within a computed observable so that we can update the bindings whenever
// the binding context is updated or if the binding provider accesses observables.
var bindingsUpdater = ko.dependentObservable( //依賴監(jiān)控對(duì)象
function() { //做了read、write處理,實(shí)現(xiàn)雙向關(guān)聯(lián)(只做了read),默認(rèn)會(huì)執(zhí)行一次read的。
bindings = sourceBindings ? sourceBindings(bindingContext, node) : getBindings.call(provider, node, bindingContext);
// Register a dependency on the binding context to support observable view models.
if (bindings && bindingContext._subscribable)
bindingContext._subscribable();
return bindings;
},
null, { disposeWhenNodeIsRemoved: node }
);
if (!bindings || !bindingsUpdater.isActive())
bindingsUpdater = null;
}
2.5. 然后分析bindings中每個(gè)binding,并將init、update方法創(chuàng)建為一個(gè)dependentObservable對(duì)象(其中bindings的執(zhí)行是有順序的)。
二、BindingProvider分析
此類主要提供關(guān)于data-bind屬性的解析,主要提供getBindings、getBindingsAccessors、parseBindingsString(內(nèi)容使用)方法輔助binding過(guò)程。創(chuàng)建function對(duì)象:
function createBindingsStringEvaluator(bindingsString, options) {
// Build the source for a function that evaluates "expression"
// For each scope variable, add an extra level of "with" nesting
// Example result: with(sc1) { with(sc0) { return (expression) } }
var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString, options),
functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}"; //執(zhí)行with表達(dá)式
return new Function("$context", "$element", functionBody);
}
1、在分析bindings時(shí),會(huì)區(qū)分NodeType為1、8的類型。如果是8(注釋)就會(huì)調(diào)用virtualElements類的virtualNodeBindingValue方法來(lái)分析binding結(jié)果。
四、bindings的排序技巧
查看自定義binding是否有after屬性,如果存在則進(jìn)行遞歸操作:
function topologicalSortBindings(bindings) {
// Depth-first sort
var result = [], // The list of key/handler pairs that we will return
bindingsConsidered = {}, // A temporary record of which bindings are already in 'result'
cyclicDependencyStack = []; // Keeps track of a depth-search so that, if there's a cycle, we know which bindings caused it
ko.utils.objectForEach(bindings, function pushBinding(bindingKey) {
if (!bindingsConsidered[bindingKey]) {
var binding = ko['getBindingHandler'](bindingKey);
if (binding) {
// First add dependencies (if any) of the current binding
if (binding['after']) { //依賴檢測(cè),將after的引用先添加到數(shù)組中,然后再添加當(dāng)前項(xiàng) cyclicDependencyStack.push(bindingKey);
ko.utils.arrayForEach(binding['after'], function(bindingDependencyKey) {
if (bindings[bindingDependencyKey]) {
if (ko.utils.arrayIndexOf(cyclicDependencyStack, bindingDependencyKey) !== -1) {
throw Error("Cannot combine the following bindings, because they have a cyclic dependency: " + cyclicDependencyStack.join(", "));
} else {
pushBinding(bindingDependencyKey);
}
}
});
cyclicDependencyStack.length--;
}
// Next add the current binding result.push({ key: bindingKey, handler: binding });
}
bindingsConsidered[bindingKey] = true;
}
});
五、注意
1.所有的dependentObservable對(duì)象,在創(chuàng)建的過(guò)程中都會(huì)默認(rèn)執(zhí)行一次readFunction方法。
原文來(lái)自:博客園/小龍女先生