.8-Vue源码之AST(4)
上一节讲完了超长的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)的更多相关文章
- 大白话Vue源码系列(03):生成AST
阅读目录 AST 节点定义 标签的正则匹配 解析用到的工具方法 解析开始标签 解析结束标签 解析文本 解析整块 HTML 模板 未提及的细节 本篇探讨 Vue 根据 html 模板片段构建出 AST ...
- [Vue源码]一起来学Vue模板编译原理(一)-Template生成AST
本文我们一起通过学习Vue模板编译原理(一)-Template生成AST来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学Vu ...
- [Vue源码]一起来学Vue模板编译原理(二)-AST生成Render字符串
本文我们一起通过学习Vue模板编译原理(二)-AST生成Render字符串来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫持和发布订阅 一起来学V ...
- Vue源码后记-其余内置指令(3)
其实吧,写这些后记我才真正了解到vue源码的精髓,之前的跑源码跟闹着玩一样. go! 之前将AST转换成了render函数,跳出来后,由于仍是字符串,所以调用了makeFunction将其转换成了真正 ...
- 大白话Vue源码系列(02):编译器初探
阅读目录 编译器代码藏在哪 Vue.prototype.$mount 构建 AST 的一般过程 Vue 构建的 AST 题接上文,上回书说到,Vue 的编译器模块相对独立且简单,那咱们就从这块入手,先 ...
- 大白话Vue源码系列(03):生成render函数
阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...
- 大白话Vue源码系列(04):生成render函数
阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...
- 大白话Vue源码系列(05):运行时鸟瞰图
阅读目录 Vue 实例的生命周期 实例创建 响应的数据绑定 挂载到 DOM 节点 结论 研究 runtime 一边 Vue 一边源码 初看 Vue 是 Vue 源码是源码 再看 Vue 不是 Vue ...
- 入口文件开始,分析Vue源码实现
Why? 网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行. 然后平时的项目也主要是Vue,在使用Vue的过程中,也对其一些约定产生了一 ...
- 入口开始,解读Vue源码(一)-- 造物创世
Why? 网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行. 然后平时的项目也主要是Vue,在使用Vue的过程中,也对其一些约定产生了一 ...
随机推荐
- Java Sftp上传下载文件
需要使用jar包 jsch-0.1.50.jar sftp上传下载实现类 package com.bstek.transit.sftp; import java.io.File; import ja ...
- 浅谈大数据和hadoop家族
按照时间的早晚从大数据出现之前的时代讲到现在.暂时按一个城市来比喻吧,反正Landscape的意思也大概是”风景“的意思. 早在大数据概念出现以前就存在了各种各样的关于数学.统计学.算法.编程语言的研 ...
- PuTsangTo-单撸游戏开发03 碰撞与跳跃瑕疵版
继续上一部分,游戏的定位是横版平台动作类游戏,所以得有跳跃动作,首先想到的就是物理引擎,不过在2D游戏里,仅为了角色的跳跃而引入物理引擎,目前想来有些不至于,仅使用cocos默认带有的碰撞系统也足够了 ...
- JSP入门 导出文件
1.图片校验码 <img src="captcha.jpg" /> web.xml配置 <servlet> <servlet-name& ...
- PHP常用字符串处理函数
(1)strlen(string) 返回字符串长度 (2)strpos(string,find,begin) 返回find字符串第一次出现的位置(从0开始) string:处理的字符串 find:想找 ...
- 技术领导(Technical Leader)画像
程序员都讨厌被管理,而乐于被领导.管理的角色由PM(project manager)扮演,具体来说,PM负责提需求.改改改.大多数情况,PM是不懂技术的,这也是程序员觉得PM难以沟通的原因.而后者由技 ...
- php过滤textarea 中的换行符问题
之前我写的替换代码是这样的 $content = str_replace('\r\n', '', $_POST['content']); 为了确保window和Linux的换行符都能去掉,改成这样的: ...
- 史上前端面试最全知识点(附答案)---html & js & css
史上前端面试最全知识点(附答案) 一.html & js & css 1.AMD和CMD是什么?它们的区别有哪些? AMD和CMD是二种模块定义规范.现在都使用模块化编程,AMD,异步 ...
- C#中的原子操作Interlocked,你真的了解吗?
阅读目录 背景 代码描述 越分析越黑暗 结语 一.背景 这个标题起的有点标题党的嫌疑[捂脸],这个事情的原委是这样的,有个Web API的站点在本地使用Release模式Run的时候出现问题,但是使用 ...
- 获取报告 Stream转string,利用字符串分割转换成DataTable
protected void Button1_Click(object sender, EventArgs e) { MemoryStream stream = new MemoryStream(); ...