• 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方法,其中会调用bindingProvider的getBindingsAccessors方法(用于分析和获取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() { //做了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;
            }
    2.5. 然后分析bindings中每个binding,并将init、update方法创建为一个dependentObservable对象(其中bindings的执行是有顺序的)。

    三、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);
        }

    1、在分析bindings时,会区分NodeType为1、8的类型。如果是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方法。

  • 相关阅读:
    C# 上传辅助方法
    C# 经纬度计算
    C#Excel导出反射数据集
    C# 数据模板导出
    C# 百度经纬度获取地址信息
    【平台开发】— 5.后端:代码分层
    【平台开发】— 4.mysql建库建表
    【平台开发】— 3.前端开发思路
    【平台开发】— 2.前端:vue-element-admin
    【平台开发】— 1.开篇
  • 原文地址:https://www.cnblogs.com/cqhaibin/p/5679677.html
Copyright © 2020-2023  润新知