VUE响应式原理
一、vue2的数据响应式原理
1. 什么是defineProperty
其实是定义对象的属性,并不是核心的微一个对象做数据双向绑定,而是去给对象做属性标签,只不过属性里的get和set实现了响应式。
属性名 | 默认值 | 说明 |
---|---|---|
value | Undefined | 值 |
Get | Undefined | 取值 |
Set | Undefined | 设置值 |
Writable | False | 可写 |
Enumerable | False | 可遍历 |
Configurable | False | 可枚举 |
defineProperty的使用
let ob = {a: 123, b: 456}
let _value = ob.a // 需要额外变量去存储值
Object.defineProperty(ob, "a", {
writable: false,
enumerable: false,
configurable: false,
get: () => {
console.log("you get a number")
return _value
},
set: (val) => {
console.log("you set a number")
_value = val
}
})
console.log(Object.getOwnPropertyDescriptor(ob, "a"))
ob.a = 111;
console.log(ob.a) // 123
for (let item in ob) {
console.log(item) // 只能打印出 b 属性
}
2. 双向绑定实现
2.1vue中从改变一个数据到发生改变的过程
数据改变出发set --> set部分出发notify --> 更改对应的虚拟dom --> 更新render
初始化 --> get部分手机依赖
2.2 代码实现
function vue() {
this.$data = {a: 1}; // 写死了,不是传参,简化
this.el = document.getElementById('app');
this.virtualdom = ''; // 虚拟dom
this.observe(this.$set) // 将数据进行get/set处理
this.render(); // 初始化页面
}
vue.prototype.observe = function(obj) {
let value, self = this;
for (let item in obj) {
value = obj[item]
if (typeof value === 'object') {
this.observe(value)
} else {
Object.defineProperty(ob, item, {
get: function() {
// 依赖收集:收集这个属性,在哪几个组件使用
return value
},
set: function(newvalue) {
// 触发更新
value = newvalue
selt.render()
}
})
}
}
}
vue.prototype.render = function() {
this.virtualdom = "i am" + this.$data.a
this.el.innerHTML = this.virtualdmo
}
数组更新
let arrayPro = Array.protorype
let arrayob = Object.create(arrayPro)
let arr = ['push', 'pop', 'shift', 'unshift'] // 触发数组更新的方法
arr.forEach((method, index) => {
arrayob[method] = function() {
let ret = arrayPro[method].apply(this, arguments)
dep.notify() // 触发更新
return ret;
}
})
// 最后将数组的prototype修改一下
二、vue3的数据响应式原理
1. 什么是proxy
proxy对象用于定义基本操作的自定义行为,和defineProperty类型功能几乎一样,只不过用法上有些不同。
2. 代码实现
let ob = {a: 1}
let obproxy = new Proxy(ob, {
get: function(target, key, receiver) {
return target[key] // 可通过此方式直接取值,而不是还要需要中间的一个变量
},
set: function(target, key, value, receiver) {
// target[key] = value
return Reflect.set(target, key, value)
},
})
defineProperty:是去改变原对象的,只能监听某个属性,不能对全对象监听(还要递归判断属性是否是对象)
proxy:是去新建一个代理对象,并不会改变原对象;可以省去for in提升效率,可以监听数组,不用再去单独的对数组做特异性操作。
function vue() {
this.$data = {a: 1}; // 写死了,不是传参,简化
this.el = document.getElementById('app');
this.virtualdom = ''; // 虚拟dom
this.observe() // 将数据进行get/set处理
this.render(); // 初始化页面
}
vue.prototype.observe = function() {
let self = this
this.$data = new Proxy(this.$data, {
get: function(target, key, receiver) {
return target[key] // 可通过此方式直接取值,而不是还要需要中间的一个变量
},
set: function(target, key, value, receiver) {
target[key] = value
selt.render()
},
})
}
vue.prototype.render = function() {
this.virtualdom = "i am" + this.$data.a
this.el.innerHTML = this.virtualdmo
}
3. 扩展:proxy还能做什么
3.1 类型校验
// 策略模式:新建一个策略对象,存放校验规则
let validtor = {
name: function(value) {
let reg = /^[\u4e00-\u9fa5]+$/;
if (typeof value === 'string' && reg.test(value)) {
return false
}
return true
}
}
function person(name, age) {
this.age= age
this.name = name
return new Proxy(this, {
get: function(target, key) {},
set: function(target, key, value) {
if (validtor[key](value)) {
return Reflect.set(target, key, value)
} else {
throw new Error(key + 'is not right')
}
}
})
}
3.2 真正的私有变量
例如:this.$route不能进行更改,比如设置defineProperty只定义get不定义set,就无法进行set从而实现;
三、diff算法和virtual dom
1. 虚拟DOM
用js去虚拟的描述dom元素,例如:
<template>
<div class="demo">
<p>skdjfskdf</p>
</div>
</template>
let ob = {
el: "div",
props: {class: "demo"},
text: "",
children: [{
el: 'p',
props: {},
text: 'skdjfskdf',
children: []
}]
}
2. diff算法
计算虚拟dom是否有更新
patchVnode(oldVnode, vnode) {
const el = vnode.el = oldVnode.el;
let i, oldCh = oldVnode.children, ch = vnode.children;
// 如果没有更新,则直接return
if (oldVnode === vnode) return;
// 如果是文字内容更新,则调用更新内容的方法
if (oldVnode.text !== null && vnode.text !== null && oldVnode.text !== vnode.text) {
api.setTextContent(el, vnode.text)
} else {
// 如果是子节点更新,调用方法更新dom节点
updateEle(el, vnode, oldVnode)
if (oldCh && ch && oldCh !== ch) {
updateChildren() // 节点都存在,则表示子节点有变动
} else if (ch) {
createEle(vnode) // 如果新子节点存在,旧不存在,则创建
} else {
api.removeChildren(el) // 如果旧存在,新不存在,则删除
}
}
}
3. vue性能优化
比较尖端的操作:ssr,app混合reata-native
给项目构建工具链流程(初始化-测试-规范-构建,如vue-cli),给项目构建有一套基础设施(项目的工具库,组件库,插件库)
各种的优化实践
源码:axios,vue-router,redux