vue原理Compile从新建实例到结束流程源码

引言

Compile 的内容十分之多,今天先来个热身,先不研究 compile 内部编译细节,而是记录一下

从新建实例开始,到结束 compile ,其中的大致外部流程,不涉及 compile 的内部流程

或者说,我们要研究 compile 这个函数是怎么生成的

注意,如果你没有准备好,请不要阅读这篇文章

注意哦,会很绕,别晕了

好的,正文开始

首先,当我们通过 Vue 新建一个实例的时候会调用Vue

所以从 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 上,用于在后面调用

vue原理Compile从新建实例到结束流程源码_第1张图片

下面就来说 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
        }
    }
);

卧槽,又来一个函数,别晕啊兄弟

不过,注意注意,这里是重点,非常重

首先明确两点

1、createCompiler 是 createCompilerCreator 生成的

2、给 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

vue原理Compile从新建实例到结束流程源码_第2张图片

再看看那个 内部 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 这个实例,按理就需要解析两次,但是有缓存之后就不会

理清思路

vue原理Compile从新建实例到结束流程源码_第3张图片

也就是说,compileToFunctions 其实内核就是 baseCompile!

不过 compileToFunctions 是经过了 两波包装的 baseCompile

第一波包装在 createCompilerCreator 中的 内部 compile 函数中

内部函数的作用是

合并公共options和 自定义options ,但是相关代码已经省略,

另一个就是执行 baseCompile

第二波包装在 createCompileToFunctions 中,目的是进行 缓存

以上就是vue原理Compile从新建实例到结束流程源码的详细内容,更多关于vue原理Compile新建实例的资料请关注脚本之家其它相关文章!

你可能感兴趣的:(vue原理Compile从新建实例到结束流程源码)