vue的双向绑定原理
Object.defineProperty(a,b,c) 方法
const obj = {
username: '第嘉',
userage: 18
}
当我要修改 obj 的属性时执行一些操作
需要通过 Object.defineProperty 给对象 添加/更新 属性,
或者 使用 Object.defineProperty 重写对象
Object.defineProperty(原对象,属性名,对象(属性名的属性描述))
属性的描述分两大类
- 存取描述符 configurable enumerable get set
- 数据描述符 configurable enumerable value writable
configurable:true/false 是否可以用其他描述符描述和是否可以删除该属性,默认false
writable:true/false 是否可以重写属性
enumberable 该属性是否可以被枚举,对象遍历时是否可以遍历到该属性,默认true
/ Object.defineProperty(obj, 'username', {
configurable: false,//不可以用其他描述
enumerable: false,//不能被遍历,一般都写成true
writable: true,//可以重写
value: '杨露'//修改的值
})
Object.defineProperty(obj, 'userage', {
configurable: true,
enumerable: true,
//get 的返回值用于设置属性值的,
get() {
return 10
},
//set 函数用于监听属性值的变化,属性值改变就会触发
set(value) {
//value 表示改变之后的值
console.log('发生改变', value)
//更新age相关的视图
}
})
对象属性变视图变
const data = {
num: 10,
post: {
title: '成功之路',
comments: [
{
id: 1,
text: '666'
},
{
id: 2,
text: '牛逼'
},
]
},
tabs: ['good', 'share']
}
对象更新视图函数,data内属性变就会触发
function render() {
console.log('要更新视图')
}
想让data变视图变,首先重新描述对象
let num = data.num
Object.defineProperty(data, 'num', {
configurable: true,
enumerable: true,
get() {
return num
},
set(value) {
num = value
render()
}
})
data.title = 'Vue'
这时候就会触发render()函数
封装对象属性添加描述函数
function definieReactive(obj, propotypeName, value) {
Object.defineProperty(obj, propotypeName, {
configurable: true,
enumerable: true,
get() {
return value
// console.log(111)
},
set(val) {
//相同的值不做视图更新
if (val !== value) {
value = val
render()
}
}
})
}
找到data内所有属性,并且添加描述函数
function observe(obj) {
if (obj !== null) {//首先判断obj不为空,因为 typeof null 检测出来是 object
if (typeof obj === 'object') {//判断是不是对象或数组
if (Array.isArray(obj)) {//如果是数组,数组内是有可能是对象,这些对象的属性也需要添加描述,所以遍历数组再执行observe,
for (let key in obj) {
observe(obj[key])
}
} else {//在这里拿到的就是纯纯对象Object
Object.keys(obj)//Object.keys()获取对象属性名返回一个由对象名组成的数组
.forEach(key => {//对返回的数组进行遍历,拿到各个属性名添加描述
definieReactive(obj, key, obj[key])
observe(obj[key])//子集有可能是个对象,调用函数进行判断
})
}
}
}
}
observe(data)
数组改变引起视图变化
数组的修改方法 push() reverse() shift() unshift() sort() splic() pop()
如何重写数组的方法 原型链
Array.prototype.push =新方法
const arr = [1, 2, 3, 4, 5, 6]
const oldarrPrototype = Array.prototype//先把老方法存起来
const methods = ['push', 'reverse', 'shift', 'unshift', 'sort', 'splic', 'pop']
// /拷贝一个原型对象 Array.protype 下的属性不可枚举不能遍历
// 我们重写这些方法并不是将所有的数组下的这些方法重写了,只有我们的数据是数组的话里面的数组方法才能被修改
//所以我们的做法是创建一个新的原型对象,里面包含了数组内所偶的方法,以及修改之后的方法
const proto = Object.create(oldarrPrototype)
methods.forEach(ele => {
proto[ele] = function () {
// 1.执行原来的方法
// 2.更新视图
// console.log(111)
oldarrPrototype[ele].apply(this, [...arguments])
render()
}
})
arr.__proto__ = proto
document.querySelector('.a').onclick = function () {
arr.push(7)
console.log(arr) //1,2,3,4,5,6,7
}
Vue里面一些数组或者对象的操作不会导致视图的变化
原因是因为 Vue 的响应式原理处理不了这些变化
vue 提供了 $set用于对象的新增属性 $delete用于对象的删除属性
对于数组来说, 可以直接赋值