1. 程式人生 > >.6-Vue源碼之AST(2)

.6-Vue源碼之AST(2)

png 變量聲明 enc 標簽 ons directive option 復雜 html

  上一節獲取到了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
    }

  這部分就是一個函數的調用,第一個參數E為字符串,第二個參數是一個對象,包含多個屬性與方法,屬性內容如下:

    // 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(2)