【Vue源码探究二】从 $mount 讲起,一起探究Vue的渲染机制

mount, 意思为挂载。可以理解为将vue实例(逻辑应用),挂靠在某个dom元素(载体)上的一个过程。

一、创建Vue实例时的渲染过程

上一篇文章我们讲到, 在创建一个vue实例的时候(var vm = new Vue(options))。Vue的构造函数将自动运行 this._init(启动函数)。启动函数的最后一步为initRender(vm),

// Vue.prototype._init
    ...
    initLifecycle(vm);
    initEvents(vm);
    callHook(vm, 'beforeCreate');
    initState(vm);
    callHook(vm, 'created');
    initRender(vm);

initRender中调用vm.$mount(vm.$options.el),将实例挂载到dom上,至此启动函数完成。

//initRender
  ...
  if (vm.$options.el) {
    vm.$mount(vm.$options.el);
  }

可以看出,vm.$mount为vue渲染的主要函数

二、Vue的渲染机制

clipboard.png

上图,展示的是独立构建时的一个渲染流程图

模板字符串

//模板字符串
<div id = "app">
  {{message}}
</div>

render函数

//render函数
function anonymous() {
with(this){return _h('div',{attrs:{"id":"app"}},["\n  "+_s(message)+"\n"])}
}

vnode

clipboard.png

真实dom节点($el)

clipboard.png

独立构建 与 运行时构建

我们先看一下官方文档 独立构建和运行时构建

clipboard.png

这两个概念,我在初学的时候是一头雾水。现在对照着渲染的流程图,我们可以知道

独立构建:包含模板编译器
渲染过程: html字符串 → render函数 → vnode → 真实dom节点

运行时构建: 不包含模板编译器
渲染过程: render函数 → vnode → 真实dom节点

运行时构建通过砍掉模板编译器,让整个包少了30%(官方数据)。我在阅读源码的过程中,发现vue源码7000余行,而和模板编译相关的代码,则约有1000行左右。看起来确实是轻便了。这是在鼓励我们多用render函数吗?

三、$mount函数

上面我们说到,运行时构建的包,会比独立构建少一个模板编译器。在$mount函数上也不同

运行时构建的 $mount函数

clipboard.png

而独立构建的 $mount函数,会先用一个临时变量mount保存上面的$mount方法

var mount = Vue$2.prototype.$mount;  //此处mount即为运行时版的 $mount

然后重写$mount函数,这时,调用$mount就会包括模板编译功能了

var mount = Vue$2.prototype.$mount;
Vue$2.prototype.$mount = function (el, hydrating) {
  ...省略代码(里面为模板编译器入口)...
  return mount.call(this, el, hydrating)
};

我们可以看到,不管独立构建还是运行时构建,都会调用 vm._mount方法我们来看看源码

Vue.prototype._mount = function(el, hydrating) {
    ...一些防止运行时的包,却用了template的报错代码...


    callHook(vm, 'beforeMount');

    vm._watcher = new Watcher(vm, function () {
      vm._update(vm._render(), hydrating);
    }, noop);
    
    hydrating = false;

    if (vm.$vnode == null) {
      vm._isMounted = true;
      callHook(vm, 'mounted');
    }
    return vm    
    
}

使用过的vue的人,都会很敏锐地发现, 在调用beforeMount生命周期,和mounted生命周期中间的关键代码为

clipboard.png

鉴于大牛已经讲过很多次这里的数据监听了,我们只讲其中渲染部分

vm._update(vm._render(), hydrating);

vm._render函数返回一个vnode作为 vm._update的参数。 hydrating是与服务器渲染(SSR)相关的,浏览器端可以不用管。

vm._render (将render函数转化成vnode)

最核心代码为

var render = vm.$options.render
try{
  vnode = render.call(vm._renderProxy, vm.$createElement);
}catch{
  ...
}

此处,使用call方法, 将this指向 vm.renderProxy js功底差的同学要去补补知识了。
vm.renderProxy是个代理,代理vm,主要用来报错,如果render函数上使用了vm上没有的属性或方法,就会报错。
vm.$createElement 这个是创建vnode的方法,作为第一个参数传入。

clipboard.png

render函数
这里的h即是, vm.$createElement ,便是在vm._render这个阶段被传入。

vm._update (将vnode生成真实dom节点)

最关键一句话为

 vm.$el = vm.__patch__(prevVnode, vnode);

vm.__patch__也是个大家伙,我之后会再去研究。
里面的方法,将新旧vnode使用 diff算法进行比对,找出要替换的地方,这样更新dom的性能会有较大优化。
最后会返回一个dom节点。
这个时候将vm.$el 赋值为这个dom节点,挂载完成!


肉丸的unity学习笔记
unity/laya/3dmax学习

学习unity、laya、3dmax中

1.9k 声望
193 粉丝
0 条评论
推荐阅读
unity 运动相关学习记录
unity部分文章为学习记录,理解不是很深刻,主要用于自我学习总结,如果有纰漏请大佬轻喷。 一、通过position属性实现物体的运动 1、update循环中调用Vector3.movetowards,直接设置物体的position 输入的参数 终...

曾广营2阅读 3k

正则表达式实例
收集在业务中经常使用的正则表达式实例,方便以后进行查找,减少工作量。常用正则表达式实例1. 校验基本日期格式 {代码...} {代码...} 2. 校验密码强度密码的强度必须是包含大小写字母和数字的组合,不能使用特殊...

寒青57阅读 8.6k评论 11

JavaScript有用的代码片段和trick
平时工作过程中可以用到的实用代码集棉。判断对象否为空 {代码...} 浮点数取整 {代码...} 注意:前三种方法只适用于32个位整数,对于负数的处理上和Math.floor是不同的。 {代码...} 生成6位数字验证码 {代码...} ...

jenemy49阅读 7.3k评论 12

再也不学AJAX了!(二)使用AJAX ① XMLHttpRequest
「再也不学 AJAX 了」是一个以 AJAX 为主题的系列文章,希望读者通过阅读本系列文章,能够对 AJAX 技术有更加深入的认识和理解,从此能够再也不用专门学习 AJAX。本篇文章为该系列的第二篇,最近更新于 2023 年 1...

libinfs42阅读 6.9k评论 12

封面图
「多图预警」完美实现一个@功能
一天产品大大向 boss 汇报完研发成果和产品业绩产出,若有所思的走出来,劲直向我走过来,嘴角微微上扬。产品大大:boss 对我们的研发成果挺满意的,balabala...(内心 OS:不听,讲重点)产品大大:咱们的客服 I...

wuwhs32阅读 3.5k评论 5

封面图
安全地在前后端之间传输数据 - 「3」真的安全吗?
在「2」注册和登录示例中,我们通过非对称加密算法实现了浏览器和 Web 服务器之间的安全传输。看起来一切都很美好,但是危险就在哪里,有些人发现了,有些人嗅到了,更多人却浑然不知。就像是给门上了把好锁,还...

边城29阅读 6.4k评论 5

封面图
2022大前端总结和2023就业分析
我在年前给掘金平台分享了《2022年热点技术盘点》的前端热点,算是系统性的梳理了一下我自己对前端一整年的总结。年后,在知乎上看到《前端的就业行情怎么样?》,下面都是各种唱衰前端的论调,什么裁员,外包化...

i5ting27阅读 2.3k评论 4

封面图

学习unity、laya、3dmax中

1.9k 声望
193 粉丝
宣传栏