.6-Vue源码之AST

Posted QH-Jimmy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.6-Vue源码之AST相关的知识,希望对你有一定的参考价值。

  上一节获取到了DOM树的字符串,准备进入compile阶段:

    // Line-9326
    function compileToFunctions(template,options,vm) {
        // 获取配置参数
        options = options || {};

        // ...

        // Go!
        var compiled = compile(template, options);

        // ...
    }

  该函数接受两个参数,DOM树字符串、配置参数,如图:,进函数:

    // Line-9287
    function compile(template, options) {
        var finalOptions = Object.create(baseOptions);
        var errors = [];
        var tips = [];
        finalOptions.warn = function(msg, tip$$1) {
            (tip$$1 ? tips : errors).push(msg);
        };
        // 合并参数
        if (options) {
            if (options.modules) {
                finalOptions.modules = (baseOptions.modules || []).concat(options.modules);
            }
            if (options.directives) {
                finalOptions.directives = extend(
                    Object.create(baseOptions.directives),
                    options.directives
                );
            }
            for (var key in options) {
                if (key !== \'modules\' && key !== \'directives\') {
                    finalOptions[key] = options[key];
                }
            }
        }
        // 核心编译函数
        var compiled = baseCompile(template, finalOptions);
        errors.push.apply(errors, detectErrors(compiled.ast));
        // 提示与报错属性添加
        compiled.errors = errors;
        compiled.tips = tips;
        return compiled
    }

  compile主要做了3件事:

  1、参数合并

  这里涉及到baseOptions与传进来的options,baseOptions是内置对象,与options合并后得到finalOptions作为参数传进第二步的函数。

    // Line-9529
    var baseOptions = {
        expecthtml: true,
        modules: modules$1,
        directives: directives$1,
        isPreTag: isPreTag,
        isUnaryTag: isUnaryTag,
        mustUseProp: mustUseProp,
        canBeLeftOpenTag: canBeLeftOpenTag,
        isReservedTag: isReservedTag,
        getTagNamespace: getTagNamespace,
        staticKeys: genStaticKeys(modules$1)
    };

  这个对象十分复杂,涉及的时候再做讲解。

 

  2、调用baseCompile函数

  该函数接受2个参数:字符串、参数对象。

    // Line-9261
    function baseCompile(template, options) {
        // 解析字符串为AST
        var ast = parse(template.trim(), options);
        // 优化
        optimize(ast, options);
        // 
        var code = generate(ast, options);
        return {
            ast: ast,
            render: code.render,
            staticRenderFns: code.staticRenderFns
        }
    }

  简单看一眼这个函数,3个调用都很简单暴力,后面一个一个讲解。

 

  3、添加提示、报错属性并返回compiled值

  将过程中出现的error与tips作为属性添加到compiled对象上,以便一次性输出。

 

  跑流程的话,主要还是看第二步,理一理DOM树字符串是如何被解析和编译的,来看parse这个函数吧! 

  这函数太长了,分两部分来: 

    // Line-7982
    function parse(template, options) {
        // 参数修正
        warn$2 = options.warn || baseWarn;
        // 这几个属性都是原型链上面的
        platformGetTagNamespace = options.getTagNamespace || no;
        platformMustUseProp = options.mustUseProp || no;
        platformIsPreTag = options.isPreTag || no;
        preTransforms = pluckModuleFunction(options.modules, \'preTransformNode\');
        transforms = pluckModuleFunction(options.modules, \'transformNode\');
        postTransforms = pluckModuleFunction(options.modules, \'postTransformNode\');
        // 这是自家的 值为undefined…
        delimiters = options.delimiters;

        // 变量声明
        var stack = [];
        var preserveWhitespace = options.preserveWhitespace !== false;
        var root;
        var currentParent;
        var inVPre = false;
        var inPre = false;
        var warned = false;

        // 功能函数
        function warnOnce(msg) {
            if (!warned) {
                warned = true;
                warn$2(msg);
            }
        }

        function endPre(element) {
            if (element.pre) {
                inVPre = false;
            }
            if (platformIsPreTag(element.tag)) {
                inPre = false;
            }
        }

        parseHTML( /*...*/ );
        return root
    }

   首先是获取options参数的属性,其中大部分都是定义在原型上,即baseOptions,所以没什么讲的。唯一涉及的函数就是pluckModuleFunction,比较简单,看一下内容:

    // Line-5684
    function pluckModuleFunction(modules, key) {
        // 返回modules[key]组成的数组
        return modules ?
            modules.map(function(m) {
                return m[key];
            }).filter(function(_) {
                return _;
            }) : []
    }

  简而言之,就是返回一个数组,内容是modules[key],这里返回空数组。

 

  第一部分没什么讲的,主要是声明一些变量,第二部分才是核心:

    // Line-7982
    function parse(template, options) {
        // ...part-1

        parseHTML(template, {
            warn: warn$2,
            expectHTML: options.expectHTML,
            isUnaryTag: options.isUnaryTag,
            canBeLeftOpenTag: options.canBeLeftOpenTag,
            shouldDecodeNewlines: options.shouldDecodeNewlines,
            start: function start(tag, attrs, unary) {
                /* code... */
            },

            end: function end() {
                /* code... */
            },

            chars: function chars(text) {
                /* code... */
            }
        });
        return root
    }

  这部分就是巨大的函数调用,第一个参数为DOM字符串,第二个参数是一个对象,包含多个属性与方法,属性内容如下:

    // Line-7578
    // 自闭合标签
    var isUnaryTag = makeMap(
        \'area,base,br,col,embed,frame,hr,img,input,isindex,keygen,\' +
        \'link,meta,param,source,track,wbr\'
    );
    // 可以省略闭合标签
    var canBeLeftOpenTag = makeMap(
        \'colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source\'
    );

    // 当前浏览器是否会对换行转义
    var shouldDecodeNewlines = inBrowser ? shouldDecode(\'\\n\', \'
\') : false;

    function shouldDecode(content, encoded) {
        var div = document.createElement(\'div\');
        div.innerHTML = "<div a=\\"" + content + "\\">";
        return div.innerHTML.indexOf(encoded) > 0
    }

  第一个expectHTML在baseOptions中是默认true的,其余几个是标签名数组与换行转义判断的集合。

 

  剩余三个方法包括start、end、chars,单独讲不好讲,在parseHTML方法边跑边说。

  暂时结束,parseHTML方法非常长,单独讲。

以上是关于.6-Vue源码之AST的主要内容,如果未能解决你的问题,请参考以下文章

Cobar源码分析之AST

vue源码分析之目录架构

vue3源码分析——ast生成代码 - 掘金

vue3源码分析——ast生成代码 - 掘金

vue3源码分析——ast生成代码 - 掘金

vue3源码分析——ast生成代码 - 掘金