当前位置: 首页 > 知识库问答 >
问题:

前端 - vue 解析模板源码中的一个困惑?

巩子实
2023-04-27

Vue-2.0

// src/compiler/parser/html-parser.js
const isSpecialTag = makeMap("script,style", true);

export function parseHTML(html, options) {
    while (html) {
        // lastTag不存在 或 lastTag存在且不属于script,style
        if (!lastTag || !isSpecialTag(lastTag)){
            ......
        }
        else{
            var stackedTag = lastTag.toLowerCase();
            if (stackedTag !== "script" && stackedTag !== "style" && stackedTag !== "noscript") {
               text = text.replace(/<!--([\s\S]*?)-->/g, "$1")
                          .replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, "$1");
            }
        }
    }
}

能进入else,说明lastTag要么是script,要么是style。那么else里边的if判断无论如何都无法成立吧?

共有2个答案

史钊
2023-04-27

你说的是对的。在这段代码中,else语句中的if判断是多余的,因为lastTag已经被确认为是"script""style"。所以,在这种情况下,这个if判断是不必要的,也永远不会成立。

可以简化这段代码,去掉这个if判断。例如:

// src/compiler/parser/html-parser.js
const isSpecialTag = makeMap("script,style", true);

export function parseHTML(html, options) {
    while (html) {
        // lastTag不存在 或 lastTag存在且不属于script,style
        if (!lastTag || !isSpecialTag(lastTag)){
            ......
        }
        else{
            text = text.replace(/<!--([\s\S]*?)-->/g, "$1")
                      .replace(/<!\[CDATA\[([\s\S]*?)\]\]>/g, "$1");
        }
    }
}

这样,代码会变得更简洁、易读。

龙毅
2023-04-27

看了好久才大概理解:
首先这段代码在while循环里,说明没有满足一定条件前是会不断重复的,很明显能看出来是对传入的html片段进行了处理,这样会导致lastTag的值也会不断变化:
lastTag在一开始被声明为undefined,然后在while循环中,如果不在script或style标签内,会根据HTML的不同情况进行不同的处理。如果是注释或条件注释,会直接跳过;如果是doctype,会直接跳过;如果是结束标签,会调用parseEndTag函数处理,并将lastTag设置为结束标签的标签名;如果是开始标签,会调用handleStartTag函数处理,并将lastTag设置为开始标签的标签名。如果不是以上情况,会将HTML中的文本提取出来,并调用options.chars函数处理。如果在script或style标签内,会将HTML中的文本提取出来,并调用options.chars函数处理,然后将HTML中的文本和结束标签提取出来,调用parseEndTag函数处理,并将lastTag设置为结束标签的标签名。
所以lastTag的值会在处理开始标签和结束标签时发生变化,有可能就会进入这个if判断语句了
补充:
我以以下html代码作为传入parseHTML的html参数:

<div class="example">
  <h1>Hello, World!</h1>
  <p>This is an example HTML block.</p>
</div>

一开始的时候:

let last, lastTag;

因为lastTag是undefined,所以while循环的第一次循环一定是进入if-else的if分支:

if (!lastTag || !isSpecialTag(lastTag)) {
...    //因为!lastTag为真所以进入这里
}else{
...
}

if分支里面其实就是作正则匹配,匹配到谁就知道传入的这段代码是什么类型的

//匹配到最后发现我们传入的html只会走到最下面的情况
// Start tag:
const startTagMatch = parseStartTag();
if (startTagMatch) {
  handleStartTag(startTagMatch);
  continue;
}

这里的parseStartTag方法里面:

const start = html.match(startTagOpen);

是拿我们的html去匹配一个正则,得到的start是"<div"
image.png
反正最后这个方法返回一个包含标签名称、属性和开始和结束位置的对象,如果标签是自闭合的,它还会设置一个标志来指示这一点。篇幅原因具体我不赘述,你可以点击进去自己看看。说白了startTagMatch就是一个包含了标签各种信息的对象:

match = {
    tagName: start[1],  //div
    attrs: [?],//div标签里面有什么属性,这里我偷懒不去看了
    start: index, // 0 div标签开始的位置
    unarySlash:?//是不是自闭合标签的标志,这里我偷懒不去看了
    end:?//div标签结束的位置,这里我偷懒不去看了
  }

