• .8-Vue源码之AST(4)


      上一节讲完了超长的start函数,也同时完结了handleStartTag函数,接着continue进入下一轮while循环。

      此时剩余的字符串状态如图:,切掉了<div id='app'>。

      再次进入while循环时,发生了一些变化:

        // Line-7672
        function parseHTML(html, options) {
            /* code */
    
            while (html) {
                last = html;
                if (!lastTag || !isPlainTextElement(lastTag)) {
                    var textEnd = html.indexOf('<');
                    // 此时字符串不是以<开头 所以不会进入此条件
                    if (textEnd === 0) {
                        // ...
                    }
                    var text = (void 0),
                        rest$1 = (void 0),
                        next = (void 0);
                    if (textEnd >= 0) {
                        // 截取<字符索引 => </div>
                        rest$1 = html.slice(textEnd);
                        // 处理文本中的<字符
                        while (!endTag.test(rest$1) &&
                            !startTagOpen.test(rest$1) &&
                            !comment.test(rest$1) &&
                            !conditionalComment.test(rest$1)
                        ) {
                            next = rest$1.indexOf('<', 1);
                            if (next < 0) {
                                break
                            }
                            textEnd += next;
                            rest$1 = html.slice(textEnd);
                        }
                        // 获取中间的字符串 => {{message}}
                        text = html.substring(0, textEnd);
                        advance(textEnd);
                    }
                    // 当字符串没有<时
                    if (textEnd < 0) {
                        text = html;
                        html = '';
                    }
                    // 另外一个函数
                    if (options.chars && text) {
                        options.chars(text);
                    }
                } else {
                    /* code */
                }
    
                if (html === last) {
                    /* code */
                }
            }
            // Clean up any remaining tags
            parseEndTag();
    
            // fn...
        }

      第一次进入while循环时,由于字符串以<开头,所以进入startTag条件,并进行AST转换,最后将对象弹入stack数组中。

      而这一次,字符串开头为{,所以会继续执行下面的代码。代码将{{message}}作为text抽离出来,并调用了参数中另外一个函数:options.chars。

        // Line-8167
        function chars(text) {
            if (!currentParent) {
                // 提示必须有一个DOM根节点
                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
            if (isIE &&
                currentParent.tag === 'textarea' &&
                currentParent.attrsMap.placeholder === text) {
                return
            }
            var children = currentParent.children;
            // text => {{message}}
            text = inPre || text.trim() ?
                isTextTag(currentParent) ? text : decodeHTMLCached(text) : preserveWhitespace && children.length ? ' ' : '';
            if (text) {
                var expression;
                if (!inVPre && text !== ' ' && (expression = parseText(text, delimiters))) {
                    // 将解析后的text弄进children数组
                    children.push({
                        type: 2,
                        expression: expression,
                        text: text
                    });
                } else if (text !== ' ' || !children.length || children[children.length - 1].text !== ' ') {
                    children.push({
                        type: 3,
                        text: text
                    });
                }
            }
        }

      本函数的核心为parseText对text的处理,即{{message}}。

        // Line-7928
        function parseText(text, delimiters) {
            // 正则选择
            var tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE;
            // 在这里调用test方法后lasatIndex会变化
            if (!tagRE.test(text)) {
                return
            }
            var tokens = [];
            var lastIndex = tagRE.lastIndex = 0;
            var match, index;
            // 匹配到中间的文本
            while ((match = tagRE.exec(text))) {
                index = match.index;
                // 将{{message}}之前的文本push进去
                if (index > lastIndex) {
                    tokens.push(JSON.stringify(text.slice(lastIndex, index)));
                }
                // 该方法对特殊字符进行处理 本例暂时用不上
                // 返回的仍然是message字符串
                var exp = parseFilters(match[1].trim());
                // _s(message)
                tokens.push(("_s(" + exp + ")"));
                lastIndex = index + match[0].length;
            }
            if (lastIndex < text.length) {
                // push}}后面的文本
                tokens.push(JSON.stringify(text.slice(lastIndex)));
            }
            return tokens.join('+')
        }

      实际上text可分为3个部分,{{之前的,{{}}中间包裹的,}}之后的,函数分别将三者抽离出来,push进了tokens,最后用+连接并返回一个字符串:

      返回后,将此字符串作为值,和其余属性一个添加到children数组中:

      

      处理完后,进入下一轮while循环。

      剩余的字符串为</div>,所以进入第一个循环,并且匹配到EndTag的分支。

        // Line-7672
        function parseHTML(html, options) {
            /* code */
            while (html) {
                /* code */
                var textEnd = html.indexOf('<');
                if (textEnd === 0) {
                    /* code */
                    var endTagMatch = html.match(endTag);
                    if (endTagMatch) {
                        var curIndex = index;
                        advance(endTagMatch[0].length);
                        parseEndTag(endTagMatch[1], curIndex, index);
                        continue
                    }
                    /* code */
                }
                /* code */
            }
            /* code */
        }

      进入endTag分支后,匹配到的endTagMatch如图所示:

      将当前索引保存为curIndex,然后根据匹配到的字符串往前推index,调用parseEndTag函数进行处理。

        // Line-7863
        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) {
                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);
                    }
                }
    
                // Remove the open elements from the stack
                stack.length = pos;
                // 0
                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);
                }
            }
        }
    
        // Line-8154
        function end() {
            // 获取对象与文本
            var element = stack[stack.length - 1];
            var lastNode = element.children[element.children.length - 1];
            // type是2 跳过
            if (lastNode && lastNode.type === 3 && lastNode.text === ' ' && !inPre) {
                element.children.pop();
            }
            // pop stack
            stack.length -= 1;
            // 变成undefined了
            currentParent = stack[stack.length - 1];
            endPre(element);
        }
    
        // Line-8010
        function endPre(element) {
            if (element.pre) {
                inVPre = false;
            }
            // tag === pre?
            if (platformIsPreTag(element.tag)) {
                inPre = false;
            }
        }

      这个函数对闭合标签进行配对,并对应将stack数组进行变动,由于本例只有一个div,所以stack被清空。

      完事后,continue进入下一轮循环,由于字符串全部被切割完,此时html为空字符串,此时while循环结束,进入下一个代码段:

        // Line-7672
        function parseHTML(html, options) {
            /* code */
            while (html) {
                /* code */
            }
    
            // Clean up any remaining tags
            parseEndTag();
    
            /* 一些方法 */
        }

      字符串解析完后,再次调用parseEndTag进行收尾工作,函数内部将pos置0,stack置空。

      回到了parse函数,并返回了root,即解析后的AST对象:,包含了标签类型、属性、文本内容等。

      

      先结束了吧。

                      

  • 相关阅读:
    什么是Service Mesh
    SQL Server 创建索引(index)
    RocketMQ在面试中那些常见问题及答案+汇总
    怎样用通俗的语言解释REST,以及RESTful?
    RPC和RestFul的区别是什么?
    Java 动态字节码生成技术 javassist
    热加载如此简单,手动写一个 Java 热加载
    Dubbo源码分析(十)同步调用与异步调用
    Dubbo源码分析(九)负载均衡算法
    Dubbo源码分析(八)集群容错机制
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/6957419.html
Copyright © 2020-2023  润新知