23
头图

Preface

Hello everyone, I’m Lin Sanxin, and everyone knows that this rookie usually writes a lot of basic articles, and I always believe in two sentences.

  • Use the most easy-to-understand words to talk about the most difficult knowledge points
  • foundation is the premise of advanced
    In fact, Vue3 has been out for a long time, and it may be used by most companies. However, how is Vue3 better than Vue2? In fact, many people don't know. Today I will first you about the responsive principle of Vue3. By the way, how is the responsiveness of 161d64d2af2075 Vue3 better than that of Vue2.

What's the good thing?

Okay, let's first talk about why Vue3 is better than that of Vue2. Maybe I usually ask you: do you know how Vue's responsiveness is implemented? Everyone can roughly answer

  • The responsive style of Object.defineProperty
  • The responsive style of Proxy is implemented based on ES6's 061d64d2af2131

Yes, although the answer above is a bit abstract, it does answer the core principles of the responsiveness of the two versions of Vue, and the responsiveness of the two versions of Vue is indeed reflected in Object.defineProperty and Proxy The difference.

Vue2

Everyone knows that the response of Object.defineProperty is based on 061d64d2af21c1, then I will take Object.defineProperty as an example

// 响应式函数
function reactive(obj, key, value) {
  Object.defineProperty(data, key, {
    get() {
      console.log(`访问了${key}属性`)
      return value
    },
    set(val) {
      console.log(`将${key}由->${value}->设置成->${val}`)
      if (value !== val) {
        value = val
      }
    }
  })
}


const data = {
  name: '林三心',
  age: 22
}
Object.keys(data).forEach(key => reactive(data, key, data[key]))
console.log(data.name)
// 访问了name属性
// 林三心
data.name = 'sunshine_lin' // 将name由->林三心->设置成->sunshine_lin
console.log(data.name)
// 访问了name属性
// sunshine_lin

Through the above example, I think everyone has Object.defineProperty , then the problem is coming? What are the disadvantages of it? This makes it especially abandoned in Vue3, let's continue to look at:

// 接着上面代码

data.hobby = '打篮球'
console.log(data.hobby) // 打篮球
data.hobby = '打游戏'
console.log(data.hobby) // 打游戏

Now everyone can see what is wrong with Object.defineProperty As we can see, data has added the hobby attribute for access and setting, but it will not trigger get and set, so the drawback is: Object.defineProperty only monitors the attributes in the initial object, but for the new attributes invalid. This is why the modification of the new properties of the object in Vue2 needs to use Vue.$set to set the value.

Vue3

From the above, we know the Object.defineProperty Proxy is the core of the responsive principle in Vue3, compensates for this defect. As usual, let’s take an example (first roughly, the specific parameters will be discussed in detail below):

const data = {
  name: '林三心',
  age: 22
}

function reactive(target) {
  const handler = {
    get(target, key, receiver) {
      console.log(`访问了${key}属性`)
      return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
      console.log(`将${key}由->${target[key]}->设置成->${value}`)
      Reflect.set(target, key, value, receiver)
    }
  }

  return new Proxy(target, handler)
}

const proxyData = reactive(data)

console.log(proxyData.name)
// 访问了name属性
// 林三心
proxyData.name = 'sunshine_lin'
// 将name由->林三心->设置成->sunshine_lin
console.log(proxyData.name)
// 访问了name属性
// sunshine_lin

As you can see, the effect is actually the same as the above Object.defineProperty , so why do you want to abandon it and choose Proxy ? Attention, the most important thing is to add new properties to the object, let's see the effect:

proxyData.hobby = '打篮球'
console.log(proxyData.hobby)
// 访问了hobby属性
// 打篮球
proxyData.hobby = '打游戏'
// 将hobby由->打篮球->设置成->打游戏
console.log(proxyData.hobby)
// 访问了hobby属性
// 打游戏

So now everyone knows how responsive Vue3 is better than Vue2, right?

截屏2021-08-26 下午8.48.43.png

Vue3 responsive principle

After talking Proxy the benefits of 061d64d2af239e, let's formally talk about the core part of Vue3's responsive principle.

Preface

First look at the following code

let name = '林三心', age = 22, money = 20
let myself = `${name}今年${age}岁,存款${money}元`

console.log(myself) // 林三心今年22岁,存款20元

money = 300

// 预期:林三心今年22岁,存款300元
console.log(myself) // 实际:林三心今年22岁,存款20元

