vue的核心思想?
数据驱动
什么是数据驱动?
数据驱动指的是视图是由数据驱动生成,我们对视图的修改不会直接操作DOM,而是修改数据。
模版和数据怎么渲染成DOM的呢?
1. new Vue发生了什么
Vue实际是一个类,只能通过new关键字初始化(在JavaScript中类是通过function实现的)
可以看到,new Vue会调用_init()
方法,并且将参数options传给这个方法。
合并参数,把参数options赋值给vue实例的$options
在初始化的最后,检测到如果有el属性,则调用vm.$mount
方法挂载vm(vue实例),挂载的目的就是把模版渲染成DOM。
2. new Vue主要执行一些初始化的操作,Vue初始化主要干了几件事?
合并参数、初始化生命周期、初始化事件、初始化渲染、初始化data、props、computed、watcher等
3. 为什么在data中定义数据message可以直接通过this.message访问到?
在初始化中有initState(vm)
初始化状态,找到initState方法
initState方法主要是实现props,methods,data,watch,computed的初始化
如果data存在,会初始化data,看看初始化data的方法initData(vm)
初始化data做了哪些事?
- 先从vue实例vm的$options中取到data
let data = vm.$options.data
-
将data赋值给vm._data,
判断data是不是一个function,如果是函数就调用getData
方法,将data()返回的值赋值给data变量,如果不是function就不处理data -
判断最终的data是不是一个对象,如果不是对象就作出警告——data必须是一个对象
-
获取data中的key,和props进行对比(就是说如果我在props中定义了一个id,就不能在data中定义id),不能重复的原因是因为不管是data还是props他们的属性最终都会挂载到vue实例上,通过this.id可以访问到
-
最终会执行
首先理解Object.defineProperty()用法
Object.defineProperty()
的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性
Object.defineProperty(obj, prop, desc)
这个方法接受3个参数:
- obj需要定义属性的当前对象(在这里就是传入的vue实例对象vm)
- prop需要定义或修改的属性名,(这里就是传入的props或者data的key)
- desc 属性描述符 是一个对象更(这里指的是sharePropertyDefinition对象)
在这里定义了描述对象的属性描述符:
enumerable: true
(可枚举,就是是否出现在for…in或Object.keys中) configurable:true
(是否配置,能否删除)
sourceKey是传入的参数_data或者_prop字符串
上面的target是传入的vue实例对象,通过set和get的方法写入或读取数据,
从上面可以看出访问data上的id通过this.id
实际上是访问了this._data.id
或this._props.id
4. Vue实例挂载的实现
以下是Runtime+compiler版本的$mount方法实现
$mount方法
这个方法在platform目录下的多个目录下文件中都有定义,因为这个方法的实现是和平台和构建方式都相关的。
- 首先缓存了Vue原型上的$mount方法,然后再重新定义该方法
为什么是重新定义呢?
因为在runtime/index.js中已经定义了$mount
的方法,需要重新定义是因为Runtime Only版本没有$mount方法中的逻辑
- Runtime Only版本的$mount方法
这里的query方法就是一个原生JavaScript API获取DOM的方法 - Runtime Compiler版本的$mount方法
首先将el转换为DOM对象,然后对el做了限制,Vue不能挂载在body、html这样的根节点上
然后判断开发人员有没有定义render方法,判断有没有写template
如果template存在,再判断template的类型,重新赋值template,判断最后的template,编译生成render函数
说到底就是看有没有render函数,因为vue最终只认render函数,如果有render函数就直接调用render函数,如果没有就将模版编译生成render函数
最后
最后调用调用缓存的mount方法(即走到了runtime/index.js里面)
最后执行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方法,只是最后一个参数传得不同:
vm._c
方法是模版被编译成render方法时调用的vm.$createElement
方法是用户手写render函数时候调用的
总的来说:render方法最终是通过执行createElement方法并返回虚拟VNode(虚拟Node)