• .6-Vue源码之AST(2)


      上一节获取到了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('
    ', '
') : 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方法非常长,单独讲。

  • 相关阅读:
    hdu1203I NEED A OFFER!(01背包)
    poj3984迷宫问题(dfs+stack)
    hdu1422重温世界杯(动态规划,最长子序列)
    hdu1231最大连续子序列(动态规划)
    cf#514B. Forgery(暴力)
    uvaoj455Periodic Strings(枚举)
    uvaoj1225Digit Counting(暴力)
    uvaoj1586Molar mass(暴力)
    uvaoj1585Score(暴力)
    使用哈希值和&和2^n数组生成索引值的原理
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/6905272.html
Copyright © 2020-2023  润新知