Think about it, everyone. I want myself follow money . What should I do? Hey, in fact, as long as myself ='${name} is ${age} years old this year, deposit ${money}元', just execute it again, as follows

let name = '林三心', age = 22, money = 20
let myself = `${name}今年${age}岁,存款${money}元`

console.log(myself) // 林三心今年22岁,存款20元

money = 300

myself = `${name}今年${age}岁,存款${money}元` // 再执行一次

// 预期:林三心今年22岁,存款300元
console.log(myself) // 实际:林三心今年22岁,存款300元

effect

As mentioned above, every time money changed, 161d64d2af2506 has to be executed again. myself ='${name} is ${age} years old this year, deposit ${money} yuan', in order to myself , in fact, this is not elegant, we can encapsulate A effect function

let name = '林三心', age = 22, money = 20
let myself = ''
const effect = () => myself = `${name}今年${age}岁,存款${money}元`

effect() // 先执行一次
console.log(myself) // 林三心今年22岁,存款20元
money = 300

effect() // 再执行一次

console.log(myself) // 林三心今年22岁,存款300元

In fact, this is also harmful. If you don’t believe me, you can take a look at the following situation

let name = '林三心', age = 22, money = 20
let myself = '', ohtherMyself = ''
const effect1 = () => myself = `${name}今年${age}岁,存款${money}元`
const effect2 = () => ohtherMyself = `${age}岁的${name}居然有${money}元`

effect1() // 先执行一次
effect2() // 先执行一次
console.log(myself) // 林三心今年22岁,存款20元
console.log(ohtherMyself) // 22岁的林三心居然有20元
money = 300

effect1() // 再执行一次
effect2() // 再执行一次

console.log(myself) // 林三心今年22岁,存款300元
console.log(ohtherMyself) // 22岁的林三心居然有300元

If you add a ohtherMyself , you have to write another effect , and then execute it every time you update. If the number of additions increases, wouldn't it be necessary to write a lot of effect function execution code every time?

track and trigger

In view of the above problem, we can solve it like this: use the track function to collect all the effect functions that depend on the money variable dep in dep Why does Set use 061d64d2af25f4? Because Set can automatically remove duplicates. After the collection, whenever the money variable changes in the trigger function to notify all the effect functions in dep that depend on the money variable to execute to implement the update of the dependent variable. Let's take a look at the code first, and then I will show you through a picture, I am afraid that everyone is dizzy haha.

let name = '林三心', age = 22, money = 20
let myself = '', ohtherMyself = ''
const effect1 = () => myself = `${name}今年${age}岁,存款${money}元`
const effect2 = () => ohtherMyself = `${age}岁的${name}居然有${money}元`

const dep = new Set()
function track () {
    dep.add(effect1)
    dep.add(effect2)
}
function trigger() {
    dep.forEach(effect => effect())
}
track() //收集依赖
effect1() // 先执行一次
effect2() // 先执行一次
console.log(myself) // 林三心今年22岁,存款20元
console.log(ohtherMyself) // 22岁的林三心居然有20元
money = 300

trigger() // 通知变量myself和otherMyself进行更新

console.log(myself) // 林三心今年22岁,存款300元
console.log(ohtherMyself) // 22岁的林三心居然有300元

截屏2021-08-26 下午10.43.12.png

What about the object?

The above are all about basic data types. Let’s talk about the object. Let me give an example first, and use the most primitive way to achieve his response.

const person = { name: '林三心', age: 22 }
let nameStr1 = ''
let nameStr2 = ''
let ageStr1 = ''
let ageStr2 = ''

const effectNameStr1 = () => { nameStr1 = `${person.name}是个大菜鸟` }
const effectNameStr2 = () => { nameStr2 = `${person.name}是个小天才` }
const effectAgeStr1 = () => { ageStr1 = `${person.age}岁已经算很老了` }
const effectAgeStr2 = () => { ageStr2 = `${person.age}岁还算很年轻啊` }

effectNameStr1()
effectNameStr2()
effectAgeStr1()
effectAgeStr2()
console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// 林三心是个大菜鸟 林三心是个小天才 22岁已经算很老了 22岁还算很年轻啊

person.name = 'sunshine_lin'
person.age = 18

effectNameStr1()
effectNameStr2()
effectAgeStr1()
effectAgeStr2()

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// sunshine_lin是个大菜鸟 sunshine_lin是个小天才 18岁已经算很老了 18岁还算很年轻啊

