vue 解析模板源码中的一个困惑?
Vue-2.0
// src/compiler/parser/html-parser.jsconst 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判断无论如何都无法成立吧?
vue解析模板是参考的JohnResig的相关代码,JohnResig代码如下。可以看到里边没有这个判断。不知道vue是出于何种动机添加了这个判断。
if (stackedTag !== "script" && stackedTag !== "style" && stackedTag !== "noscript")
// https://johnresig.com/files/htmlparser.jselse {
html = html.replace(new RegExp("(.*)<\/" + stack.last() + "[^>]*>"), function(all, text){
text = text.replace(/<!--(.*?)-->/g, "$1")
.replace(/<!\[CDATA\[(.*?)]]>/g, "$1");
if ( handler.chars )
handler.chars( text );
return "";
});
parseEndTag( "", stack.last() );
}
回答:
看了好久才大概理解:
首先这段代码在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"
反正最后这个方法返回一个包含标签名称、属性和开始和结束位置的对象,如果标签是自闭合的,它还会设置一个标志来指示这一点。篇幅原因具体我不赘述,你可以点击进去自己看看。说白了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")
这个分支了。
以上内容是我大略的想法,难免可能有疏忽大意,欢迎你指出。
回答:
你说的是对的。在这段代码中,else
语句中的if
判断是多余的,因为lastTag
已经被确认为是"script"
或"style"
。所以,在这种情况下,这个if
判断是不必要的,也永远不会成立。
可以简化这段代码,去掉这个if
判断。例如:
// src/compiler/parser/html-parser.jsconst 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");
}
}
}
这样,代码会变得更简洁、易读。
以上是 vue 解析模板源码中的一个困惑? 的全部内容, 来源链接: utcz.com/p/934127.html