Home > Web Front-end > Vue.js > body text

What is the implementation principle of Vue3 listener

王林
Release: 2023-05-16 15:04:06
forward
1316 people have browsed it

Listening to responsive objects

We talked about calculated properties earlier, which can automatically calculate and cache the value of responsive data. And if we only need to perform some preset operations when the responsive data changes, we can use the watch listener. Let’s implement the simplest example first, and then expand it bit by bit.

const data = {foo: 1}
const obj = reactive(data)
watch(obj, () => {
  console.log('obj已改变')
})
Copy after login

In this example, we use the watch listener. When the property of obj is changed, the console should print out obj has been changed. Based on our previous implementation of calculated properties, we already have a general idea here. Think of watch as a side effect function of the responsive object. When the responsive object changes, the execution of the side effect function is triggered.

If you want to trigger a side-effect function, you must first collect it. Do you remember how the side-effect function is collected? Yes, When the reactive data is get, the side effect function is collected. So first, we need to make watch collected by the reactive object.​

function watch(getter, cb) {
  effect(
    () => getter.foo
  )
}
Copy after login

Next, we also need to let our default method be executed. When responsive data is set, the side effect function is triggered. What we want to trigger here is the callback function passed in cb. Here we can use the scheduler when implementing calculated properties. When the scheduler exists, set triggers The trigger will first execute the function in the scheduler.

function watch(getter, cb) {
  effect(
    () => getter.foo,
    {
      scheduler() {
        cb()
      }
    }
  )
}
Copy after login

What is the implementation principle of Vue3 listener

A simple listener is complete! Here, for the sake of simplicity, we have written the function to death and only supports listening to obj.foo. Next, we have to think about how to listen to any properties of the responsive object?

According to the previous idea, if we want to listen to any attribute of the responsive object, we need to get to each attribute of the object, which requires us to The object is traversed recursively.

function traverse(value, seen = new Set()) { // (1)
  if(typeof value !== 'object' || value === null || seen.has(value)) return
  seen.add(value)
  for(const key in value) {
    traverse(value[key], seen)
  }
  return value
}
Copy after login

In order to avoid the infinite loop caused by circular references when recursively traversing objects, we created Set at (1). When the same object appears repeatedly , return directly.

Listening to property values

In Vue3, we cannot directly listen to the property values ​​of responsive objects. If you need to listen to the property value of the responsive object, you need a getter function so that the listener can be collected by the responsive object.

const data = {
  foo: 1
}
const obj = reactive(data)
watch(
  () => obj.foo, 
  () => {
  console.log('obj.foo已改变')
})
Copy after login

Specifying attributes means that the current listener will only be triggered by the specified attributes, and there is no need to recursively traverse the entire responsive object.

function watch(getter, cb) {
  if(typeof getter !== 'function') getter = traverse(getter) // (2)
  effect(
    () => getter(),
    {
      scheduler() {
        cb()
      }
    }
  )
}
Copy after login

What is the implementation principle of Vue3 listener

At (2), we added a judgment. If the incoming function is already a getter function, we use it directly. If notgetter function is considered a responsive object and needs to be traversed recursively.

Listen to get the new value and the old value

In Vue we also need to be able to get the new value before and after the responsive data update in the callback function cb() with the old value.

const data = {
  foo: 1
}
const obj = reactive(data)
watch(
  () => obj.foo, 
  (newValue, oldValue) => {
  console.log(newValue, oldValue)
})
Copy after login

The next question is, how to obtain newValue and oldValue. newValue is easy to solve. After executing the callback function cb(), what you get is newValue, but how to get the value of oldValue here? To get the old value from watch, the side-effect function cannot be executed immediately. What comes to mind here? Yes, when implementing computed properties, we have used lazy, which can prohibit the automatic execution of side-effect functions.

function watch(getter, cb) {
  if(typeof getter !== 'function') getter = traverse(getter)
  let oldValue
  const effectFn = effect(
    () => getter(),
    {
      lazy: true, // (3)
      scheduler() {
          cb(oldValue)
      }
    }
  )
  oldValue = effectFn() // (4)
}
Copy after login

At (3) we set the lazy switch. After setting lazy, the execution rights of the side effect function are handed over to ourselves. At (4), we manually execute the side-effect function. We need to look back here. The getter we passed in earlier is a function () => obj.foo, and the effect function One parameter is the side effect function that is actually executed, so what we execute manually is actually the function () => obj.foo, so we get the old value.

How to get the new value? After the value of the responsive data is updated, the side effect function effect will be triggered to execute. When the scheduler attribute exists, the scheduler will be executed. In the scheduler, we can execute the side effect function again and get the changed new value through () => obj.foo.

function watch(getter, cb) {
  if(typeof getter !== 'function') getter = traverse(getter)
  let oldValue, newValue
  const effectFn = effect(
    () => getter(),
    {
      lazy: true,
      scheduler() {
        newValue = effectFn()
        cb(newValue, oldValue)
        oldValue = newValue // (5)
      }
    }
  )
  oldValue = effectFn()
}
Copy after login

At (5), after executing the callback function cb(), we did some aftermath work and updated the value of oldValue for the next callback prepare for.

What is the implementation principle of Vue3 listener

#Sometimes, we also want the listener to execute the callback function immediately when it is created.

const data = {
  foo: 1
}
const obj = reactive(data)
watch(
  () => obj.foo, 
  (newValue, oldValue) => {
      console.log('newValue:', newValue,', oldValue:', oldValue)
  },
  { immediate: true }
)
Copy after login

When the value of immediate is true, it needs to be executed immediately. After clarifying the requirements, let's improve the watch listener.

function watch(getter, cb, options = {}) {
  if(typeof getter !== 'function') getter = traverse(getter)
  let oldValue, newValue
  function job() { // (6)
    newValue = effectFn()
    cb(newValue, oldValue)
    oldValue = newValue
  }

  const effectFn = effect(
    () => getter(),
    {
      lazy: true,
      scheduler: job,
    }
  )

  if(options.immediate) {  // (7)
    job()
  } else {
    oldValue = effectFn()
  } 
}
Copy after login

在(6)处,我们抽离了回调函数的执行逻辑,当options.immediate存在时,直接触发执行。

What is the implementation principle of Vue3 listener

实现效果

What is the implementation principle of Vue3 listener

The above is the detailed content of What is the implementation principle of Vue3 listener. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:yisu.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!