【Vue原理】Compile - 源碼版 之 重新建實例到 compile結束的主要流程

寫文章不容易,點個讚唄兄弟

專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧 研究基於 Vue版本 【2.5.17】數組

若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧緩存

【Vue原理】Compile - 源碼版 之 重新建實例到 compile結束的主要流程 bash

Compile 的內容十分之多,今天先來個熱身,先不研究 compile 內部編譯細節,而是記錄一下閉包

重新建實例開始,到結束 compile ,其中的大體外部流程,不涉及 compile 的內部流程函數

或者說,咱們要研究 compile 這個函數是怎麼生成的學習

注意,若是你沒有準備好,請不要閱讀這篇文章ui

注意哦,會很繞,別暈了this

公衆號

好的,正文開始spa

首先,當咱們經過 Vue 新建一個實例的時候會調用Vueprototype

公衆號

因此從 Vue 函數入手

function Vue(){    

    // .....

    vm.$mount(vm.$options.el);
}
複製代碼

而後內部的其餘處理均可以忽視,直接定位到 vm.$mount,就是從這裏開始去編譯的

繼續去查找這個函數

Vue.prototype.$mount = function(el) {    



    var options = this.$options;

   

    if (!options.render) {  

     

        var tpl= options.template;  

   

        // 獲取模板字符串
        if (tpl) {    

                // 根據傳入的選擇器找到元素,而後拿到該元素內的模板

                //  原本有不少種獲取方式,可是爲了簡單,咱們簡化爲一種,知道意思就能夠了

            tpl = document.querySelector(tpl).innerHTML;
        }    

     

        if (tpl) {  

            // 生成 render 函數保存

            var ref = compileToFunctions(tpl, {},this);  

          

            // 每個組件,都有本身的 render

            options.render = ref.render
            options.staticRenderFns =ref.staticRenderFns;
        }
    }      



    // 執行上面生成的 render,生成DOM,掛載DOM,這裏忽略不討論
    return mount.call(this, el)
};
複製代碼

compile 的主要做用就是,根據 template 模板,生成 render 函數

那麼到這裏,整個流程就走完了,由於 render 已經在這裏生成了

公衆號

咱們觀察到

在上面這個函數中,主要就作了三件事

1 獲取 template 模板

根據你傳入的參數,來各類獲取 template 模板

這裏應該都看得懂了,根據DOM,或者根據選擇器

2 生成 render

經過 compileToFunctions ,傳入 template

就能夠生成 render 和 staticRenderFns

看着是挺簡單哦,就一個 compileToFunctions,可是我告訴你,這個函數的誕生可不是這麼容易的,兜兜轉轉,十分曲折,至關得曲折複雜,沒錯,這就是咱們下面研究的重點

可是這流程其實好像也沒有什麼幫助?可是若是你閱讀源碼的話,或許能夠對你理清源碼有些許幫助吧

再一次佩服 尤大的腦回路

公衆號

3 保存 render

保存在 vm.$options 上,用於在後面調用

公衆號

下面就來講 compileToFunctions 的誕生史

注意,很繞很繞,作好心理準備

首先我定位到 compileToFunctions,看到下面這段代碼

var ref$1 = createCompiler();



// compileToFunctions 會返回 render 函數 以及 staticRenderFns

var compileToFunctions = ref$1.compileToFunctions;
複製代碼

因而我知道

compileToFunctions 是 createCompiler 執行返回的!!

那麼繼續定位 createCompiler


createCompiler

var createCompiler = createCompilerCreator(  



    function baseCompile(template, options) {    

    

        var ast = parse(template.trim(), options);    

    

        if (options.optimize !== false) {

            optimize(ast, options);
        }    

   

        var code = generate(ast, options);  

     

        return {            

            ast: ast,            

            render: code.render,            

            staticRenderFns: code.staticRenderFns

        }
    }
);
複製代碼

臥槽,又來一個函數,別暈啊兄弟

公衆號
不過,注意注意,這裏是重點,很是重

公衆號

首先明確兩點

一、createCompiler 是 createCompilerCreator 生成的

二、給 createCompilerCreator 傳了一個函數 baseCompile

baseCompile

這個 baseCompile 就是 生成 render 的大佬

看到裏面包含了 渲染三巨頭,【parse,optimize,generate】

可是今天不是講這個的,這三個東西,每一個內容都十分巨大

這裏先跳過,反正 baseCompile 很重要,會在後面被調用到,得先記着

而後,沒錯,咱們又遇到了一個 函數 createCompilerCreator ,定位它!


createCompilerCreator

