vue作为现在前端程序员必不可少的技术栈,大部分人可能都式仅仅学会了怎么调用、怎么调接口、怎么传参、怎么构造限时界面,反之其中的核心底层原理三大部分:reactive/complier/runtime
确没有花什么心思学习,那么这样显然是不利于我们程序员钻研技术的,因为没有根的技术总是像浮萍一般,不会造轮子的程序员不是一个有理想的程序员,
1响应式函数
直接进入正题,以下是vue2实现响应式原理的代码,可以看到
class Depend {
constructor() {
this.reactiveFns = new Set()
}
addDepend(fn) {
if (fn) {
this.reactiveFns.add(fn)
}
}
depend() {
if (reactiveFn) {
this.reactiveFns.add(reactiveFn)
}
}
notify() {
this.reactiveFns.forEach(fn => {
fn()
})
}
}
// 设置一个专门执行响应式函数的一个函数
let reactiveFn = null
function watchFn(fn) {
reactiveFn = fn
fn()
reactiveFn = null
}
// 封装一个函数: 负责通过obj的key获取对应的Depend对象
const objMap = new WeakMap()
function getDepend(obj, key) {
// 1.根据对象obj, 找到对应的map对象
let map = objMap.get(obj)
if (!map) {
map = new Map()
objMap.set(obj, map)
}
// 2.根据key, 找到对应的depend对象
let dep = map.get(key)
if (!dep) {
dep = new Depend()
map.set(key, dep)
}
return dep
}
// 方案一: Object.defineProperty() -> Vue2
function reactive(obj) {
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
set: function(newValue) {
value = newValue
const dep = getDepend(obj, key)
dep.notify()
},
get: function() {
// 拿到obj -> key
// console.log("get函数中:", obj, key)
// 找到对应的obj对象的key对应的dep对象
const dep = getDepend(obj, key)
// dep.addDepend(reactiveFn)
dep.depend()
return value
}
})
})
return obj
}
// 方式二: new Proxy() -> Vue3
// ========================= 业务代码 ========================
const obj = reactive({
name: "why",
age: 18,
address: "广州市"
})
watchFn(function() {
console.log(obj.name)
console.log(obj.age)
console.log(obj.age)
})
// 修改name
console.log("--------------")
// obj.name = "kobe"
obj.age = 20
// obj.address = "上海市"
console.log("=============== user =================")
const user = reactive({
nickname: "abc",
level: 100
})
watchFn(function() {
console.log("nickname:", user.nickname)
console.log("level:", user.level)
})
user.nickname = "cba"
定义了一个Depend类,包含构造器、加入depend函数、执行响应式、通知执行四大部分,之后有设置了一个专门执行响应式函数的函数,把当前函数模仿响应式原理立即执行一次,
2.获取响应式map结构
如果map里有这个obj对应的存储的响应的depend对象,就让他直接执行,不然创建一个map结构
随后,vue2中利用defineProperty设置set和get函数,对每个属性,遍历一次,调用dep的notity方法,执行一次所有的fn
之后,在get中获取getDepend返回的dep对象,调用depend方法,
把函数加入到reactiveFns的set中,然后返回value,也就是取到的value,即那个属性值本身
3.测试
测测每个功能的执行情况
4.vue2difineProperty原理?有什么不足?为什么?
class Depend {
constructor() {
this.reactiveFns = new Set()
}
addDepend(fn) {
if (fn) {
this.reactiveFns.add(fn)
}
}
depend() {
if (reactiveFn) {
this.reactiveFns.add(reactiveFn)
}
}
notify() {
this.reactiveFns.forEach(fn => {
fn()
})
}
}
// 设置一个专门执行响应式函数的一个函数
let reactiveFn = null
function watchFn(fn) {
reactiveFn = fn
fn()
reactiveFn = null
}
// 封装一个函数: 负责通过obj的key获取对应的Depend对象
const objMap = new WeakMap()
function getDepend(obj, key) {
// 1.根据对象obj, 找到对应的map对象
let map = objMap.get(obj)
if (!map) {
map = new Map()
objMap.set(obj, map)
}
// 2.根据key, 找到对应的depend对象
let dep = map.get(key)
if (!dep) {
dep = new Depend()
map.set(key, dep)
}
return dep
}
// 方案一: Object.defineProperty() -> Vue2
function reactive(obj) {
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
set: function(newValue) {
value = newValue
const dep = getDepend(obj, key)
dep.notify()
},
get: function() {
// 拿到obj -> key
// console.log("get函数中:", obj, key)
// 找到对应的obj对象的key对应的dep对象
const dep = getDepend(obj, key)
// dep.addDepend(reactiveFn)
dep.depend()
return value
}
})
})
return obj
}
// 方式二: new Proxy() -> Vue3
// ========================= 业务代码 ========================
const obj = reactive({
name: "why",
age: 18,
address: "广州市"
})
watchFn(function() {
console.log(obj.name)
console.log(obj.age)
console.log(obj.age)
})
// 修改name
console.log("--------------")
// obj.name = "kobe"
obj.age = 20
// obj.address = "上海市"
console.log("=============== user =================")
const user = reactive({
nickname: "abc",
level: 100
})
watchFn(function() {
console.log("nickname:", user.nickname)
console.log("level:", user.level)
})
user.nickname = "cba"
直接看代码:
遍历+手动notify函数:
无法监测到对象的增删属性和过深不利于性能优化