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方法。
knockout源码分析之执行过程的更多相关文章
- 深入源码分析SpringMVC执行过程
本文主要讲解 SpringMVC 执行过程,并针对相关源码进行解析. 首先,让我们从 Spring MVC 的四大组件:前端控制器(DispatcherServlet).处理器映射器(HandlerM ...
- 精尽MyBatis源码分析 - SQL执行过程(二)之 StatementHandler
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽MyBatis源码分析 - SQL执行过程(三)之 ResultSetHandler
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- 精尽MyBatis源码分析 - SQL执行过程(四)之延迟加载
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- SOFA 源码分析 —— 服务引用过程
前言 在前面的 SOFA 源码分析 -- 服务发布过程 文章中,我们分析了 SOFA 的服务发布过程,一个完整的 RPC 除了发布服务,当然还需要引用服务. So,今天就一起来看看 SOFA 是如何引 ...
- Dubbo 源码分析 - 服务调用过程
注: 本系列文章已捐赠给 Dubbo 社区,你也可以在 Dubbo 官方文档中阅读本系列文章. 1. 简介 在前面的文章中,我们分析了 Dubbo SPI.服务导出与引入.以及集群容错方面的代码.经过 ...
- MyBatis 源码分析 - 配置文件解析过程
* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...
- 源码分析HotSpot GC过程(一)
«上一篇:源码分析HotSpot GC过程(一)»下一篇:源码分析HotSpot GC过程(三):TenuredGeneration的GC过程 https://blogs.msdn.microsoft ...
- 源码分析HotSpot GC过程(三):TenuredGeneration的GC过程
老年代TenuredGeneration所使用的垃圾回收算法是标记-压缩-清理算法.在回收阶段,将标记对象越过堆的空闲区移动到堆的另一端,所有被移动的对象的引用也会被更新指向新的位置.看起来像是把杂陈 ...
随机推荐
- Android线程处理之Handler
上一篇已经简单为大家介绍了一下关于Handler的使用,本篇我们就一起再来探讨一下Handler的高级使用,上一篇我们仅仅是简单的进行UI界面的更新,本篇我们来一起探讨一下如何把子线程的数据发送到主线 ...
- Zookeeper-Zookeeper的配置
前面两篇文章介绍了Zookeeper是什么和可以干什么,那么接下来我们就实际的接触一下Zookeeper这个东西,看看具体如何使用,有个大体的感受,后面再描述某些地方的时候也能在大脑中有具体的印象.本 ...
- 记一次上传文件到七牛云存储的经历(Plupload & UEditor)(.net)
七牛 配置ACCESS_KEY和SECRET_KEY Qiniu.Conf.Config.ACCESS_KEY = "ACCESS_KEY"; Qiniu.Conf.Config. ...
- IP,路由,交换基础培训记录
IP 掩码 子网划分 vlan划分(有助于减少广播压力) vlan之间互通通过交换机打通. 路由,静态路由,动态路由(学习到的),路由表,下一跳,网络位长的优先级高. 交换机,hub集线器. hub ...
- selenium-webdriver(python) (十三) -- cookie处理
本节重点: driver.get_cookies() 获得cookie信息 add_cookie(cookie_dict) 向cookie添加会话信息 delete_cookie(name) 删 ...
- Hadoop阅读笔记(六)——洞悉Hadoop序列化机制Writable
酒,是个好东西,前提要适量.今天参加了公司的年会,主题就是吃.喝.吹,除了那些天生话唠外,大部分人需要加点酒来作催化剂,让一个平时沉默寡言的码农也能成为一个喷子!在大家推杯换盏之际,难免一些画面浮现脑 ...
- WEB编程中获取src目录下的文件(没有src目录)
这种情况遇见的会比较多,像一个WEB工程,如果在src下面写了一个xml或者一些其它的文件,当工程发布到服务器时,web程序是在tomcat等服务器下运行这个程序的,这个时候,程序目录里面并没有src ...
- 【模式匹配】Aho-Corasick自动机
1. 多模匹配 AC自动机(Aho-Corasick Automaton)是多模匹配算法的一种.所谓多模匹配,是指在字符串匹配中,模式串有多个.前面所介绍的KMP.BM为单模匹配,即模式串只有一个.假 ...
- Elasticsearch——集群相关的配置
cluster模块主要用于控制分片在节点上如何进行分配,以及何时进行重新分配 概览 下面的一些资料可以进行相关的配置: Cluster Level Shard Allocation用于配置集群中节点如 ...
- MVC导出数据到EXCEL新方法:将视图或分部视图转换为HTML后再直接返回FileResult
导出EXCEL方法总结 MVC导出数据到EXCEL的方法有很多种,常见的是: 1.采用EXCEL COM组件来动态生成XLS文件并保存到服务器上,然后转到该文件存放路径即可: 优点:可设置丰富的EXC ...