Preface ๐ŸŒŸ

In the form of experiments, this paper reveals the best practices of Watch in Vue3.

The main purpose of this article is to investigate how watch gets current and previous values when it listens for reactive data. By the way, I will remind you how to use Watch, and then introduce how to use REF and Reactive to define reactive data in order to obtain current and previous values with watch.

I. INTRODUCTION to API

watch(WatcherSource, Callback, [WatchOptions])

type WatcherSource<T> = Ref<T> | (() = > T)

interface WatchOptions extendsWatchEffectOptions { deep? : boolean// Default: falseimmediate? : boolean// Default: false
  
}
Copy the code

Parameter Description:

WatcherSource: Used to specify the reactive variable to listen for. A WatcherSource can pass in ref-responsive data, and reactive objects are written as functions.

Callback: Callback function to execute, receiving the current value newValue and the previous value oldValue as input arguments.

WatchOptions: Supports deep and immediate. Set deep: true when deep listening on responsive objects is required. Watch is lazy by default, and when we set immediate: true, watch will execute the callback function immediately upon initialization.

In addition, VuE3’s Watch also supports listening on multiple responsive data, and can also manually stop watch listening.

Two, listen to multiple data sources

<template>
  <div class="watch-test">
    <div>Name: {{name}}</div>
    <div>Age: {{age}}</div>
  </div>
  <div>
    <button @click="changeName">Change the name</button>
    <button @click="changeAge">Change the age</button>
  </div>
</template>

<script>
  import {ref, watch} from 'vue'

  export default {
    name: 'Home'.setup() {

      const name = ref('Komatsu Nagai')
      const age = ref(25)

      const watchFunc = watch([name, age], ([name, age], [prevName, prevAge]) = > {
        console.log('newName', name, 'oldName', prevName)
        console.log('newAge', age, 'oldAge', prevAge)
        if (age > 26) {
          watchFunc() // Stop listening
        }
      },{immediate:true})

      const changeName = () = > {
        name.value = 'Pure village frame'
      }
      const changeAge = () = > {
        age.value += 2
      }
      return {
        name,
        age,
        changeName,
        changeAge
      }
    }
  }
</script>
Copy the code

What you see: Watch detects changes in data when you change your name and age. When age is greater than 26, we stop listening, and then change the age. Because of the stop of Watch, the callback function of Watch becomes invalid.

Conclusion: We can use watch to listen to the changes of multiple values, or use the watch function to fetchThe nameAnd then through executionName (s)Function to stop listening.

Listen array

<template>
  <div class="watch-test">
    <div>{{arrayRef}}</div>
    <div>{{arrayReactive}}</div>
  </div>
  <div>
    <button @click="changeArrayRef">Change ref to define the first item in the array</button>
    <button @click="changeArrayReactive">Change the first item in the reactive definition array</button>
  </div>
</template>

<script>
  import {ref, reactive, watch} from 'vue'

  export default {
    name: 'WatchTest'.setup() {
      const arrayRef = ref([1.2.3.4])
      const arrayReactive = reactive([1.2.3.4])

      //ref not deep
      const arrayRefWatch = watch(arrayRef, (newValue, oldValue) = > {
        console.log('newArrayRefWatch', newValue, 'oldArrayRefWatch', oldValue)
      })

      //ref deep
      const arrayRefDeepWatch = watch(arrayRef, (newValue, oldValue) = > {
        console.log('newArrayRefDeepWatch', newValue, 'oldArrayRefDeepWatch', oldValue)
      }, {deep: true})

      //reactive, sources are not functions
      const arrayReactiveWatch = watch(arrayReactive, (newValue, oldValue) = > {
        console.log('newArrayReactiveWatch', newValue, 'oldArrayReactiveWatch', oldValue)
      })

      // Best practice of array listening - reactive and source function return, return copied data
      const arrayReactiveFuncWatch = watch(() = > [...arrayReactive], (newValue, oldValue) = > {
        console.log('newArrayReactiveFuncWatch', newValue, 'oldArrayReactiveFuncWatch', oldValue)
      })

      const changeArrayRef = () = > {
        arrayRef.value[0] = 6
      }
      const changeArrayReactive = () = > {
        arrayReactive[0] = 6
      }
      return {
        arrayRef,
        arrayReactive,
        changeArrayRef,
        changeArrayReactive
      }
    }
  }
