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

目录
  • 引言
    • 1 获取 template 模板
    • 2 生成 render
    • 3 保存 render
  • createCompiler
    • baseCompile
  • createCompilerCreator
    • 生成一个函数 compile
    • 返回 compileToFunctions 和 compile
    • 但是为什么分出一个 compileToFunctions 呢?
    • 因为要做模板缓存!!
  • createCompileToFunctionFn
    • 他的缓存是怎么做的
  • 理清思路
    • 内部函数的作用是

引言

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

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

再看看那个 内部 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 中,目的是进行 缓存

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

(0)

相关推荐

  • Vue 中的compile操作方法

    在 Vue 里,模板编译也是非常重要的一部分,里面也非常复杂,这次探究不会深入探究每一个细节,而是走一个全景概要,来吧,大家和我一起去一探究竟. 初体验 我们看了 Vue 的初始化函数就会知道,在最后一步,它进行了 vm.$mount(el) 的操作,而这个 $mount 在两个地方定义过,分别是在 entry-runtime-with-compiler.js(简称:eMount) 和 runtime/index.js(简称:rMount) 这两个文件里,那么这两个有什么区别呢? // entr

  • vue原理Compile之optimize标记静态节点源码示例

    目录 引言 而 optimize 的作用是什么呢? 1 是否存在 v-pre 2 不能存在 node.hasBindings 3 不能存在 node.if 和 node.for 4 节点名称不能是slot 或者 component 5 isPlatformReservedTag(node.tag) 6 isDirectChildOfTemplateFor(node) 7 Object.keys(node).every(isStaticKey) 标记静态节点 1 isStatic 这个方法对 as

  • Vue编译器optimize源码分析

    目录 引言 optimize 源码之旅 markStatic$1源码 isStatic源码 复杂点的 回归到markStatic$1 markStaticRoots 源码 引言 接上文 parseHTML 函数源码解析 chars.end.comment钩子函数 上一章节我们讲到通过解析将template转成AST(抽象语法树),接下来继续对模型树优化,进行静态标注.那么问题来了,什么是静态标注?为什么要静态标注. 在源码的注释中我们找到了下面这段话: /** * Goal of the opt

  • 详解如何解决Vue和vue-template-compiler版本之间的问题

    今天把远程仓库拉下项目,运行'npm run dev'时,报错 Module build failed: Error: Cannot find module 'vue-template-compiler' 报错原因:通常出现于一些依赖库的更新或者安装新的依赖库之后(可以认为npm update已经成为一种习惯),导致了vue和vue-template-compiler的版本不一致. 解决方案:统一vue和vue-template-compiler的版本 "vue": "2.3

  • 解决vue安装less报错Failed to compile with 1 errors的问题

    1.创建vue项目后安装less,执行 npm install less less-loader --save-dev 下载版本为:less-loader@6.1.0 , less@3.11.3,重启服务报错,报错信息如下: 2.报错原因 less 本版太高需要降低版本,执行代码 先移除之前版本: npm uninstall less-loader 下载指定版本: npm install less-loader@5.0.0 -D 3.重启代码就可以了,若还是报错可移除文件node_modules

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

    目录 引言 1 获取 template 模板 2 生成 render 3 保存 render createCompiler baseCompile createCompilerCreator 生成一个函数 compile 返回 compileToFunctions 和 compile 但是为什么分出一个 compileToFunctions 呢? 因为要做模板缓存!! createCompileToFunctionFn 他的缓存是怎么做的 理清思路 内部函数的作用是 引言 Compile 的内容

  • vue+axios实现文件下载及vue中使用axios的实例

    功能:点击导出按钮,提交请求,下载excel文件: 第一步:跟后端童鞋确认交付的接口的response header设置了 以及返回了文件流. 第二步:修改axios请求的responseType为blob,以post请求为例: axios({ method: 'post', url: 'api/user/', data: { firstName: 'Fred', lastName: 'Flintstone' }, responseType: 'blob' }).then(response =>

  • Vue自定义toast组件的实例代码

    写了两三天,终于把toast组件写出来了.不敢说是最好的设计,希望有更好思路的朋友可以在评论区给我意见!_(:з」∠)_ 第一步:写toast.vue,将样式之类的先定下来 <template> <div v-show="showToast" class="toast" :class="position"> <div class="toast_container" v-if="type=

  • vue 地图可视化 maptalks 篇实例代码详解

    Maptalks 项目是一个 HTML5 的地图引擎, 基于原生 ES6 Javascript 开发: - 二三维一体化地图, 通过二维地图的旋转 /倾斜增加三维视角 - 插件化设计, 能与其他图形库结合, 开发各种二三维效果, 例如 echarts/d3/THREE 等 - 很认真的优化了绘制性能 - 很重视测试, 有接近 1.5K 个单元测试用例, 所以稳定性还不错, 已经应用在很多大大小小的系统上了 上面是一段 maptalks 官方介绍,下面来创建工程.首先利用 vue-cli3 搭建一

  • vue的toast弹窗组件实例详解

    相信普通的vue组件大家都会写, 定义 -> 引入 -> 注册 -> 使用 ,行云流水,一气呵成,但是如果我们今天是要自定义一个弹窗组件呢? 首先,我们来分析一下弹窗组件的特性(需求): 0. 轻量 --一个组件小于 1Kib (实际打包完不到0.8k) 1.一般都是多处使用 --需要解决每个页面重复引用+注册 1.一般都是跟js交互的 --无需 在 <template> 里面写 <toast :show="true" text="弹窗消息

  • Vue.extend实现挂载到实例上的方法

    本文实例讲述了Vue.extend实现挂载到实例上的方法.分享给大家供大家参考,具体如下: 这里主要是做个笔记 根据官网的说法,Vue.extend:是使用基础 Vue 构造器,创建一个"子类".参数是一个包含组件选项的对象. 官网的用法是: <div id="mount-point"></div> // 创建构造器 var Profile = Vue.extend({ template: '<p>{{firstName}} {{

  • 使用vue重构资讯页面的实例代码解析

    从我接手到将这个页面代码重构前,一直都还是使用angular1的代码去做的,需求来了也是用angular去实现:作为一个憧憬新技术的前端,怎么忍受得了现在还在使用这么有历史感的框架,所以,以前就一直在酝酿着如何将angular重构成vue. 代码结构设计 这个资讯项目代码整体都是使用angular.js来去实现的,而此次想重构的资讯详情页面只是其中的一个页面,所以新建了一个文件夹 /newApp .作为以后新技术的文件夹,以后使用vue技术的都放在这个文件夹下,区别于原先文件夹 /app . 在

  • Spring AOP的实现原理详解及实例

    Spring AOP的实现原理详解及实例 spring 实现AOP是依赖JDK动态代理和CGLIB代理实现的. 以下是JDK动态代理和CGLIB代理简单介绍 JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理. CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类.CGLIB是高效的代码生成包,底层是依靠ASM(开源的Java字节码编辑类库)操作字节码实现的,性能比JDK强. 在Spring中

  • 基于vue.js路由参数的实例讲解——简单易懂

    vue中,我们构建单页面应用时候,一定必不可少用到vue-router vue-router 就是我们的路由,这个由vue官方提供的插件 首先在我们项目中安装vue-router路由依赖 第一种,我们提供命令行来安装 npm install vue-router --save 第二种,我们直接去官方github下载 https://github.com/vuejs/vue-router 路由参数设置 1,实例化一个路由,然后路由映射表中的地址带参数,这个参数就是路由的参数 接着给映射表中的路由设

随机推荐