We have also seen the above code, and feel that it is very brainless. . Remember the dep collection effect ? Let's take the name and age in the person object as two variables for the time being, they all have their own dependent variables

  • name: nameStr1 and nameStr2
  • age: ageStr1 and ageStr2
    So name and age should have their own dep effect corresponding to each dependent variable

截屏2021-08-26 下午11.11.04.png

dep earlier, 061d64d2af2827 uses Set . Since the person has two attributes of age and name, it has two deps . So what should be used to store these two deps? We can use another ES6 data structure Map to store

const person = { name: '林三心', age: 22 }
let nameStr1 = ''
let nameStr2 = ''
let ageStr1 = ''
let ageStr2 = ''

const effectNameStr1 = () => { nameStr1 = `${person.name}是个大菜鸟` }
const effectNameStr2 = () => { nameStr2 = `${person.name}是个小天才` }
const effectAgeStr1 = () => { ageStr1 = `${person.age}岁已经算很老了` }
const effectAgeStr2 = () => { ageStr2 = `${person.age}岁还算很年轻啊` }

const depsMap = new Map()
function track(key) {
    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }
    // 这里先暂且写死
    if (key === 'name') {
        dep.add(effectNameStr1)
        dep.add(effectNameStr2)
    } else {
        dep.add(effectAgeStr1)
        dep.add(effectAgeStr2)
    }
}
function trigger (key) {
    const dep = depsMap.get(key)
    if (dep) {
        dep.forEach(effect => effect())
    }
}

track('name') // 收集person.name的依赖
track('age') // 收集person.age的依赖



effectNameStr1()
effectNameStr2()
effectAgeStr1()
effectAgeStr2()
console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// 林三心是个大菜鸟 林三心是个小天才 22岁已经算很老了 22岁还算很年轻啊

person.name = 'sunshine_lin'
person.age = 18

trigger('name') // 通知person.name的依赖变量更新
trigger('age') // 通知person.age的依赖变量更新

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// sunshine_lin是个大菜鸟 sunshine_lin是个小天才 18岁已经算很老了 18岁还算很年轻啊

Above we have only one person object, what if there are multiple objects? what to do? We all know that each object will create a Map dep of the attributes in the object (using Set to store). If there are multiple objects, what should be used to store the Map corresponding to each object? Please see the picture below

截屏2021-08-27 下午8.01.20.png

In fact, ES6 also has a new data structure called WeakMap . Let's use it to store the Map these objects. So we have to modify the track function and the trigger function, let’s see what they look like before.

const depsMap = new Map()
function track(key) {
    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }
    // 这里先暂且写死
    if (key === 'name') {
        dep.add(effectNameStr1)
        dep.add(effectNameStr2)
    } else {
        dep.add(effectAgeStr1)
        dep.add(effectAgeStr2)
    }
}
function trigger (key) {
    const dep = depsMap.get(key)
    if (dep) {
        dep.forEach(effect => effect())
    }
}

The previous code only did a single object processing solution, but now if you want multiple objects, you have to use WeakMap for transformation (the code may be a bit verbose, but it will all take care of students with weak foundations)

const person = { name: '林三心', age: 22 }
const animal = { type: 'dog', height: 50 }

const targetMap = new WeakMap()
function track(target, key) {
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        targetMap.set(target, depsMap = new Map())
    }

    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }
    // 这里先暂且写死
    if (target === person) {
        if (key === 'name') {
            dep.add(effectNameStr1)
            dep.add(effectNameStr2)
        } else {
            dep.add(effectAgeStr1)
            dep.add(effectAgeStr2)
        }
    } else if (target === animal) {
        if (key === 'type') {
            dep.add(effectTypeStr1)
            dep.add(effectTypeStr2)
        } else {
            dep.add(effectHeightStr1)
            dep.add(effectHeightStr2)
        }
    }
}

function trigger(target, key) {
    let depsMap = targetMap.get(target)
    if (depsMap) {
        const dep = depsMap.get(key)
        if (dep) {
            dep.forEach(effect => effect())
        }
    }
}

After the above transformation, we finally realized the multi-object dependency collection, let's give it a try

