一、執行流程
二、主要類分析
2.1. 在applyBindings中,建立bindingContext,然後執行applyBindingsToNodeAndDescendantsInternal方法
2.2. 在applyBindinsToNodeAndDescendantsInteranl方法,主要完成當前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.4. 建立dependentObservable物件(依賴監控物件)
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( //依賴監控物件 function() { //做了read、write處理,實現雙向關聯(只做了read),預設會執行一次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; }
三、BindingProvider分析
此類主要提供關於data-bind屬性的解析,主要提供getBindings、getBindingsAccessors、parseBindingsString(內容使用)方法輔助binding過程。建立function物件:
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 + "}}}"; //執行with表示式 return new Function("$context", "$element", functionBody); }
四、bindings的排序技巧
檢視自定義binding是否有after屬性,如果存在則進行遞迴操作:
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']) { //依賴檢測,將after的引用先新增到陣列中,然後再新增當前項 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物件,在建立的過程中都會預設執行一次readFunction方法。