讲完了数据劫持原理和一堆初始化 现在是DOM相关的代码了

  上一节是从这个函数开始的:

    // Line-3924
Vue.prototype._init = function(options) {
// 大量初始化
// ...
// Go!
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
};

  弄完data属性的数据绑定后,开始处理el属性,也就是挂载的DOM节点,这里的vm.$options.el也就是传进去的'#app'字符串。

  有一个值得注意的点是,源码中有2个$mount函数都是Vue$3的原型函数,其中一个标记了注释public mount method,在7531行,另外一个在9553行。打断点进入的是后面,因为定义的晚,覆盖了前面的函数。

  (时隔多日,我终于看懂了,这里首先定义了原型方法$mount,然后将其赋给了一个变量mount,然后重新定义此方法,所以调用mount.call会调用第一次定义的$mount,vue原型上最终只有第二次定义的$mount) => 多此一举! 2017.7.31

    // Line-7531
// public mount method
Vue$3.prototype.$mount = function(el,hydrating) {
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating)
}; // Line-9552
var mount = Vue$3.prototype.$mount;
Vue$3.prototype.$mount = function(
el,
hydrating
) {
// ...很多代码
return mount.call(this, el, hydrating)
};

  现在进入后面的$mount函数看看内部结构:

    // Line-9552
var mount = Vue$3.prototype.$mount;
Vue$3.prototype.$mount = function(el,hydrating) {
// 将el格式化为DOM节点
el = el && query(el);
// 判断是否挂载到body或者html标签上
if (el === document.body || el === document.documentElement) {
"development" !== 'production' && warn(
"Do not mount Vue to <html> or <body> - mount to normal elements instead."
);
return this
} var options = this.$options;
// 处理template/el 转换为渲染函数
if (!options.render) {
// ...非常多代码
}
return mount.call(this, el, hydrating)
};

  代码前半段首先将el转换为DOM节点,并判断是否挂载到body或者html标签,看看简单的query函数:

    // Line-4583
function query(el) {
// 如果是字符串就调用querySelector
if (typeof el === 'string') {
var selected = document.querySelector(el);
if (!selected) {
"development" !== 'production' && warn(
'Cannot find element: ' + el
);
// 找不到就返回一个div
return document.createElement('div')
}
return selected
}
// 不是字符串就默认传进来的是DOM节点
else {
return el
}
}

  函数比较简单,值得注意的一个点是,由于调用的是querySelector方法,所以可以传标签名、类名、C3新选择器等,都会返回查询到的第一个。当然,总是传一个ID或者确定的DOM节点才是正确用法。

  下面看接下来的代码:

    // Line-9552
var mount = Vue$3.prototype.$mount;
Vue$3.prototype.$mount = function(el,hydrating) {
// ...el转换为DOM节点
// ...
// 没有render属性 进入代码段
if (!options.render) {
var template = options.template;
// 没有template 跳
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template);
/* istanbul ignore if */
if ("development" !== 'production' && !template) {
warn(
("Template element not found or is empty: " + (options.template)),
this
);
}
}
} else if (template.nodeType) {
template = template.innerHTML;
} else {
{
warn('invalid template option:' + template, this);
}
return this
}
}
// 有el 获取字符串化的DOM树
else if (el) {
template = getOuterHTML(el);
}
if (template) {
// ...小段代码
}
}
return mount.call(this, el, hydrating)
};

  由于没有template属性,会直接进入第二个判断条件,调用getOuterHTML来初始化template变量,函数比较简单, 来看看:

    // Line-9623
function getOuterHTML(el) {
if (el.outerHTML) {
return el.outerHTML
}
// 兼容IE中的SVG
else {
var container = document.createElement('div');
container.appendChild(el.cloneNode(true));
return container.innerHTML
}
}

  简单来讲,就是调用outerHTML返回DOM树的字符串形式,看图就明白了:

  下面看最后一段代码:

    // Line-9552
var mount = Vue$3.prototype.$mount;
Vue$3.prototype.$mount = function(el,hydrating) {
// ...el转换为DOM节点
// ...
// 没有render属性 进入代码段
if (!options.render) {
// ...处理template
// ...
if (template) {
// 编译开始
if ("development" !== 'production' && config.performance && mark) {
mark('compile');
} // 将DOM树字符串编译为函数
var ref = compileToFunctions(template, {
shouldDecodeNewlines: shouldDecodeNewlines,
delimiters: options.delimiters
}, this);
// options添加属性
var render = ref.render;
var staticRenderFns = ref.staticRenderFns;
options.render = render;
options.staticRenderFns = staticRenderFns; // 编译结束
if ("development" !== 'production' && config.performance && mark) {
mark('compile end');
measure(((this._name) + " compile"), 'compile', 'compile end');
}
}
}
return mount.call(this, el, hydrating)
};

  忽略2段dev模式下的提示代码,剩下的代码做了3件事,调用compileToFunctions函数肢解DOM树字符串,将返回的对象属性添加到options上,再次调用mount函数。

  首先看一下compileToFunctions函数,该函数接受3个参数,分别为字符串、配置对象、当前vue实例。

  由于函数比较长,而且部分是错误判断,简化后如下:

    // Line-9326