const person = { name: '林三心', age: 22 }
const animal = { type: 'dog', height: 50 }
let nameStr1 = ''
let nameStr2 = ''
let ageStr1 = ''
let ageStr2 = ''
let typeStr1 = ''
let typeStr2 = ''
let heightStr1 = ''
let heightStr2 = ''

const effectNameStr1 = () => { nameStr1 = `${person.name}是个大菜鸟` }
const effectNameStr2 = () => { nameStr2 = `${person.name}是个小天才` }
const effectAgeStr1 = () => { ageStr1 = `${person.age}岁已经算很老了` }
const effectAgeStr2 = () => { ageStr2 = `${person.age}岁还算很年轻啊` }
const effectTypeStr1 = () => { typeStr1 = `${animal.type}是个大菜鸟` }
const effectTypeStr2 = () => { typeStr2 = `${animal.type}是个小天才` }
const effectHeightStr1 = () => { heightStr1 = `${animal.height}已经算很高了` }
const effectHeightStr2 = () => { heightStr2 = `${animal.height}还算很矮啊` }

track(person, 'name') // 收集person.name的依赖
track(person, 'age') // 收集person.age的依赖
track(animal, 'type') // animal.type的依赖
track(animal, 'height') // 收集animal.height的依赖



effectNameStr1()
effectNameStr2()
effectAgeStr1()
effectAgeStr2()
effectTypeStr1()
effectTypeStr2()
effectHeightStr1()
effectHeightStr2()

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// 林三心是个大菜鸟 林三心是个小天才 22岁已经算很老了 22岁还算很年轻啊

console.log(typeStr1, typeStr2, heightStr1, heightStr2)
// dog是个大菜鸟 dog是个小天才 50已经算很高了 50还算很矮啊

person.name = 'sunshine_lin'
person.age = 18
animal.type = '猫'
animal.height = 20

trigger(person, 'name')
trigger(person, 'age')
trigger(animal, 'type')
trigger(animal, 'height')

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// sunshine_lin是个大菜鸟 sunshine_lin是个小天才 18岁已经算很老了 18岁还算很年轻啊

console.log(typeStr1, typeStr2, heightStr1, heightStr2)
// 猫是个大菜鸟 猫是个小天才 20已经算很高了 20还算很矮啊

Proxy

Through the above learning, we can realize that when the data is updated, his dependent variables are also changed, but there are still shortcomings. You can find that each time we always have to manually execute the track function to perform dependent collection, and When the data changes, we have to manually execute the trigger function to update the notification

So, is there a way to achieve automatic collection of dependencies and automatic notification of updates? The answer is yes, Proxy can solve this problem for us. Let’s first write a reactive function. Let’s take a look and understand Proxy-track-trigger . Later I will talk about why Proxy needs to be matched with Reflect

function reactive(target) {
    const handler = {
        get(target, key, receiver) {
            track(receiver, key) // 访问时收集依赖
            return Reflect.get(target, key, receiver)
        },
        set(target, key, value, receiver) {
            Reflect.set(target, key, value, receiver)
            trigger(receiver, key) // 设值时自动通知更新
        }
    }

    return new Proxy(target, handler)
}

Then change the previous code, remove manual track and manual trigger, and found that the previous effect can also be achieved

const person = reactive({ name: '林三心', age: 22 }) // 传入reactive
const animal = reactive({ type: 'dog', height: 50 }) // 传入reactive

effectNameStr1()
effectNameStr2()
effectAgeStr1()
effectAgeStr2()
effectTypeStr1()
effectTypeStr2()
effectHeightStr1()
effectHeightStr2()

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// 林三心是个大菜鸟 林三心是个小天才 22岁已经算很老了 22岁还算很年轻啊

console.log(typeStr1, typeStr2, heightStr1, heightStr2)
// dog是个大菜鸟 dog是个小天才 50已经算很高了 50还算很矮啊

person.name = 'sunshine_lin'
person.age = 18
animal.type = '猫'
animal.height = 20

console.log(nameStr1, nameStr2, ageStr1, ageStr2)
// sunshine_lin是个大菜鸟 sunshine_lin是个小天才 18岁已经算很老了 18岁还算很年轻啊

console.log(typeStr1, typeStr2, heightStr1, heightStr2)
// 猫是个大菜鸟 猫是个小天才 20已经算很高了 20还算很矮啊

Some students may be a little bit confused, a little confused about the above code, or a little bit circumstantial, I thought that through a picture to explain the process to everyone, the picture may be compressed, it is recommended to open it and have a look

