上一节讲完了超长的start函数,也同时完结了handleStartTag函数,接着continue进入下一轮while循环。

  此时剩余的字符串状态如图:,切掉了<div id='app'>。

  再次进入while循环时,发生了一些变化:

    // Line-7672
function parseHTML(html, options) {
/* code */ while (html) {
last = html;
if (!lastTag || !isPlainTextElement(lastTag)) {
var textEnd = html.indexOf('<');
// 此时字符串不是以<开头 所以不会进入此条件
if (textEnd === 0) {
// ...
}
var text = (void 0),
rest$1 = (void 0),
next = (void 0);
if (textEnd >= 0) {
// 截取<字符索引 => </div>
rest$1 = html.slice(textEnd);
// 处理文本中的<字符
while (!endTag.test(rest$1) &&
!startTagOpen.test(rest$1) &&
!comment.test(rest$1) &&
!conditionalComment.test(rest$1)
) {
next = rest$1.indexOf('<', 1);
if (next < 0) {
break
}
textEnd += next;
rest$1 = html.slice(textEnd);
}
// 获取中间的字符串 => {{message}}
text = html.substring(0, textEnd);
advance(textEnd);
}
// 当字符串没有<时
if (textEnd < 0) {
text = html;
html = '';
}
// 另外一个函数
if (options.chars && text) {
options.chars(text);
}
} else {
/* code */
} if (html === last) {
/* code */
}
}
// Clean up any remaining tags
parseEndTag(); // fn...
}

  第一次进入while循环时,由于字符串以<开头,所以进入startTag条件,并进行AST转换,最后将对象弹入stack数组中。

  而这一次,字符串开头为{,所以会继续执行下面的代码。代码将{{message}}作为text抽离出来,并调用了参数中另外一个函数:options.chars。

    // Line-8167
function chars(text) {
if (!currentParent) {
// 提示必须有一个DOM根节点
if (text === template) {
warnOnce(
'Component template requires a root element, rather than just text.'
);
}
// 节点外的文本会被忽略
else if ((text = text.trim())) {
warnOnce(
("text \"" + text + "\" outside root element will be ignored.")
);
}
return
}
// IE textarea placeholder bug
if (isIE &&
currentParent.tag === 'textarea' &&
currentParent.attrsMap.placeholder === text) {
return
}
var children = currentParent.children;
// text => {{message}}
text = inPre || text.trim() ?
isTextTag(currentParent) ? text : decodeHTMLCached(text) : preserveWhitespace && children.length ? ' ' : '';
if (text) {
var expression;
if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) {
// 将解析后的text弄进children数组
children.push({
type: 2,
expression: expression,
text: text
});
} else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
children.push({
type: 3,
text: text
});
}
}
}

  本函数的核心为parseText对text的处理,即{{message}}。

    // Line-7928
function parseText(text, delimiters) {
// 正则选择
var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE;
// 在这里调用test方法后lasatIndex会变化
if (!tagRE.test(text)) {
return
}
var tokens = [];
var lastIndex = tagRE.lastIndex = 0;
var match, index;
// 匹配到中间的文本
while ((match = tagRE.exec(text))) {
index = match.index;
// 将{{message}}之前的文本push进去
if (index > lastIndex) {
tokens.push(JSON.stringify(text.slice(lastIndex, index)));
}
// 该方法对特殊字符进行处理 本例暂时用不上
// 返回的仍然是message字符串
var exp = parseFilters(match[1].trim());
// _s(message)
tokens.push(("_s(" + exp + ")"));
lastIndex = index + match[0].length;
}
if (lastIndex < text.length) {
// push}}后面的文本
tokens.push(JSON.stringify(text.slice(lastIndex)));
}
return tokens.join('+')
}

  实际上text可分为3个部分,{{之前的,{{}}中间包裹的,}}之后的,函数分别将三者抽离出来,push进了tokens,最后用+连接并返回一个字符串:

  返回后,将此字符串作为值,和其余属性一个添加到children数组中:

  

  处理完后,进入下一轮while循环。

  剩余的字符串为</div>,所以进入第一个循环,并且匹配到EndTag的分支。

    // Line-7672
function parseHTML(html, options) {
/* code */
while (html) {
/* code */
var textEnd = html.indexOf('<');
if (textEnd === 0) {
/* code */
var endTagMatch = html.match(endTag);
if (endTagMatch) {
var curIndex = index;
advance(endTagMatch[0].length);
parseEndTag(endTagMatch[1], curIndex, index);
continue
}
/* code */
}
/* code */
}
/* code */
}

  进入endTag分支后,匹配到的endTagMatch如图所示:

  将当前索引保存为curIndex,然后根据匹配到的字符串往前推index,调用parseEndTag函数进行处理。

    // Line-7863
