数据驱动
数据驱动指的是视图是由数据驱动生成,我们对视图的修改不会直接操作DOM,而是修改数据。
Vue实际是一个类,只能通过new关键字初始化(在JavaScript中类是通过function实现的)
可以看到,new Vue会调用_init()
方法,并且将参数options传给这个方法。
合并参数,把参数options赋值给vue实例的$options
在初始化的最后,检测到如果有el属性,则调用vm.$mount
方法挂载vm(vue实例),挂载的目的就是把模版渲染成DOM。
合并参数、初始化生命周期、初始化事件、初始化渲染、初始化data、props、computed、watcher等
在初始化中有initState(vm)
初始化状态,找到initState方法
initState方法主要是实现props,methods,data,watch,computed的初始化
如果data存在,会初始化data,看看初始化data的方法initData(vm)
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个参数:
在这里定义了描述对象的属性描述符:
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
以下是Runtime+compiler版本的$mount方法实现
$mount方法
这个方法在platform目录下的多个目录下文件中都有定义,因为这个方法的实现是和平台和构建方式都相关的。
因为在runtime/index.js中已经定义了$mount
的方法,需要重新定义是因为Runtime Only版本没有$mount方法中的逻辑
最后执行mountComponent
方法,这个方法做的事:如果没有写render函数,首先会将vm.$options.render赋值成一个空Node对象
vm.$options.render=createEmptyNode
, 然后会尝试错误提示,就是如果你使用的是Runtime Only版本却没有写render函数却写了template警告,或者即没有写render函数又没有写template警告。
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)