</script>
Copy the code

If deep:true is not added, watch will not listen to the change of value when the array is defined as responsive data ref. With deep:true, watch can detect changes in the data, but the old value is the same as the new value, that is, it cannot fetch the old value. When the array is defined as a responsive object, watch can detect the change of data without any processing, but the old value is the same as the new value. If the data source of Watch is written as a function and a set of numbers is returned by cloning the extended operator, the new and old values can be obtained at the same time while listening.

Conclusion: When defining an array, it is better to define the data as a reactive object. In this way, when watch listens, it only needs to write the data source as a function and clone a set of returns by extending the operator, so that it can obtain new and old values while listening.

4. Listening object

<template>
  <div class="watch-test">
    <div>user:{</div>
      <div>name:{{objReactive.user.name}}</div>
      <div>age:{{objReactive.user.age}}</div>
    <div>}</div>
    <div>brand:{{objReactive.brand}}</div>
    <div>
      <button @click="changeAge">Change the age</button>
    </div>
  </div>
</template>

<script>
  import {ref, reactive, watch} from 'vue'
  import _ from 'lodash';

  export default {
    name: 'WatchTest'.setup() {
      const objReactive = reactive({user: {name: 'Komatsu Nagai'.age: '20'}, brand: 'Channel'})

      //reactive sources are functions
      const objReactiveWatch = watch(() = > objReactive, (newValue, oldValue) = > {
        console.log('objReactiveWatch')
        console.log('new:'.JSON.stringify(newValue))
        console.log('old:'.JSON.stringify(oldValue))
      })

      //reactive, source function, deep: true
      const objReactiveDeepWatch = watch(() = > objReactive, (newValue, oldValue) = > {
        console.log('objReactiveDeepWatch')
        console.log('new:'.JSON.stringify(newValue))
        console.log('old:'.JSON.stringify(oldValue))
      }, {deep: true})

      // Best practice of deep object listening reactive and source function return, return deep-copy data
      const objReactiveCloneDeepWatch = watch(() = > _.cloneDeep(objReactive), (newValue, oldValue) = > {
        console.log('objReactiveCloneDeepWatch')
        console.log('new:'.JSON.stringify(newValue))
        console.log('old:'.JSON.stringify(oldValue))
      })

      const changeAge = () = > {
        objReactive.user.age = 26
      }

      return {
        objReactive,
        changeAge
      }
    }
  }
</script>
Copy the code

When the reactive object is configured, the watch cannot monitor the value changes if deep:true is not added. With deep:true, watch can detect data changes, but the old value is the same as the new value, that is, it cannot obtain the old value. If you write the watch data source as a function and return an object with a deep-copy clone (here using a deep copy of the LoDash library), you can get both the new and old values while listening.

Conclusion: When defining objects, it is better to define data as reactive objects. In this way, when watch is listening, it only needs to write data source in the form of function and return a cloned object through deep copy, so that it can obtain new and old values while listening.

5. Conclusion โœจ

1. Usually we takeThe original typeThe data (number, string, etc.) ofrefResponse data,Reference typesThe data (array, object) of is defined asreactiveResponsive data;

2. When we use Watch to monitor data change needsGet both the new value and the old valueWhen, we need to putThe data source is defined as a function and is returned as a deep copy. When weYou just need the new value, can be increaseddeep:trueThe options are ok.

In fact, the data of reference types defined as ref form it doesn’t matter, only need to define the data source for the function form, and return the data source for deep copy, to get a new and old value ~ ha ha ha ha ha ha ha ha ha ha ha ha ha ha, but I think best practice is to define the reference type as reactive response data.

End ~ ๐Ÿ‘ป