vue 原理(数据劫持和渲染)
如何实现双向绑定
1. Vue是采用数据劫持结合发布/订阅模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
- 什么是setter、getter
- 对象有两种属性:(1)数据属性,就是我们经常使用的属性(2)访问器属性,也称存取器属性(存取器属性就是一组获取和设置值的函数)
- get和set就是关键字 它们后面各自对应一个函数,这个函数就是上面红字部分所讲的,**存储器属性**
2. 什么是Object.defineProperty() ?
- 对象是由多个名/值对组成的无序的集合。对象中每个属性对应任意类型的值
在vue中v-model,v-name,{{}}等都可以对数据进行展示,也就是说假如一个属性都通过这三个指令了,那么每当这个属性改变的时候,相应的这个三个指令的html视图也必须改变;
于是vue中就是每当有这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者(addSub),其订阅者只是更新自己的指令对应的数据,也就是v-model='name'和{{name}}有两个对应的订阅者,各自管理自己的地方;
每当属性的set方法触发,就循环更新Dep中的订阅者(notify);
首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;然后在编译的时候在该属性的数组dep中添加订阅者,v-model会添加一个订阅者,{{}}也会,v-bind也会,只要用到该属性的指令理论上都会; 接着为input会添加监听事件,修改值就等于为该属性赋值,则会触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。
Virtual Dom和Diff算法
1. 什么是虚拟DOM?
- 用js来模拟DOM中的节点。传说中的虚拟DOM。
2. Virtual Dom和真实Dom的区别
- 虚拟DOM不会进行排版与重绘操作 虚拟DOM就是把真实DOM转换为Javascript代码
- 虚拟DOM进行频繁修改,然后一次性**比较并修改真实DOM中需要改的部分**,**最后并在真实DOM中进行排版与重绘**,减少过多DOM节点排版与重绘损耗
- 真实DOM频繁排版与重绘的效率是相当低的
3. Diff算法
- diff的过程就是调用名为 `patch` 的函数,比较新旧节点,一边比较一边给 **真实的DOM** 打补丁。
Vue的生命周期
- beforeCreate( 创建前 )
- 在实例初始化之后,数据观测和事件配置之前被调用,此时组件的选项对象还未创建,el 和 data 并未初始化,因此无法访问methods, data, computed等上的方法和数据
- created ( 创建后 )
- 数据观测、属性和方法的运算,watch/event事件回调,完成了data 数据的初始化,el没有。
- beforeMount(挂载前)
- 编译模板,把data里面的数据和模板生成html,完成了el和data 初始化,注意此时还没有挂在html到页面上。
- mounted(挂载后)
- 挂在完成,也就是模板中的HTML渲染到HTML页面中,此时一般可以做一些ajax操作,mounted只会执行一次
- beforeUpdate(更新前)
- 在数据更新之前被调用,发生在虚拟DOM重新渲染和打补丁之前,可以在该钩子中进一步地更改状态,不会触发附加地重渲染过程
- updated(更新后)
- 在由于数据更改导致地虚拟DOM重新渲染和打补丁只会调用,调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作,然后在大多是情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环,该钩子在服务器端渲染期间不被调用
- beforeDestroy(销毁前)
- 在实例销毁之前调用,实例仍然完全可用,这一步还可以用this来获取实例, 一般在这一步做一些重置的操作,比如清除掉组件中的定时器 和 监听的dom事件
- destroyed(销毁后)
- 在实例销毁之后调用,调用后,所以的事件监听器会被移出,所有的子实例也会被销毁,该钩子在服务器端渲染期间不被调用
methods、watch、computed之间的差别对比以及适用场景
一、computer
-
当页面中有某些数据依赖其他数据进行变动的时候,可以使用计算属性。需要注意的是,就算在data中没有直接声明出要计算的变量,也可以直接在computed中写入。
-
计算属性默认只有getter,可以在需要的时候自己设定setter
-
需要注意的是,computed是具有缓存的,这就意味着只要计算属性的依赖没有进行相应的数据更新,那么computed会直接从缓存中获取值,多次访问都会返回之前的计算结果 适用场景:
二、watch
- watch和computed很相似,watch用于观察和监听页面上的vue实例,当然在大部分情况下我们都会使用computed,但如果要在数据变化的同时进行异步操作或者是比较大的开销,那么watch为最佳选择
- watch为一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。
- 如果在data中没有相应的属性的话,是不能watch的,这点和computed不一样。 适用场景:
三、methods
- 跟前面的都不一样,我们通常在这里面写入方法,只要调用就会重新执行一次,相应的有一些触发条件,在某些时候methods和computed看不出来具体的差别,但是一旦在运算量比较复杂的页面中,就会体现出不一样。
总结:
在computed和watch方面,一个是计算,一个是观察,在语义上是有区别的。
计算是通过变量计算来得出数据。而观察是观察一个特定的值,根据被观察者的变动进行相应的变化,在特定的场景下不能相互混用,所以还是需要注意api运用的合理性和语义性。