|
@@ -2569,7 +2569,7 @@ tokenValue = text.slice(lastIndex, index)
|
|
|
tokenValue = 'abc{{name}}'.slice(0, 3)
|
|
|
```
|
|
|
|
|
|
-可以看到这句代码的最终结果就是将原始文本中的 `'abc'` 字符片段截取了出来,并保存在变量 `tokenValue` 中,我们发现截取出来的字符片段就是字面量表达式前的普通文本,这段普通文本的文本内容除了会保存在 `tokenValue` 变量中外还会被 `push` 到 `rawTokens` 数组中。另外我们注意到在这段 `if` 条件语句中还有如下这句代码:
|
|
|
+可以看到这句代码的最终结果就是将原始文本中的 `'abc'` 字符片段截取了出来,并保存在变量 `tokenValue` 中,我们发现截取出来的字符片段就是字面量表达式前的普通文本,这段普通文本的文本内容除了会保存在 `tokenValue` 变量中之外还会被 `push` 到 `rawTokens` 数组中。另外我们注意到在这段 `if` 条件语句中还有如下这句代码:
|
|
|
|
|
|
```js
|
|
|
tokens.push(JSON.stringify(tokenValue))
|
|
@@ -2623,7 +2623,7 @@ rawTokens = [
|
|
|
lastIndex = index + match[0].length
|
|
|
```
|
|
|
|
|
|
-这句代码使用的作用是更新 `lastIndex` 变量的值,可以看到 `lastIndex` 变量的值等于 `index` 变量的值加上匹配的字符串的长度,我们以字符串 `'abc{{name}}def'` 为例,此时 `lastIndex` 变量的初始值为 `0`;`index` 变量的值为 `3`,指向第一左花括号(`{`);`match[0].length` 的值为匹配的字符串 `'{{name}}'` 的长度,所以 `match[0].length` 的值为 `8`,最终:
|
|
|
+这句代码的作用是更新 `lastIndex` 变量的值,可以看到 `lastIndex` 变量的值等于 `index` 变量的值加上匹配的字符串的长度,我们以字符串 `'abc{{name}}def'` 为例,此时 `lastIndex` 变量的初始值为 `0`;`index` 变量的值为 `3`,指向第一个左花括号(`{`);`match[0].length` 的值为匹配的字符串 `'{{name}}'` 的长度,所以 `match[0].length` 的值为 `8`,最终:
|
|
|
|
|
|
```js
|
|
|
lastIndex = 3 + 8 // lastIndex = 11
|
|
@@ -2698,7 +2698,7 @@ end () {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-如上这段代码是 `parseHTML` 函数的 `end` 钩子函数,当解析 `html` 字符串遇到结束标签的时候,会调用该钩子函数并传递三个参数,不过我们发现在如上代码中并没有使用到 `end` 钩子函数的任何参数,这是因为当遇到结束标签时的处理逻辑根本用不到这些参数。那么在 `end` 钩子函数中都需要做哪些事情呢?关于这个问题在之前章节中的讲解中我们多少都提到过了,我们知道每当解析器遇到非一元标签的开始标签时,会将该标签的元素描述对象设置给 `currentParent` 变量,代表后续解析过程中遇到的所有标签都应该是 `currentParent` 变量所代表的标签的子节点,同时还会将该标签的元素描述对象添加到 `stack` 栈中。而当遇到结束标签的时候则意味着 `currentParent` 变量所代表的标签以及其子节点全部解析完毕了,此时我们应该把 `currentParent` 变量的引用修改为当前标签的父标签,这样我们就将作用域还原给了上层节点,以保证解析过程中正确的父子关系。如下这段代码就是用来完成这些工作的:
|
|
|
+如上这段代码是 `parseHTML` 函数的 `end` 钩子函数,当解析 `html` 字符串遇到结束标签的时候,会调用该钩子函数并传递三个参数,不过我们发现在如上代码中并没有使用到 `end` 钩子函数的任何参数,这是因为当遇到结束标签时的处理逻辑根本用不到这些参数。那么在 `end` 钩子函数中都需要做哪些事情呢?关于这个问题在之前章节的讲解中我们多少都提到过了,我们知道每当解析器遇到非一元标签的开始标签时,会将该标签的元素描述对象设置给 `currentParent` 变量,代表后续解析过程中遇到的所有标签都应该是 `currentParent` 变量所代表的标签的子节点,同时还会将该标签的元素描述对象添加到 `stack` 栈中。而当遇到结束标签的时候则意味着 `currentParent` 变量所代表的标签以及其子节点全部解析完毕了,此时我们应该把 `currentParent` 变量的引用修改为当前标签的父标签,这样我们就将作用域还原给了上层节点,以保证解析过程中正确的父子关系。如下这段代码就是用来完成这些工作的:
|
|
|
|
|
|
```js
|
|
|
// pop stack
|
|
@@ -2730,7 +2730,7 @@ function closeElement (element) {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-它的工作由两个,第一个是对数据状态的还原,我们知道每当遇到 `<pre>` 标签的开始标签时,解析器会将 `inPre` 变量设置为 `true`,这代表着后续解析所遇到的标签都存在于 `<pre>` 标签中,一旦 `<pre>` 标签内的所有内容解析完毕后,解析器将会遇到 `<pre>` 标签的结束标签,此时 `platformIsPreTag(element.tag)` 将会为真,如上代码所示,会将 `inPre` 变量的值重置为 `false`。同样的道理,如果需要的话还会重置 `inVPre` 变量的值。`closeElement` 函数的第二个作用是调用后置处理转换钩子函数,即如上代码中的 `for` 循环部分,这段代码我们在前面的章节中中已经讲解过了,这里不再细说。
|
|
|
+它的工作有两个,第一个是对数据状态的还原,我们知道每当遇到 `<pre>` 标签的开始标签时,解析器会将 `inPre` 变量设置为 `true`,这代表着后续解析所遇到的标签都存在于 `<pre>` 标签中,一旦 `<pre>` 标签内的所有内容解析完毕后,解析器将会遇到 `<pre>` 标签的结束标签,此时 `platformIsPreTag(element.tag)` 将会为真,如上代码所示,会将 `inPre` 变量的值重置为 `false`。同样的道理,如果需要的话还会重置 `inVPre` 变量的值。`closeElement` 函数的第二个作用是调用后置处理转换钩子函数,即如上代码中的 `for` 循环部分,这段代码我们在前面的章节中已经讲解过了,这里不再细说。
|
|
|
|
|
|
我们回到 `end` 钩子函数,注意如下高亮的代码:
|
|
|
|
|
@@ -2749,7 +2749,7 @@ end () {
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-这段高亮代码的作用是去除当前元素最后一个空白子节点的,我们在讲解 `chars` 钩子函数时了解到:**`preserveWhitespace` 只会保留那些不在开始标签之后的空格(说空白也没问题)**,所以当空白作为标签的最后一个子节点存在时,也会被保留,如下代码所示:
|
|
|
+这段高亮代码的作用是去除当前元素最后一个空白子节点,我们在讲解 `chars` 钩子函数时了解到:**`preserveWhitespace` 只会保留那些不在开始标签之后的空格(说空白也没问题)**,所以当空白作为标签的最后一个子节点存在时,也会被保留,如下代码所示:
|
|
|
|
|
|
```html
|
|
|
<div><span>test</span> <!-- 空白占位 --> </div>
|
|
@@ -2759,7 +2759,7 @@ end () {
|
|
|
|
|
|
## 注释节点的元素描述对象
|
|
|
|
|
|
-解析器是否会解析并保留注释节点,是由 `shouldKeepComment` 编译器选项决定的,开发者可以在创建 `Vue` 实例的时候通过设置 `comments` 选项的值来控制编译器的 `shouldKeepComment` 选项。没人情况 `comments` 选项的值为 `false`,即不保留注释,假如将其设置为 `true`,则当计息期遇到注释节点时会保留该注释节点,此时 `parseHTML` 函数的 `comment` 钩子函数会被调用,如下:
|
|
|
+解析器是否会解析并保留注释节点,是由 `shouldKeepComment` 编译器选项决定的,开发者可以在创建 `Vue` 实例的时候通过设置 `comments` 选项的值来控制编译器的 `shouldKeepComment` 选项。默认情况下 `comments` 选项的值为 `false`,即不保留注释,假如将其设置为 `true`,则当解析器遇到注释节点时会保留该注释节点,此时 `parseHTML` 函数的 `comment` 钩子函数会被调用,如下:
|
|
|
|
|
|
```js
|
|
|
comment (text: string) {
|
|
@@ -2773,7 +2773,6 @@ comment (text: string) {
|
|
|
|
|
|
`comment` 钩子函数接收注释节点的内容作为参数,在 `comment` 钩子函数内所做的事情很简单,就是为当前注释节点创建一个类型为 `3` 并且 `isComment` 属性为 `true` 的元素描述对象,并将其添加到父节点元素描述对象的 `children` 数组内。
|
|
|
|
|
|
-大家需要注意的是,普通文本节点与注释节点的元素描述对象的类型是一样的,都是 `3`,不同的是注释节点的元素描述对象拥有 `isComment` 属性,并且该属性的值为 `true`,目的就是用来与普通文本节点做区分的。
|
|
|
+大家需要注意的是,普通文本节点与注释节点的元素描述对象的类型是一样的,都是 `3`,不同的是注释节点的元素描述对象拥有 `isComment` 属性,并且该属性的值为 `true`,目的就是用来与普通文本节点作区分的。
|
|
|
|
|
|
至此,对于解析器相关的内容我们就全部讲解完毕了,最终解析器把 `Vue` 的模板解析为抽象语法树(`AST`),强烈建议读完本节的同学能够仔细阅读以下附录 [Vue 模板 AST 详解](../appendix/ast.md),相信你一定会有更多的收获。
|
|
|
-
|