3

parseHTML()执行完第一次循环之后,html变成:

clipboard.png
接下来开始执行第二次循环,解析的是文本"(回车)请输入:"

  while (html) {
    last = html;
    if (!lastTag || !isPlainTextElement(lastTag)) {
      var textEnd = html.indexOf('<');
      if (textEnd === 0) {
         /*当新的html以<开头,执行以下代码*/
      }
      //新的循环执行这段
      var text = (void 0), rest = (void 0), next = (void 0);
      if (textEnd >= 0) {
        //截取字符串,存放在rest中
        rest = html.slice(textEnd);
        while (
          !endTag.test(rest) &&
          !startTagOpen.test(rest) &&
          !comment.test(rest) &&
          !conditionalComment.test(rest)
        ) {
          // 如果纯文本中存在<,作为文本处理
          next = rest.indexOf('<', 1);
          if (next < 0) { break }
          textEnd += next;
          rest = html.slice(textEnd);
        }
        //获取文本字符串,此处就是‘(回车)请输入:’
        text = html.substring(0, textEnd);
        //重新修改index索引,重新截取html
        advance(textEnd);
      }

      if (textEnd < 0) {
        text = html;
        html = '';
      }

      if (options.chars && text) {
        //调用parseHTML里面定义的chars方法
        options.chars(text);
      }
    } else {
      /*代码已省略*/
    }
    //当html是最后一段时间运行这段代码
    if (html === last) {
      /*代码已省略*/
    }
  }

第二次循环,字符串为纯文本"(回车)请输入:",所以会继续执行下面的代码,调用parseHTML里面定义的chars方法。

chars

chars: function chars (text) {
      if (!currentParent) {
        {
          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的 bug,学习于https://blog.csdn.net/wide288/article/details/51094041
      if (isIE &&
        currentParent.tag === 'textarea' &&
        currentParent.attrsMap.placeholder === text
      ) {
        return
      }
      var children = currentParent.children;
      text = inPre || text.trim()
        ? isTextTag(currentParent) ? text : decodeHTMLCached(text)
        // only preserve whitespace if its not right after a starting tag
        : preserveWhitespace && children.length ? ' ' : '';
      if (text) {
        var res;
        if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) {
          children.push({
            type: 2,
            expression: res.expression,
            tokens: res.tokens,
            text: text
          });
        } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
        //将解析后的text push到children数组
          children.push({
            type: 3,
            text: text
          });
        }
      }
    }

chars方法对text进行处理,并转换成children添加到当天父节点下。这里text是纯静态文本,走的代码是children.push({type: 3,text: text});,将type定为3,若text是{{message}},则走的是children.push({type: 2,expression: res.expression, tokens: res.tokens,text: text});,将type设为2。
接下来执行第三次循环,解析<input type="text" v-model="message">

clipboard.png
解析过程与第一次相同。
第四次循环解析<br/>

clipboard.png
解析过程与第一次相同。
第五次循环解析的时<的位置是1,因此解析的其实是回车,,text就是"",解析过程与第二次相同。但最后在调用end方法是,该条children会从数组中pop出来。

clipboard.png
第六次循环解析的就是结束标签</div>

clipboard.png
在parseHTML()匹配到endTag之后调用parseEndTag()方法。

parseEndTag

  function 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 shop
      pos = 0;
    }

    if (pos >= 0) {
      // Close all the open elements, up the stack
      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);
        }
      }

      //对应将stack数组进行变动
      //pos=0;
      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()首先对闭合标签进行匹配,将start和end的值设为index最后的索引值,即html的末位,然后调用options里面的end方法清空stack,最后修改stack的长度为0。end方法的代码如下:

end

end: function end () {
      // 获取对象与文本
      var element = stack[stack.length - 1];
      var lastNode = element.children[element.children.length - 1];
      if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) {
        //如果lastNode是空文本,把lastNode弹出
        element.children.pop();
      }
      // pop stack
      stack.length -= 1;
      currentParent = stack[stack.length - 1];
      closeElement(element);
    }

至此parseHTML循环结束,返回的ast对象有如下这些信息:

clipboard.png


花花呀
375 声望23 粉丝

学无止境 做有灵魂的程序员