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

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

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

    // Line-7672function 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-8167function 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 bugif (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-7928function 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-7672function 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-7863function 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 shoppos = 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 stackstack.length = pos;// 0lastTag = 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-8154function 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 stackstack.length -= 1;// 变成undefined了currentParent = stack[stack.length - 1];endPre(element);}// Line-8010function endPre(element) {if (element.pre) {inVPre = false;}// tag === pre?if (platformIsPreTag(element.tag)) {inPre = false;}}

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

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

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

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

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

  

  先结束了吧。

                  

转载于:https://www.cnblogs.com/QH-Jimmy/p/6957419.html

.8-Vue源码之AST(4)相关推荐

  1. 什么是php的ast结构,什么是AST?Vue源码中AST语法树的解析

    这篇文章给大家介绍的内容是关于什么是AST?Vue源码中AST语法树的解析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 什么是AST AST是指抽象语法树(abstract syn ...

  2. Vue源码解析之Template转化为AST的实现方法

    什么是AST 在Vue的mount过程中,template会被编译成AST语法树,AST是指抽象语法树(abstract syntax tree或者缩写为AST),或者语法树(syntax tree) ...

  3. vue实例没有挂载到html上,vue 源码学习 - 实例挂载

    前言 在学习vue源码之前需要先了解源码目录设计(了解各个模块的功能)丶Flow语法. src ├── compiler # 把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能. ├── ...

  4. 【vuejs深入三】vue源码解析之二 htmlParse解析器的实现

    写在前面 一个好的架构需要经过血与火的历练,一个好的工程师需要经过无数项目的摧残. 昨天博主分析了一下在vue中,最为基础核心的api,parse函数,它的作用是将vue的模板字符串转换成ast,从而 ...

  5. [Vue源码分析] 模板的编译

    最近小组有个关于vue源码分析的分享会,提前准备一下- 前言: Vue有两个版本:Runtime + Compiler . Runtime only ,前者是包含编译代码的版本,后者不包含编译代码,编 ...

  6. [Vue源码分析] v-model实现原理

    最近小组有个关于vue源码分析的分享会,提前准备一下- 前言: 我们都知道使用v-model可以实现数据的双向绑定,及实现数据的变化驱动dom的更新,dom的更新影响数据的变化.那么v-model是怎 ...

  7. iris流程图_GitHub - LeoIris/vue: vue源码逐行注释分析+40多m的vue源码程序流程图思维导图 (diff部分待后续更新)...

    vue源码业余时间差不多看了一年,以前在网上找帖子,发现很多帖子很零散,都是一部分一部分说,断章的很多,所以自己下定决定一行行看,经过自己坚持与努力,现在基本看完了 .这个vue源码逐行分析,我基本每 ...

  8. 源码解读_入口开始解读Vue源码系列(二)——new Vue 的故事

    作者:muwoo 转发链接:https://github.com/muwoo/blogs/blob/master/src/Vue/2.md 目录 入口开始解读Vue源码系列(一)--造物创世 入口开始 ...

  9. Vue源码解析:虚拟dom比较原理

    通过对 Vue2.0 源码阅读,想写一写自己的理解,能力有限故从尤大佬2016.4.11第一次提交开始读,准备陆续写: 模版字符串转AST语法树 AST语法树转render函数 Vue双向绑定原理 V ...

  10. Vue源码流程图(函数名与源码对应)

    这里写目录标题 概览 1. 变化侦查 1.1 Observer流程图 2. vdom虚拟DOM 2.1 创建节点createElm 2.2 更新节点patchVnode 2.3 更新子节点 updat ...

最新文章

  1. debian,ubuntu下安装MariaDB,并设置密码,修改端口,允许外网访问
  2. Java实现数据库表结构导出到Excel
  3. 【mycat】分库分表
  4. C#启动其他程序的代码
  5. RPM安装包-Spec文件參数具体解释与演示样例分析
  6. Nginx的rewrite之if指令(一)
  7. nodejs 定时 mysql_nodejs 使用 mysql
  8. Vue项目实战09 : vue3.0实现点击切换验证码(组件)及校验
  9. 怎样删去csv中重复行_4个锦囊,祝你快速删去Excel中的重复数据
  10. HttpHandler:给指定路径下的图片添加水印显示
  11. Android官方开发文档Training系列课程中文版:创建自定义View之View的绘制
  12. kerberos安装配置与使用
  13. ssm框架从前端传值到后台出现乱码的解决办法,你遇到的可能就是这几种
  14. Atitit. 异常的使用总结最佳实践java .net php Vo8f
  15. Java:三角函数计算器!
  16. Hotspot 偏向锁BiasedLocking 源码解析
  17. 极化码信道极化-构造-编码-译码(不断更新)
  18. html文件夹加密,HTML加密转换工具(WebCrypt)
  19. photoshop----剪切蒙版/置入图片
  20. 图片太大不知道怎么变小,教你几个压缩方法

热门文章

  1. Dockerfile使用,怎么通过Dockerfile完成docker映像配置
  2. Java泛型详解,通俗易懂只需5分钟
  3. Android mediaRecorder框架简述(二)
  4. Android 6.0 PowerManagerService状态分析
  5. js日期格式判断(2018/01/30) -方法 小时分秒格式判断((HH:MM)
  6. dos系统 关闭服务器,如何开启或关闭服务程序?巧用DOS命令全攻略
  7. 两个软件相互交换数据_六轴算法机软件使用说明
  8. mysql试题百度云_BATJ大厂600多道技术面试题及答案解析
  9. c语言中return的作用_C语言简介
  10. java union方法参数_Java Geometry.union方法代碼示例