vue2中的双向绑定是怎么实现的
vue2的双向绑定主要是基于object.defineProperty来实现的,利用get、set方法进行拦截。 具体的实现是:
利用obeserver方法遍历对象,对对象的属性进行defineReactive方法
利用defineReactive方法对属性的set、get方法进行重写
function observer(data) {
if(typeof data !== 'obj' || data === null) {
return data
}
for(let key in data) {
defineReactive(data, key, data[key])
}
}
function defineRective(target, key, val){
observer(val)
Object.defineProperty(target, key, {
get(){
return val
}
set(newVal){
if(newVal !== val) {
val = newVal;
console.log('视图更新')
}
}
})
}
缺陷:
- 对于新增的属性,由于不会被vue编译,所以不会被添加get、set,所以不会被监听
- 对于删除的属性,同样不会刷新
解决:
- 新增属性时使用Vue.$set
- 删除属性时使用Vue.$delete
缺陷:
- 使用数组索引修改数组不会得到响应式
- 直接使用数组的方法不会的得到响应式
解决:
- 新增属性时使用Vue.$set
vue2关于数组 对象的数据劫持做了什么处理吗?
//即将要被劫持的数组
let arr = [1,2,3];
//先把要劫持的方法列出来
let methods = [
'push',
'pop',
'shift',
'unshift',
'reverse',
'sort',
'splice'
]
//既然要劫持原型,就要先把原型拿过来
let arrayProto = Array.prototype;
//创建一个我们自己的原型方法
let arrayMethods = Object.create(Array.prototype);
//遍历方法
methods.forEach(method=>{
//给每个方法做切片
arrayMethods[method] = function (...args){
//还是先调用真正的原型方法获得正常的返回值
const result = arrayProto[method].apply(this,args)
//模拟插入切片
console.log(`调用了${method}方法`)
return result
}
})
arr.__proto__ = arrayMethods
console.log(arr.push(4))
console.log(arr.splice(0,1))
vue3中的双向绑定是怎么实现的
vue3是基于proxy对对象实现的数据劫持,具体的实现如下:
function vue() {
this.$data = {
testData: 1
}
this.el = docunment.getElementById('app')
this.virtualDom = ''
this.observer(this.$data)
this.render()
}
vue.prototype.observer = function(data) {
let self = this;
this.$data = new Proxy(this.data, {
get: function(target, key, vceiver) {
return target[key]
},
set: function(target, key, value, vceiver) {
target[key] = value
slef.render()
}
})
}
vue.prototype.render = functin() {
this.virtualDom = 'a test vue' + this.$data.testData
this.el.innerHtml = this.virtualDom
}
object.definePerproty 跟 proxy的区别
- 丢掉麻烦的备份数据
- 省去for in 循环
- 可以监听数组变化
- 代码更简化
vue中的数据频繁变化,为什么只会更新一次?
- 检测到数据变化
- 开启一个队列
- 在同一事件循环中缓冲所有数据改变
- 如果同一个
watcher (watcherId相同)被多次触发,只会被推入到队列中一次
vue的computed跟watch有什么区别
- 计算属性是从一个或者几个属性中派生计算出新的属性,最常见的是设置一个函数,返回计算之后的结果。computed跟watch的区别是是否有缓存,computed具有缓存,如果依赖不改变,则不会进行重新计算,watch是进行监听,并执行副作用函数,watch没有返回值,但是可以执行异步等操作。
- 计算属性主要是用于简化模板里的复杂计算,模板中出现很多臃肿的复杂逻辑,会使得页面难以维护,侦听器主要是用于监听场景变化之后进行一些异步的操作。判断场景需要选择watch还是computed的标准,主要是看是否需要派生出新值,能用computed则用computed
- 计算属性可以传递对象,成为可以读写的计算属性,watch可以传递对象,设置
deep、immediate等选项。当我们需要深度监听对象中的属性时,可以打开deep:true选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话可以使用字符串形式监听,如果没有写到组件中,不要忘记使用unWatch手动注销哦。 - vue3中的watch出现了一些变化,不再能监听点操作符之后的表达式,reactivity API中新出现了
watch、watchEffect可以完全替代目前的watch选项,且功能更加强大。
computed的原理是什么
computed的实现是基于Watcher对象的,众所周知,vue 是基于 Object.defineProperty 实现监听的。在 vue 初始化数据 data 和 computed 数据过程中。会涉及到以下几个对象
-
Observe对象(观察者) -
Dep对象(订阅者) -
Watch对象Observe对象是在 data 执行响应式时候调用,因为 computed 属性基于响应式属性,所以其不需要创建 Observe 对象。 Dep 对象主要功能是做依赖收集,有个属性维护多个 Watch 对象,当更新时候循环调用每个 Watch 执行更新。 Watch 对象主要是用于更新,而且是收集的重点对象
vue 在创建 computed 属性时候,会循环所有计算属性,每一个计算属性会创建一个 watch,并且在通过 defineProperty 定义监听,在 get 中,计算属性工作是做依赖收集,在 set 中,计算属性重要工作是重新执行计算方法,这里需要多补充一句,因为 computed 是懒执行,也就是说第一次初始化之后,便不会执行计算,下一次变更执行重新计算是在 set 中。
另一个补充点是依赖收集的时机,computed 收集时机和 data 一样,是在组件挂载前,但是其收集对象是自己属性对应的 watch,而 data 本身所有数据对应一个 watch