截屏2021-08-27 下午9.13.55.png

Solve the hard-coded problem

, do you still remember, it is in the track function 161d64d2af2a7a

function track(target, key) {
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        targetMap.set(target, depsMap = new Map())
    }

    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }
    // 这里先暂且写死
    if (target === person) {
        if (key === 'name') {
            dep.add(effectNameStr1)
            dep.add(effectNameStr2)
        } else {
            dep.add(effectAgeStr1)
            dep.add(effectAgeStr2)
        }
    } else if (target === animal) {
        if (key === 'type') {
            dep.add(effectTypeStr1)
            dep.add(effectTypeStr2)
        } else {
            dep.add(effectHeightStr1)
            dep.add(effectHeightStr2)
        }
    }
}

In actual development, there must be more than two objects. If you add an additional object, you must add another else if judgment, which is absolutely impossible. So how do we solve this problem? In fact, it is not difficult to say. The authors of Vue3 came up with a very clever way, using a global variable activeEffect to cleverly solve this problem. How to solve it? In fact, it is very simple, that is, as as each 161d64d2af2ada effect function is executed, it puts itself in the corresponding 061d64d2af2ad6, which eliminates the need dep

截屏2021-08-27 下午9.31.37.png

How can we achieve this function? We need to modify the effect function, and modify the track function

let activeEffect = null
function effect(fn) {
    activeEffect = fn
    activeEffect()
    activeEffect = null // 执行后立马变成null
}
function track(target, key) {
    // 如果此时activeEffect为null则不执行下面
    // 这里判断是为了避免例如console.log(person.name)而触发track
    if (!activeEffect) return
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        targetMap.set(target, depsMap = new Map())
    }

    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }
    dep.add(activeEffect) // 把此时的activeEffect添加进去
}

// 每个effect函数改成这么执行
effect(effectNameStr1)
effect(effectNameStr2)
effect(effectAgeStr1)
effect(effectAgeStr2)
effect(effectTypeStr1)
effect(effectTypeStr2)
effect(effectHeightStr1)
effect(effectHeightStr2)

截屏2021-08-27 下午9.49.44.png

Implement ref

This is how we use ref

let num = ref(5)

console.log(num.value) // 5

Then num will become a responsive data, and num , you need to write num.value to use it.

Realizing ref is actually very simple. We have already implemented reactive above, and we only need to do this to achieve ref

function ref (initValue) {
    return reactive({
        value: initValue
    })
}

Let's try it out

let num = ref(5)

effect(() => sum = num.value * 100)

console.log(sum) // 500

num.value = 10

console.log(sum) // 1000

Achieve computed

Let's simply implement computed way, it's actually very simple

function computed(fn) {
    const result = ref()
    effect(() => result.value = fn()) // 执行computed传入函数
    return result
}

Let's see the result

let num1 = ref(5)
let num2 = ref(8)
let sum1 = computed(() => num1.value * num2.value)
let sum2 = computed(() => sum1.value * 10)

console.log(sum1.value) // 40
console.log(sum2.value) // 400

num1.value = 10

console.log(sum1.value) // 80
console.log(sum2.value) // 800

num2.value = 16

console.log(sum1.value) // 160
console.log(sum2.value) // 1600

Since then we have implemented all the functions of this article

Final code

const targetMap = new WeakMap()
function track(target, key) {
    // 如果此时activeEffect为null则不执行下面
    // 这里判断是为了避免例如console.log(person.name)而触发track
    if (!activeEffect) return
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        targetMap.set(target, depsMap = new Map())
    }

    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }
    dep.add(activeEffect) // 把此时的activeEffect添加进去
}
function trigger(target, key) {
    let depsMap = targetMap.get(target)
    if (depsMap) {
        const dep = depsMap.get(key)
        if (dep) {
            dep.forEach(effect => effect())
        }
    }
}
function reactive(target) {
    const handler = {
        get(target, key, receiver) {
            track(receiver, key) // 访问时收集依赖
            return Reflect.get(target, key, receiver)
        },
        set(target, key, value, receiver) {
            Reflect.set(target, key, value, receiver)
            trigger(receiver, key) // 设值时自动通知更新
        }
    }

    return new Proxy(target, handler)
}
let activeEffect = null
function effect(fn) {
    activeEffect = fn
    activeEffect()
    activeEffect = null
}
function ref(initValue) {
    return reactive({
        value: initValue
    })
}
function computed(fn) {
    const result = ref()
    effect(() => result.value = fn())
    return result
}

