欢迎加入QQ讨论群258996829
麦子学院 头像
苹果6袋
6
麦子学院

如何理解knockout执行过程

发布时间:2016-07-28 20:14  回复:0  查看:2625   最后回复:2016-07-28 20:14  

knockout执行过程是怎么样的呢?你是不是还很迷惑。这里我就结合我自己学习knockout的总结和自己的开发经验给大家讲解一些,有源码哟。

 

一、执行流程

如何理解knockout执行过程

一、主要类分析

 

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.3. 进入applyBindingsToNodeInternal方法,其中会调用bindingProvidergetBindingsAccessors方法(用于分析和获取bindings数据,主要分析data-bind属性)
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() { //做了readwrite处理,实现双向关联(只做了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;

        }

 

2.5. 然后分析bindings中每个binding,并将initupdate方法创建为一个dependentObservable对象(其中bindings的执行是有顺序的)。

 

二、BindingProvider分析

 

此类主要提供关于data-bind属性的解析,主要提供getBindingsgetBindingsAccessorsparseBindingsString(内容使用)方法辅助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);

    }

 

1、在分析bindings时,会区分NodeType18的类型。如果是8(注释)就会调用virtualElements类的virtualNodeBindingValue方法来分析binding结果。

 

四、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方法。

 

 

 

 

原文来自:博客园/小龙女先生

您还未登录,请先登录

热门帖子

最新帖子