【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 中,目的是进行 缓存

公众号

公众号
相关文章
相关标签/搜索