vue源码学习——数据驱动

vue的核心思想?

数据驱动

什么是数据驱动?

数据驱动指的是视图是由数据驱动生成,我们对视图的修改不会直接操作DOM,而是修改数据。

模版和数据怎么渲染成DOM的呢?

1. new Vue发生了什么

Vue实际是一个类,只能通过new关键字初始化(在JavaScript中类是通过function实现的)
vue源码学习——数据驱动_第1张图片
可以看到,new Vue会调用_init()方法,并且将参数options传给这个方法。
vue源码学习——数据驱动_第2张图片
合并参数,把参数options赋值给vue实例的$options
vue源码学习——数据驱动_第3张图片
在初始化的最后,检测到如果有el属性,则调用vm.$mount方法挂载vm(vue实例),挂载的目的就是把模版渲染成DOM。

2. new Vue主要执行一些初始化的操作,Vue初始化主要干了几件事?

合并参数、初始化生命周期、初始化事件、初始化渲染、初始化data、props、computed、watcher等
vue源码学习——数据驱动_第4张图片

3. 为什么在data中定义数据message可以直接通过this.message访问到?

在初始化中有initState(vm)初始化状态,找到initState方法
vue源码学习——数据驱动_第5张图片
initState方法主要是实现props,methods,data,watch,computed的初始化
如果data存在,会初始化data,看看初始化data的方法initData(vm)
vue源码学习——数据驱动_第6张图片

初始化data做了哪些事?

  1. 先从vue实例vm的$options中取到data

let data = vm.$options.data

  1. 将data赋值给vm._data,
    判断data是不是一个function,如果是函数就调用getData方法,将data()返回的值赋值给data变量,如果不是function就不处理data

  2. 判断最终的data是不是一个对象,如果不是对象就作出警告——data必须是一个对象

  3. 获取data中的key,和props进行对比(就是说如果我在props中定义了一个id,就不能在data中定义id),不能重复的原因是因为不管是data还是props他们的属性最终都会挂载到vue实例上,通过this.id可以访问到

  4. 最终会执行

在这里插入图片描述
在这里插入图片描述
vue源码学习——数据驱动_第7张图片
首先理解Object.defineProperty()用法
Object.defineProperty()的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性
Object.defineProperty(obj, prop, desc)
这个方法接受3个参数:

  1. obj需要定义属性的当前对象(在这里就是传入的vue实例对象vm)
  2. prop需要定义或修改的属性名,(这里就是传入的props或者data的key)
  3. desc 属性描述符 是一个对象更(这里指的是sharePropertyDefinition对象)

在这里定义了描述对象的属性描述符:
enumerable: true(可枚举,就是是否出现在for…in或Object.keys中) configurable:true(是否配置,能否删除)
在这里插入图片描述
sourceKey是传入的参数_data或者_prop字符串
在这里插入图片描述
在这里插入图片描述
上面的target是传入的vue实例对象,通过set和get的方法写入或读取数据,
从上面可以看出访问data上的id通过this.id实际上是访问了this._data.idthis._props.id

4. Vue实例挂载的实现

以下是Runtime+compiler版本的$mount方法实现

$mount方法
这个方法在platform目录下的多个目录下文件中都有定义,因为这个方法的实现是和平台和构建方式都相关的。
在这里插入图片描述
在这里插入图片描述

  1. 首先缓存了Vue原型上的$mount方法,然后再重新定义该方法
    为什么是重新定义呢?
    vue源码学习——数据驱动_第8张图片

因为在runtime/index.js中已经定义了$mount的方法,需要重新定义是因为Runtime Only版本没有$mount方法中的逻辑

  1. Runtime Only版本的$mount方法
    vue源码学习——数据驱动_第9张图片
    这里的query方法就是一个原生JavaScript API获取DOM的方法
  2. Runtime Compiler版本的$mount方法
    vue源码学习——数据驱动_第10张图片
    首先将el转换为DOM对象,然后对el做了限制,Vue不能挂载在body、html这样的根节点上
    vue源码学习——数据驱动_第11张图片
    然后判断开发人员有没有定义render方法,判断有没有写template
    vue源码学习——数据驱动_第12张图片
    如果template存在,再判断template的类型,重新赋值template,判断最后的template,编译生成render函数
    说到底就是看有没有render函数,因为vue最终只认render函数,如果有render函数就直接调用render函数,如果没有就将模版编译生成render函数
    最后
    在这里插入图片描述
    最后调用调用缓存的mount方法(即走到了runtime/index.js里面)
    vue源码学习——数据驱动_第13张图片

最后执行mountComponent方法,这个方法做的事:如果没有写render函数,首先会将vm.$options.render赋值成一个空Node对象

vm.$options.render=createEmptyNode, 然后会尝试错误提示,就是如果你使用的是Runtime Only版本却没有写render函数却写了template警告,或者即没有写render函数又没有写template警告。

5. render方法做了哪些事情

Vue的_render方法是实例的一个私有方法,用来把实例渲染成一个虚拟Node(_render方法定义在src/core/instance/render.js中)

我们平时在开发过程中手写render函数的场景比较少,写得比较多的是template模版,template模版会被编译成render方法。

render函数的第一个参数是createElement(createElement是一个函数vm.$createElement
vm.$createElement
在这里插入图片描述

createElement方法是在src/core/vdom/create-element下定义的
在执行initRender方法时候会调用createElement方法
可以看到除了又一个vm.$createElement方法外还定义了vm._c方法,那这两个方法有什么区别,他们内部都调用了createElement方法,只是最后一个参数传得不同:

  1. vm._c方法是模版被编译成render方法时调用的
  2. vm.$createElement方法是用户手写render函数时候调用的

总的来说:render方法最终是通过执行createElement方法并返回虚拟VNode(虚拟Node)

6. Virtual DOM(虚拟DOM)

你可能感兴趣的:(vue)