浏览代码

修改语误

HcySunYang 7 年之前
父节点
当前提交
0da8917e23
共有 1 个文件被更改,包括 100 次插入0 次删除
  1. 100 0
      note/9Vue中的html-parser.md

+ 100 - 0
note/9Vue中的html-parser.md

@@ -1048,5 +1048,105 @@ if (options.start) {
 
 如果 `parser` 选项中包含 `options.start` 函数,则调用之,并将开始标签的名字(`tagName`),格式化后的属性数组(`attrs`),是否为一元标签(`unary`),以及开始标签在元 `html` 中的开始和结束位置(`match.start` 和 `match.end`) 作为参数传递。
 
+##### parse 结束标签
 
+接下来我们将会讲解 `textEnd === 0` 时的最后一种情况,即可能是结束标签,`parse` 结束标签的代码如下:
+
+```js
+// End tag:
+const endTagMatch = html.match(endTag)
+if (endTagMatch) {
+  const curIndex = index
+  advance(endTagMatch[0].length)
+  parseEndTag(endTagMatch[1], curIndex, index)
+  continue
+}
+```
+
+首先调用 `html` 字符串的 `match` 函数匹配正则 `endTag`,将结果保存在常量 `endTagMatch` 中。正则 `endTag` 用来匹配结束标签,并且拥有一个捕获组用来捕获标签名字,比如有如下 `html` 字符串:
+
+```html
+<div></div>
+```
+
+则匹配后 `endTagMatch` 如下:
+
+```js
+endTagMatch = [
+  '</div>',
+  'div'
+]
+```
+
+第一个元素是整个匹配到的结束标签字符串,第二个元素是对应的标签名字。同时如果匹配成功 `if` 语句块的代码将被执行,首先使用 `curIndex` 常量存储当前 `index` 的值,然后调用 `advance` 函数,并以 `endTagMatch[0].length` 作为参数,接着调用了 `parseEndTag` 函数对结束标签进行解析,传递给 `parseEndTag` 函数的三个参数分别是:标签名以及结束标签在 `html` 字符串中起始和结束的位置,最后调用 `continue` 语句结束此次循环。
+
+关键点在于 `parseEndTag` 函数,它定义在 `handleStartTag` 函数的下面:
+
+```js
+function parseEndTag (tagName, start, end) {
+  let pos, lowerCasedTagName
+  if (start == null) start = index
+  if (end == null) end = index
+
+  if (tagName) {
+    lowerCasedTagName = tagName.toLowerCase()
+  }
+
+  // Find the closest opened tag of the same type
+  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) {
+    // Close all the open elements, up the stack
+    for (let i = stack.length - 1; i >= pos; i--) {
+      if (process.env.NODE_ENV !== '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)
+    }
+  }
+}
+```
+
+`parseEndTag` 函数的代码看上去很长,但实际上它所做的事情并没有想象的那么复杂。按照通常的逻辑,在调用 `parseEndTag` 函数之前已经获得到了结束标签的名字以及结束标签在原始 `html` 字符串中的起始和结束位置,所以完全可以直接调用 `parser` 钩子 `options.end(tagName, start, end)`,然后宣布大功告成。然而实际上 `Vue` 的 `html parser` 并没有这样做,而是又调用了 `parseEndTag` 函数,那说明必然有其他的事情需要处理,到底是什么事情呢?我们可以想象一下 `parseEndTag` 函数都会做什么事情,首先 `parseEndTag` 函数的执行说明此时正在 `parse` 结束标签,假设我们有如下 `html` 字符串:
+
+```html
+<article><section><div></section></article>
+```
+
+很明显,`<div>` 标签缺少结束标签:`</div>`,那么此时是不是应该给用户一个提示?而这就是 `parseEndTag` 函数所做的事情之一。除此之外我们再看如下 `html` 字符串:
+
+```html
+<article><section></section></article><div>
+```