一、执行流程

二、主要类分析

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

knockout源码分析之执行过程的更多相关文章

  1. 深入源码分析SpringMVC执行过程

    本文主要讲解 SpringMVC 执行过程,并针对相关源码进行解析. 首先,让我们从 Spring MVC 的四大组件:前端控制器(DispatcherServlet).处理器映射器(HandlerM ...

  2. 精尽MyBatis源码分析 - SQL执行过程(二)之 StatementHandler

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  3. 精尽MyBatis源码分析 - SQL执行过程(三)之 ResultSetHandler

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  4. 精尽MyBatis源码分析 - SQL执行过程(四)之延迟加载

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  5. SOFA 源码分析 —— 服务引用过程

    前言 在前面的 SOFA 源码分析 -- 服务发布过程 文章中,我们分析了 SOFA 的服务发布过程,一个完整的 RPC 除了发布服务,当然还需要引用服务. So,今天就一起来看看 SOFA 是如何引 ...

  6. Dubbo 源码分析 - 服务调用过程

    注: 本系列文章已捐赠给 Dubbo 社区,你也可以在 Dubbo 官方文档中阅读本系列文章. 1. 简介 在前面的文章中,我们分析了 Dubbo SPI.服务导出与引入.以及集群容错方面的代码.经过 ...

  7. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

  8. 源码分析HotSpot GC过程(一)

    «上一篇:源码分析HotSpot GC过程(一)»下一篇:源码分析HotSpot GC过程(三):TenuredGeneration的GC过程 https://blogs.msdn.microsoft ...

  9. 源码分析HotSpot GC过程(三):TenuredGeneration的GC过程

    老年代TenuredGeneration所使用的垃圾回收算法是标记-压缩-清理算法.在回收阶段,将标记对象越过堆的空闲区移动到堆的另一端,所有被移动的对象的引用也会被更新指向新的位置.看起来像是把杂陈 ...

随机推荐

  1. 初了解NodeJS

    发现了NodeJS以后让我有一种很激动的心情,因为它能做我以前一直想写的东西,而如果没有NodeJS我还需要去学习别的语言,但是有了NodeJS以后就可以通过Javascript来写我的程序了,不得不 ...

  2. 深入理解javascript作用域系列第二篇——词法作用域和动态作用域

    × 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极 ...

  3. Java多线程系列--“基础篇”05之 线程等待与唤醒

    概要 本章,会对线程等待/唤醒方法进行介绍.涉及到的内容包括:1. wait(), notify(), notifyAll()等方法介绍2. wait()和notify()3. wait(long t ...

  4. Gephi可视化(一)——使用Gephi Toolkit创建Gephi应用

    在Prefuse上摸打滚爬了一段时间,发现其和蔼可亲,容易上手.但是每每在打开gephi,导入数据再运行时,总还是在心里暗自赞叹gephi的绚烂之极,无与匹敌,当然,gephi也有自己的缺陷,但是ge ...

  5. JS双月份显示联动效果,点击日期浮出消息提示

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. Android中Services之异步IntentService

    IntentService:异步处理服务,新开一个线程:handlerThread在线程中发消息,然后接受处理完成后,会清理线程,并且关掉服务. IntentService有以下特点: (1)  它创 ...

  7. oracle数据库字符集US7ASCII,在java中处理中文问题

    原来项目中oracle数据库一直是US7ASCII,我新项目对接的时候,查询以及插入中文,出现乱码问题. 暂时未能解决此问题,最终决定每次转码: 查询的时候: List<Record> l ...

  8. C# 根据自定义线程定时器 生成随机订单

    这个源之于一个朋友问我的一个问题,他说他们的需求是在一天之内随机抽取数据生成订单,还不能让客户看出来. 随机生成的订单还分概率抽取不一定的状态值,那么根据我之前写的定时器线程执行器,我们设计需要一个定 ...

  9. mysql的collation

    mysql的collation大致的意思就是字符序.首先字符本来是不分大小的,那么对字符的>, = , < 操作就需要有个字符序的规则.collation做的就是这个事情,你可以对表进行字 ...

  10. 阅读《LEARNING HARD C#学习笔记》知识点总结与摘要五

    本篇文章主要是总结异步编程的知识点,也是本系列的最后一篇文章,每一个知识点我都有写出示例代码,方便大家理解,若发现有误或不足之处还请指出,由于书中作者对此知识点讲解过于简单,所以在写这篇文章时本人参考 ...