function parseEndTag(tagName, start, end) {
// 参数修正
var pos, lowerCasedTagName;
if (start == null) {
start = index;
}
if (end == null) {
end = index;
}
if (tagName) {
lowerCasedTagName = tagName.toLowerCase();
} // 获取最近的匹配标签
if (tagName) {
for (pos = stack.length - 1; pos >= 0; pos--) {
if (stack[pos].lowerCasedTag === lowerCasedTagName) {
break
}
}
} else {
// If no tag name is provided, clean shop
pos = 0;
} if (pos >= 0) {
for (var i = stack.length - 1; i >= pos; i--) {
// 提示没有匹配的标签
if ("development" !== 'production' &&
(i > pos || !tagName) &&
options.warn) {
options.warn(
("tag <" + (stack[i].tag) + "> has no matching end tag.")
);
}
// 调用剩下的一个参数函数
if (options.end) {
options.end(stack[i].tag, start, end);
}
} // Remove the open elements from the stack
stack.length = pos;
//
lastTag = pos && stack[pos - 1].tag;
} else if (lowerCasedTagName === 'br') {
if (options.start) {
options.start(tagName, [], true, start, end);
}
} else if (lowerCasedTagName === 'p') {
if (options.start) {
options.start(tagName, [], false, start, end);
}
if (options.end) {
options.end(tagName, start, end);
}
}
} // Line-8154
function end() {
// 获取对象与文本
var element = stack[stack.length - 1];
var lastNode = element.children[element.children.length - 1];
// type是2 跳过
if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) {
element.children.pop();
}
// pop stack
stack.length -= 1;
// 变成undefined了
currentParent = stack[stack.length - 1];
endPre(element);
} // Line-8010
function endPre(element) {
if (element.pre) {
inVPre = false;
}
// tag === pre?
if (platformIsPreTag(element.tag)) {
inPre = false;
}
}

  这个函数对闭合标签进行配对,并对应将stack数组进行变动,由于本例只有一个div,所以stack被清空。

  完事后,continue进入下一轮循环,由于字符串全部被切割完,此时html为空字符串,此时while循环结束,进入下一个代码段:

    // Line-7672
function parseHTML(html, options) {
/* code */
while (html) {
/* code */
} // Clean up any remaining tags
parseEndTag(); /* 一些方法 */
}

  字符串解析完后,再次调用parseEndTag进行收尾工作,函数内部将pos置0,stack置空。

  回到了parse函数,并返回了root,即解析后的AST对象:,包含了标签类型、属性、文本内容等。

  

  先结束了吧。

                  

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

  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. mybatis-resultMap使用与详解

    1,当数据库的字段名与属性名称不一致时,在mybatis中如何处理? 第一种方式: 采用投影对字段重命名<select id="load" parameterType=&qu ...

  2. 鸟哥Linux学习笔记07

    1, vi 是 老式的文字处理器,不过功能已经很齐全了,但是还是有可以进步的地方. vim可以说是程序开发者的一项很好用的工具,vim官网(http://www.vim.org)自己也说vim是一个“ ...

  3. Azure ARM (16) 基于角色的访问控制 (Role Based Access Control, RBAC) - 使用默认的Role

    <Windows Azure Platform 系列文章目录> 今天上午刚刚和客户沟通过,趁热打铁写一篇Blog. 熟悉Microsoft Azure平台的读者都知道,在老的Classic ...

  4. uvalive 3971 Assemble

    https://vjudge.net/problem/UVALive-3971 题意: 现在你要组装一台电脑,每个电脑的一种类型的配件都有多种选择,它们的名字是不同的. 现在给出已有的元件,每种类型都 ...

  5. 一文搞懂各种 Docker 网络 - 每天5分钟玩转 Docker 容器技术(72)

    前面各小节我们先后学习了 Docker Overaly,Macvaln,Flannel,Weave 和 Calico 跨主机网络方案.目前这个领域是百家争鸣,而且还有新的方案不断涌现. 本节将从不同维 ...

  6. 详细解读-this-关键字在全局、函数、对象、jQuery中的基础用法!

    一.前言 1. Javascript是一门基于对象的动态语言,也就是说,所有东西都是对象,一个很典型的例子就是函数也被视为普通的对象.Javascript可以通过一定的设计模式来实现面向对象的编程,其 ...

  7. 用static声明的函数和变量小结

    static 声明的变量在C语言中有两方面的特征: 1).变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值.这一点是它与堆栈变量和堆变量的区别. 2).变量用static ...

  8. C#操作SqlServer MySql Oracle通用帮助类Db_Helper_DG(默认支持数据库读写分离、查询结果实体映射ORM)

    [前言] 作为一款成熟的面向对象高级编程语言,C#在ADO.Net的支持上已然是做的很成熟,我们可以方便地调用ADO.Net操作各类关系型数据库,在使用了多年的Sql_Helper_DG后,由于项目需 ...

  9. iOS 多人共享开发证书

    当多人开发时,如果已经申请了几个开发者证书和发布者证书,苹果就不允许再创建了,页面添加的地方被灰化了,所以不可能每个人都建一个开发证书,这时候需要共用一个证书了.(其实一般在我们的证书界面中应该只有一 ...

  10. 关于 HashTable

    hashTable 的一些认识: 底层使用散列表,存贮键值对,键值非null 使用synchronize 保证线程安全 (线程安全) ■全局变量 //The hash table data. //底层 ...