学习了coderwhy的JavaScript高级语法视频课的笔记
如有错误或者不合适的地方,敬请见谅,欢迎指出和拓展,谢谢各位了
一、Vue3响应式原理的实现
- 下面的步骤是一步步推导,逐步实现出来的过程。
第一步:响应式函数的封装
// 响应式函数的封装
const reactiveFns = []
function watchFn(fn) {
reactiveFns.push(fn)
}
const obj = {
name: 'xwl',
age: 18
}
// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
console.log('obj.name的值:' + obj.name)
})
watchFn(function () {
console.log('obj.name2的值:' + obj.name)
})
obj.name = 'xxx'
reactiveFns.forEach(item => {
item()
})
复制代码
第二步:依赖收集类的封装
第一步用数组不方便管理,所以用一个class类来管理。
class Depend {
constructor() {
this.reactiveFns = []
}
addDepend(fn) {
this.reactiveFns.push(fn)
}
notify() {
this.reactiveFns.forEach(item => {
item()
})
}
}
// 响应式函数的封装
const depend = new Depend()
function watchFn(fn) {
depend.addDepend(fn)
}
const obj = {
name: 'xwl',
age: 18
}
// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
console.log('obj.name的值:' + obj.name)
})
watchFn(function () {
console.log('obj.name2的值:' + obj.name)
})
obj.name = 'xxx'
depend.notify()
复制代码
第三步:自动监听对象变化
使用Proxy和Reflect监听对象中的变化。
class Depend {
constructor() {
this.reactiveFns = []
}
addDepend(fn) {
this.reactiveFns.push(fn)
}
notify() {
this.reactiveFns.forEach(item => {
item()
})
}
}
// 响应式函数的封装
const depend = new Depend()
function watchFn(fn) {
depend.addDepend(fn)
}
const obj = {
name: 'xwl',
age: 18
}
const objProxy = new Proxy(obj, {
get(target, key, receiver) {
return Reflect.get(target, key, receiver)
},
set(target, key, newValue, receiver) {
Reflect.set(target, key, newValue, receiver)
depend.notify()
}
})
// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
console.log('obj.name的值:' + objProxy.name)
})
watchFn(function () {
console.log('obj.name2的值:' + objProxy.name)
})
watchFn(function () {
console.log('obj.age的值:' + objProxy.age)
})
objProxy.name = 'xxx'
objProxy.age = 20
复制代码
第四步:依赖收集的管理
这就需要用到Map和WeakMap的使用。 第三步问题就是执行 objProxy.age = 20 时,全部响应式函数都执行一遍,没区分name和age对应的响应式函数。
class Depend {
constructor() {
this.reactiveFns = []
}
addDepend(fn) {
this.reactiveFns.push(fn)
}
notify() {
this.reactiveFns.forEach(item => {
item()
})
}
}
// 响应式函数的封装
const depend = new Depend()
function watchFn(fn) {
depend.addDepend(fn)
}
// 获取depend函数的封装
const targetMap = new WeakMap()
function getDepend(target, key) {
let map = targetMap.get(target)
if (!map) {
map = new Map()
targetMap.set(target, map)
}
let depend = map.get(key)
if (!depend) {
depend = new Depend()
map.set(key, depend)
}
return depend
}
const obj = {
name: 'xwl',
age: 18
}
const objProxy = new Proxy(obj, {
get(target, key, receiver) {
return Reflect.get(target, key, receiver)
},
set(target, key, newValue, receiver) {
Reflect.set(target, key, newValue, receiver)
const depend = getDepend(target, key)
depend.notify()
}
})
// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
console.log('obj.name的值:' + objProxy.name)
})
watchFn(function () {
console.log('obj.name2的值:' + objProxy.name)
})
watchFn(function () {
console.log('obj.age的值:' + objProxy.age)
})
objProxy.name = 'xxx'
objProxy.age = 20
复制代码
第五步:正确的收集依赖
class Depend {
constructor() {
this.reactiveFns = []
}
addDepend(fn) {
this.reactiveFns.push(fn)
}
notify() {
this.reactiveFns.forEach(item => {
item()
})
}
}
// 响应式函数的封装
let activeReactiveFn = null //用于给depend.addDepend(activeReactiveFn)传递参数
function watchFn(fn) {
activeReactiveFn = fn
fn()
activeReactiveFn = null
}
// 获取depend函数的封装
const targetMap = new WeakMap()
function getDepend(target, key) {
let map = targetMap.get(target)
if (!map) {
map = new Map()
targetMap.set(target, map)
}
let depend = map.get(key)
if (!depend) {
depend = new Depend()
map.set(key, depend)
}
return depend
}
const obj = {
name: 'xwl',
age: 18
}
const objProxy = new Proxy(obj, {
get(target, key, receiver) {
const depend = getDepend(target, key)
depend.addDepend(activeReactiveFn)
return Reflect.get(target, key, receiver)
},
set(target, key, newValue, receiver) {
Reflect.set(target, key, newValue, receiver)
const depend = getDepend(target, key)
depend.notify()
}
})
// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
console.log('obj.name的值:' + objProxy.name)
})
watchFn(function () {
console.log('obj.name2的值:' + objProxy.name)
})
watchFn(function () {
console.log('obj.age的值:' + objProxy.age)
})
objProxy.name = 'xxx'
objProxy.age = 20
复制代码
第六步:对Depend类重构
这一步的修改有两点:
- depend.addDepend(activeReactiveFn)改为depend.depend(),定义一个全局变量activeReactiveFn解决传递函数问题;
- 普通数组 [ ] 改为new Set(),去除重复。
let activeReactiveFn = null
class Depend {
constructor() {
//
this.reactiveFns = new Set()
}
// addDepend(fn) {
// this.reactiveFns.push(fn)
// }
depend() {
if (activeReactiveFn) {
this.reactiveFns.add(activeReactiveFn)
}
}
notify() {
this.reactiveFns.forEach(item => {
item()
})
}
}
// 响应式函数的封装
function watchFn(fn) {
activeReactiveFn = fn
fn()
activeReactiveFn = null
}
// 获取depend函数的封装
const targetMap = new WeakMap()
function getDepend(target, key) {
let map = targetMap.get(target)
if (!map) {
map = new Map()
targetMap.set(target, map)
}
let depend = map.get(key)
if (!depend) {
depend = new Depend()
map.set(key, depend)
}
return depend
}
const obj = {
name: 'xwl',
age: 18
}
const objProxy = new Proxy(obj, {
get(target, key, receiver) {
const depend = getDepend(target, key)
// depend.addDepend(activeReactiveFn)
depend.depend()
return Reflect.get(target, key, receiver)
},
set(target, key, newValue, receiver) {
Reflect.set(target, key, newValue, receiver)
const depend = getDepend(target, key)
depend.notify()
}
})
// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
// 这里两次使用objProxy.name,就会在数组中添加重复的响应式函数,所以上面用new Set()
console.log('obj.name的值:' + objProxy.name)
console.log('obj.name的值111:' + objProxy.name)
})
watchFn(function () {
console.log('obj.name2的值:' + objProxy.name)
})
watchFn(function () {
console.log('obj.age的值:' + objProxy.age)
})
objProxy.name = 'xxx'
objProxy.age = 20
复制代码
第七步(最后一步):对象的响应式操作vue3
let activeReactiveFn = null
class Depend {
constructor() {
//
this.reactiveFns = new Set()
}
// addDepend(fn) {
// this.reactiveFns.push(fn)
// }
depend() {
if (activeReactiveFn) {
this.reactiveFns.add(activeReactiveFn)
}
}
notify() {
this.reactiveFns.forEach(item => {
item()
})
}
}
// 响应式函数的封装
function watchFn(fn) {
activeReactiveFn = fn
fn()
activeReactiveFn = null
}
// 获取depend函数的封装
const targetMap = new WeakMap()
function getDepend(target, key) {
let map = targetMap.get(target)
if (!map) {
map = new Map()
targetMap.set(target, map)
}
let depend = map.get(key)
if (!depend) {
depend = new Depend()
map.set(key, depend)
}
return depend
}
// 第一种方式
// const obj = {
// name: 'xwl',
// age: 18
// }
// const objProxy = reactive(obj)
// 第二种方式:简写
const objProxy = reactive({
name: 'xwl',
age: 18
})
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
const depend = getDepend(target, key)
// depend.addDepend(activeReactiveFn)
depend.depend()
return Reflect.get(target, key, receiver)
},
set(target, key, newValue, receiver) {
Reflect.set(target, key, newValue, receiver)
const depend = getDepend(target, key)
depend.notify()
}
})
}
// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
// 这里两次使用objProxy.name,就会在数组中添加重复的响应式函数,所以上面用new Set()
console.log('obj.name的值:' + objProxy.name)
console.log('obj.name的值111:' + objProxy.name)
})
watchFn(function () {
console.log('obj.name2的值:' + objProxy.name)
})
watchFn(function () {
console.log('obj.age的值:' + objProxy.age)
})
objProxy.name = 'xxx'
objProxy.age = 20
// ---------------------------------------------------
const infoProxy = reactive({
address: 'gd'
})
watchFn(function () {
console.log('-----info.address的值:' + infoProxy.address)
})
infoProxy.address = 'bj'
复制代码
二、Vue2响应式原理的实现
vue2和vue3实现的逻辑是差不多的,vue2是用Object.defineProperty(),vue3使用Proxy和Reflect。
let activeReactiveFn = null
class Depend {
constructor() {
//
this.reactiveFns = new Set()
}
// addDepend(fn) {
// this.reactiveFns.push(fn)
// }
depend() {
if (activeReactiveFn) {
this.reactiveFns.add(activeReactiveFn)
}
}
notify() {
this.reactiveFns.forEach(item => {
item()
})
}
}
// 响应式函数的封装
function watchFn(fn) {
activeReactiveFn = fn
fn()
activeReactiveFn = null
}
// 获取depend函数的封装
const targetMap = new WeakMap()
function getDepend(target, key) {
let map = targetMap.get(target)
if (!map) {
map = new Map()
targetMap.set(target, map)
}
let depend = map.get(key)
if (!depend) {
depend = new Depend()
map.set(key, depend)
}
return depend
}
// 第一种方式
// const obj = {
// name: 'xwl',
// age: 18
// }
// const objProxy = reactive(obj)
// 第二种方式:简写
const objProxy = reactive({
name: 'xwl',
age: 18
})
function reactive(obj) {
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj, key, {
get: function () {
const depend = getDepend(obj, key)
depend.depend()
return value
},
set: function (newValue) {
value = newValue
const depend = getDepend(obj, key)
depend.notify()
}
})
})
return obj
}
// watchFn()封装,区分开和普通函数的区别
watchFn(function () {
// 这里两次使用objProxy.name,就会在数组中添加重复的响应式函数,所以上面用new Set()
console.log('obj.name的值:' + objProxy.name)
console.log('obj.name的值111:' + objProxy.name)
})
watchFn(function () {
console.log('obj.name2的值:' + objProxy.name)
})
watchFn(function () {
console.log('obj.age的值:' + objProxy.age)
})
objProxy.name = 'xxx'
objProxy.age = 20
// ---------------------------------------------------
const infoProxy = reactive({
address: 'gd'
})
watchFn(function () {
console.log('-----info.address的值:' + infoProxy.address)
})
infoProxy.address = 'bj'
复制代码