我们进入这个handleStartTag函数里面:

const tagName = match.tagName;//就是上面的方法得到的对象的tagName,现在我们知道是div了
...
if (!unary) {
  stack.push({ tag: tagName, attrs: attrs });
  lastTag = tagName; //在这里实现了lastTag的赋值,也就是div
  unarySlash = "";
}

接下来if分支的最后一段代码:

let text;
if (textEnd >= 0) {
  text = html.substring(0, textEnd);
  advance(textEnd);
} else {
  text = html;
  html = "";
}

if (options.chars) {
options.chars(text);
}

这里的textEnd是:

const textEnd = html.indexOf("<");

也就是说如果在HTML中找到了<,则说明接下来是一个标签,需要停止提取文本内容。如果找不到<,则说明剩下的全部都是文本内容。这段代码将提取到的文本内容赋值给变量text,并将html中已经提取的部分删除。如果找不到<,则将html清空。最后检查options对象是否有一个chars属性。如果有,它就用text 参数调用chars函数。
到这里,第一次while循环就结束了,第二次进入while循环呢:
lastTag是div,也就会进入

if (stackedTag !== "script" &&stackedTag !== "style" &&stackedTag !== "noscript")

这个分支了。
以上内容是我大略的想法,难免可能有疏忽大意,欢迎你指出。

 类似资料:
  • 本文向大家介绍Vue AST源码解析第一篇,包括了Vue AST源码解析第一篇的使用技巧和注意事项,需要的朋友参考一下 讲完了数据劫持原理和一堆初始化,现在是DOM相关的代码了。 上一节是从这个函数开始的: 弄完data属性的数据绑定后,开始处理el属性,也就是挂载的DOM节点,这里的vm.$options.el也就是传进去的'#app'字符串。 有一个值得注意的点是,源码中有2个$mount函数

  • 本文向大家介绍Vue 中 template 有且只能一个 root的原因解析(源码分析),包括了Vue 中 template 有且只能一个 root的原因解析(源码分析)的使用技巧和注意事项,需要的朋友参考一下 引言 今年, 疫情 并没有影响到各种面经的正常出现,可谓是络绎不绝(学不动...)。然后,在前段时间也看到一个这样的关于 Vue 的问题, 为什么每个组件 template 中有且只能一个

  • 在我的应用程序中,我需要使用硬连线的html模板,我将通过将它们添加到和动态文本模板,我想在运行时存储在s中。 我的代码与此类似: 问题存在于实现ITemplateResolver的中。我试图使用处理字符串模板中给出的示例与thymeleaf 3,但它们似乎不再适用于当前版本的Spring。 如何从s解析模板?我使用SpringBoot2.2

  • Emmet 的介绍 Emmet 的前身叫做:Zen Coding,也许熟知旧名的人不在少数。Emmet 一般前端工程师用得比较多,具体它是做什么的,我们通过下面两张 Gif 演示图来说明: IntelliJ IDEA 自带 Emmet 功能,使用的快捷键是 Tab。 Emmet 资料介绍: Emmet 官网:http://emmet.io Emmet 官网文档:http://docs.emmet.

  • 本文向大家介绍Vue中之nextTick函数源码分析详解,包括了Vue中之nextTick函数源码分析详解的使用技巧和注意事项,需要的朋友参考一下 1. 什么是Vue.nextTick()? 官方文档解释如下: 在下次DOM更新循环结束之后执行的延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。 2. 为什么要使用nextTick? 如上代码 在页面视图上显示bb,但是当我在控制台打印

  • 最近一直在看 vue 2.2.6 的源码,所以准备分几个模块分别记录一下。由于水平有限,对整个框架的源码还没有整体的把握。所以前期内容可能比较零散,更多的是记录自己看的过程。慢慢整理成比较完整的源码分析。 备注:最近为了加深印象,写了一个简单的 mvvm 的实现,参考了 Vue 中模板解析、数据监听、render 函数的生成 传送门。 该源码分析,会带着大家一起学习 Vue 的大部分代码,而不是简