Proxy and Reflect

Proxy

const person = { name: '林三心', age: 22 }

const proxyPerson = new Proxy(person, {
    get(target, key, receiver) {
        console.log(target) // 原来的person
        console.log(key) // 属性名
        console.log(receiver) // 代理后的proxyPerson
    },
    set(target, key, value, receiver) {
        console.log(target) // 原来的person
        console.log(key) // 属性名
        console.log(value) // 设置的值
        console.log(receiver) // 代理后的proxyPerson
    }
})

proxyPerson.name // 访问属性触发get方法

proxyPerson.name = 'sunshine_lin' // 设置属性值触发set方法

Reflect

Here are two methods of Reflect

  • get(target, key, receiver) : Personal understanding is that access target of key property, but this pointing receiver , so the actual value is accessed is value of the receiver of the key, but this is not directly accessible receiver[key] property, you have to distinguish between
  • set(target, key, value, receiver) : Personal understanding is set target of key property value , but this pointing receiver , it actually is to set Receiver of the key value value , but it is not directly receiver[key] = value , we have to distinguish between

We emphasized above that you cannot directly receiver[key] or receiver[key] = value , but use and Reflect.set to access or set properties around a corner. Why is this? Let's take a counter example below

const person = { name: '林三心', age: 22 }

const proxyPerson = new Proxy(person, {
    get(target, key, receiver) {
        return Reflect.get(receiver, key) // 相当于 receiver[key]
    },
    set(target, key, value, receiver) {
        Reflect.set(receiver, key, value) // 相当于 receiver[key] = value
    }
})

console.log(proxyPerson.name)

proxyPerson.name = 'sunshine_lin' 
// 会直接报错,栈内存溢出 Maximum call stack size exceeded

Why is this so? Look at the solution below

截屏2021-08-27 下午10.55.49.png

Now you know why you can't directly receiver[key] or receiver[key] = value , because doing this directly will cause an infinite loop, and eventually an error will be reported. So the correct way is

const person = { name: '林三心', age: 22 }

const proxyPerson = new Proxy(person, {
    get(target, key, receiver) {
        return Reflect.get(target, key, receiver)
    },
    set(target, key, value, receiver) {
        Reflect.set(target, key, value, receiver)
    }
})

console.log(proxyPerson.name) // 林三心

proxyPerson.name = 'sunshine_lin'

console.log(proxyPerson.name) // sunshine_lin

Some students are sure to ask, the following is fine, why not suggest it? I put it down and say it together

const proxyPerson = new Proxy(person, {
    get(target, key, receiver) {
        return Reflect.get(target, key)
    },
    set(target, key, value, receiver) {
        Reflect.get(target, key, value)
    }
})

Why use it together

In fact, it is possible to use Proxy without Reflect. We can write like this and still achieve the desired effect

const person = { name: '林三心', age: 22 }

const proxyPerson = new Proxy(person, {
    get(target, key, receiver) {
        return target[key]
    },
    set(target, key, value, receiver) {
        target[key] = value
    }
})

console.log(proxyPerson.name) // 林三心

proxyPerson.name = 'sunshine_lin'

console.log(proxyPerson.name) // sunshine_lin

So why is it recommended to use Proxy and Reflect together? Because Proxy and methods Reflect is one to one, in Proxy use in Reflect will improve semantic

  • The get of Proxy corresponds to Reflect.get
  • Proxy set corresponds to Reflect.set
  • There are many other methods I won’t list them one by one, they all correspond one by one

Another reason is, try to put this on receiver instead of target

Why put this on the proxy object receiver as much as possible, instead of putting it on the original object target ? Because the original object target may have been a proxy object of another proxy, if this is kept on target , the probability of bugs will be greatly increased. So why is the previous code not recommended? You should know?

const proxyPerson = new Proxy(person, {
    get(target, key, receiver) {
        return Reflect.get(target, key)
    },
    set(target, key, value, receiver) {
        Reflect.set(target, key, value)
    }
})

Concluding remarks

I am Lin Sanxin, an enthusiastic front-end rookie programmer. If you are motivated, like the front-end, and want to learn the front-end, then we can make friends, fish together haha, fish school, add me, please note [think]

image.png


Sunshine_Lin
2.1k 声望7.1k 粉丝