function compileToFunctions(template,options,vm) {
// 获取配置参数
options = options || {}; // ... var key = options.delimiters ?
String(options.delimiters) + template :
template;
// 检测缓存
if (functionCompileCache[key]) {
return functionCompileCache[key]
} //
var compiled = compile(template, options); // ... //
var res = {};
var fnGenErrors = [];
res.render = makeFunction(compiled.render, fnGenErrors);
var l = compiled.staticRenderFns.length;
res.staticRenderFns = new Array(l);
for (var i = 0; i < l; i++) {
res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i], fnGenErrors);
} // ... //
return (functionCompileCache[key] = res)
}

  可以看到,这个函数流程可以分为4步,获取参数 => 调用compile函数进行编译 => 将得到的compiled转换为函数 => 返回并缓存。

  第一节现在这样吧。一张图总结下:

  

.5-Vue源码之AST(1)的更多相关文章

  1. 大白话Vue源码系列(03):生成AST

    阅读目录 AST 节点定义 标签的正则匹配 解析用到的工具方法 解析开始标签 解析结束标签 解析文本 解析整块 HTML 模板 未提及的细节 本篇探讨 Vue 根据 html 模板片段构建出 AST ...

  2. [Vue源码]一起来学Vue模板编译原理(一)-Template生成AST

    本文我们一起通过学习Vue模板编译原理(一)-Template生成AST来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vu ...

  3. [Vue源码]一起来学Vue模板编译原理(二)-AST生成Render字符串

    本文我们一起通过学习Vue模板编译原理(二)-AST生成Render字符串来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学V ...

  4. Vue源码后记-其余内置指令(3)

    其实吧,写这些后记我才真正了解到vue源码的精髓,之前的跑源码跟闹着玩一样. go! 之前将AST转换成了render函数,跳出来后,由于仍是字符串,所以调用了makeFunction将其转换成了真正 ...

  5. 大白话Vue源码系列(02):编译器初探

    阅读目录 编译器代码藏在哪 Vue.prototype.$mount 构建 AST 的一般过程 Vue 构建的 AST 题接上文,上回书说到,Vue 的编译器模块相对独立且简单,那咱们就从这块入手,先 ...

  6. 大白话Vue源码系列(03):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

  7. 大白话Vue源码系列(04):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

  8. 大白话Vue源码系列(05):运行时鸟瞰图

    阅读目录 Vue 实例的生命周期 实例创建 响应的数据绑定 挂载到 DOM 节点 结论 研究 runtime 一边 Vue 一边源码 初看 Vue 是 Vue 源码是源码 再看 Vue 不是 Vue ...

  9. 入口文件开始,分析Vue源码实现

    Why? 网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行. 然后平时的项目也主要是Vue,在使用Vue的过程中,也对其一些约定产生了一 ...

  10. 入口开始,解读Vue源码(一)-- 造物创世

    Why? 网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行. 然后平时的项目也主要是Vue,在使用Vue的过程中,也对其一些约定产生了一 ...

随机推荐

  1. Java-大集合拆分为指定大小的小集合

    因为Oracle数据的in 最大允许1000 ,超过就会报错, 所以需要将集合拆分为多个集合进行处理. /** * 拆分集合 * @param <T> * @param resList 要 ...

  2. Apache POI

    Apache POI 用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能.POI为"Po ...

  3. XML(二)之DTD——XML文件约束

    前面介绍了XML的作用和基本的格式,今天我给大家分享的是关于XML的约束.废话不多说,我们直接来正题! 一.DTD简介 1.1.DTD概述 DTD(Document Type Definition,文 ...

  4. angular 自定义指令详解 Directive

    在angular中,Directive,自定义指令的学习,可以更好的理解angular指令的原理,当angular的指令不能满足你的需求的时候,嘿嘿,你就可以来看看这篇文章,自定义自己的指令,可以满足 ...

  5. 从Leetcode的Combination Sum系列谈起回溯法

    在LeetCode上面有一组非常经典的题型--Combination Sum,从1到4.其实就是类似于给定一个数组和一个整数,然后求数组里面哪几个数的组合相加结果为给定的整数.在这个题型系列中,1.2 ...

  6. java 线程二

    一.线程的优先级别 线程优先级别的使用范例: 1 package cn.galc.test; 2 3 public class TestThread6 { 4 public static void m ...

  7. 移动端touch事件实现页面弹动--小插件

    动手之前的打盹 说实话真的是好久没有更新博客了,最近一直赶项目,身心疲惫:最关键的是晚上还要回去上一波王者,实在是忙啊! 这周下来,清闲了些许,或许是因为要到国庆的缘故吧,大家都显得无精打采.俗话说的 ...

  8. 每周分享之 二 http协议(2)

    本次分享http协议,共分为三部分,这是第二部分,主要讲解请求与响应的字段,以及状态码. 以http/1.1版本的一个完整的请求与响应作为例子 http请求信息由三部分组成 1.请求方法(GET/PO ...

  9. E. Exposition

                                                                        E. Exposition time limit per tes ...

  10. FPGA与数字图像处理技术

    数字图像处理方法的重要性源于两个主要应用领域: 改善图像信息以便解释. 为存储.传输和表示而对图像数据进行处理,以便于机器自动理解. 图像处理(image processing): 用计算机对图像进行 ...