function createCompilerCreator(baseCompile) {    



    return function () {

       

        // 做用是合併選項,而且調用 baseCompile
        function compile(template) {  

         

            // baseCompile 就是 上一步傳入的,這裏執行獲得 {ast,render,statickRenderFn}

            var compiled = baseCompile(template);            

            return compiled

        }        

        return {          



            // compile 執行會返回 baseCompile 返回的 字符串 render

            compile: compile,      

     

            // 爲了建立一層 緩存閉包,而且閉包保存 compile

            // 獲得一個函數,這個函數是 把 render 字符串包在 函數 中
            compileToFunctions: createCompileToFunctionFn(compile)
        }
    }
}
複製代碼

這個函數執行事後,會返回一個函數

很明顯,返回的函數就 直接賦值 給了上面講的的 createCompiler

咱們看下這個返回給 createCompiler 的函數裏面都幹了什麼?

生成一個函數 compile

內部存在一個函數 compile,這個函數主要做用是

調用 baseCompile,把 baseCompile 執行結果 return 出去

baseCompile 以前咱們強調過的,就是那個生成 render 的大佬

忘記的,能夠回頭看看,執行完畢會返回

{ render,staticRenderFns }

返回 compileToFunctions 和 compile

其實 返回的這兩個函數的做用大體都是同樣的

都是爲了執行上面那個 內部 compile

可是爲何分出一個 compileToFunctions 呢?

還記得開篇咱們的 compileToFunctions 嗎

就是那個在 vm.$mount 裏咱們要探索的東西啊

就是他這個吊毛,生成的 render 和 staticRenderFns

公衆號

再看看那個 內部 compile,能夠看到他執行完就是返回

{ render, staticRenderFns }

你看,內部 compile 就是 【vm.$mount 執行的 compileToFunctions】 啊

爲何 compileToFunctions 沒有直接賦值爲 compile 呢!!

由於要作模板緩存!!

能夠看到,沒有直接讓 compileToFunctions = 內部compile

而是把 內部 compile 傳給了 createCompileToFunctionFn

沒錯 createCompileToFunctionFn 就是作緩存的

爲了不每一個實例都被編譯不少次,因此作緩存,編譯一次以後就直接取緩存

createCompileToFunctionFn

來看看內部的源碼,緩存的代碼已經標紅

function createCompileToFunctionFn(compile) {    



    // 做爲緩存,防止每次都從新編譯

    // template 字符串 爲 key , 值是 render 和 staticRenderFns
    var cache = Object.create(null);    



    return function compileToFunctions(template, options, vm) {        

        var key = template;        



        // 有緩存的時候直接取出緩存中的結果便可

        if (cache[key]) return cache[key]  

     

        // compile 是 createCompileCreator 傳入的compile

        var compiled = compile(template, options);        

        var res = {            



            // compiled.render 是字符串,須要轉成函數

            render : new Function(compiled.render)
            staticRenderFns : compiled.staticRenderFns.map(function(code) {                

                return  new Function(code, fnGenErrors)

            });
        };        



        return (cache[key] = res)

    }
}

複製代碼

額外:render 字符串變成可執行函數

var res = {    

    render: new Function(compiled.render) ,    

    staticRenderFns: compiled.staticRenderFns.map(function(code) {        

        return new Function(code, fnGenErrors)

    });
};
複製代碼

這段代碼把 render 字符串可執行函數,由於render生成的形態是一個字符串,若是後期要調用運行,好比轉成函數

因此這裏使用了 new Function() 轉化成函數

同理,staticRenderFns 也同樣,只不過他是數組,須要遍歷,逐個轉化成函數

他的緩存是怎麼作的

使用一個 cache 閉包變量

template 爲 key

生成的 render 做爲 value

當實例第一次渲染解析,就會被存到 cache 中

當實例第二次渲染解析,那麼就會從 cache 中直接獲取

何時實例會解析第二次?

好比 頁面A到頁面B,頁面B又轉到頁面A。

頁面A 這個實例,按理就須要解析兩次,可是有緩存以後就不會


理清思路

公衆號

也就是說,compileToFunctions 其實內核就是 baseCompile!

不過 compileToFunctions 是通過了 兩波包裝的 baseCompile

第一波包裝在 createCompilerCreator 中的 內部 compile 函數中

內部函數的做用是

合併公共options和 自定義options ,可是相關代碼已經省略,

另外一個就是執行 baseCompile

第二波包裝在 createCompileToFunctions 中,目的是進行 緩存

公衆號

公衆號
相關文章
相關標籤/搜索