Table of contents
1. Comparison with vue2
The responsive principle of vue2 is implemented through defineProperty
, and vue3 is implemented through Proxy
.
But the principle is not discussed here. What is discussed is the use and points of attention of the new API of vue3. Because vue3 exposes the responsive API, it is an independent data responsive system that has nothing to do with the components, so it can be achieved a>. Single state management
- The responsive data of vue2 is placed in
data(){}
and will eventually be injected into the component instance. - The responsive data of vue3 is implemented through the exposed responsive API, and is finally returned through
setup()
.
2. Introduction to core API
Responsive data in vue3 has 2 formats:
reactive
returns a Proxy object with direct access to properties.ref
The Ref object returned by Attributes.computed
needs to be accessed through and.value
1,reactive 和 readonly
Let me talk about a few important points first:
- can only be used forobject types.
- The returned proxy object is not equal to the original object.
- vue3 In order to ensure the consistency of access to the proxy, calling the same original object
reactive()
will always return the same proxy object, while calling an existing proxy object (example below). itselfreactive()
will return readonly
The only difference is that the returned proxy object is read-only, and an error will be reported if the properties are modified.
Look at the example below:
import {
reactive, readonly } from 'vue'
const origin = {
a: 1, b: 2 }
const state = reactive(origin)
console.log(state === reactive(origin)) // true 相同的代理对象
console.log(state === reactive(state)) // true 返回自身
const stateOnly = readonly(state)
console.log(stateOnly === state) // false
state.a++
console.log(stateOnly.a) // 2
While stateOnly
is read-only. But because the proxy is state
, when state
is modified, stateOnly
will also be modified.
extra,stateOnly
proxy–> state
proxy–> { a: 1, b: 2 }
, therefore stateOnly !== state
For simple purchase:
import {
readonly, reactive } from 'vue'
/**
* 返回1个对象和2个方法,
* 对象是是响应式的,不允许直接修改。只能通过提供的方法修改指定属性。
* @returns Object
*/
function useUser() {
const userOrigin = reactive({
})
const user = readonly(userOrigin)
const setUserName = (name) => {
userOrigin.name = name
}
const setUserAge = (age) => {
userOrigin.age = age
}
return {
user,
setUserName,
setUserAge
}
}
const {
user, setUserName, setUserAge } = useUser()
console.log(user)
setUserName('下雪天的夏风')
setUserAge(18)
console.log(user)
2,ref
can proxy any data, because it puts all the data on the .value
attribute of an object and returns this object.
Note that this object is not a Proxy object, but a Ref object. The difference has been explained above.
If the parameter of ref()
is Proxy object, put it directly into < a i=4> attribute. .value
const state = reactive({
a: 1, b: 2 })
const stateRef = ref(state)
console.log(stateRef.value === state) // true
The reason for using .value
is that access or modification of ordinary variables cannot be directly detected. So use the getter
and setter
methods to intercept the get and set operations of the object properties.
The general principle of ref is as follows:ref principle reference
const myRef = {
_value: 0,
get value() {
// 触发依赖收集,重新渲染
track()
return this._value
},
set value(newValue) {
this._value = newValue
// 通知依赖它的对象更新
trigger()
}
}
3. Monitor data
watchEffect
This function willrun immediately whiletracking its dependencies responsively< a i=4>, and re-executed when dependencies change.
In other words, you can monitor multiple ones at the same time. As long as the reactive data used in this function is modified, this function will be re-executed.
In vue3, in most cases it is enough to use watchEffect()
to listen to data.
watch
watch
The usage of is not much different from that in vue2. The difference from watchEffect
is:
watch
The default is lazy listening, that is, the callback function will only be executed when the listening source changes. You can specify the configuration item{ immediate: true }
to execute it immediately once.- The old value can be obtained.
- You can know more clearly which state modification caused watch to be re-executed.
important point:
watch
Normal data cannot be monitored, and the monitoring sources can only be the following 4 types:
- A function that returns a value
- a ref
- a reactive object
- An array consisting of values of the above types
Look at the following example:
import {
reactive, watch } from 'vue'
const state = reactive({
a: 1, b: 2 })
watch(state.a, () => {
console.log('变化了')
})
state.a++ // watch 函数并不会执行。
state.a
It is ordinary data, not responsive, so it cannot be monitored.
import {
reactive, watch } from 'vue'
const state = reactive({
a: 1, b: 2 })
watch(() => state.a, () => {
console.log('变化了')
})
state.a++ // 变化了
If the first parameter of watch
is a function, the function will be called to collect the dependencies . In other words, the state used in state.a
is responsive data, so it can be monitored.
But if you are monitoring const count = ref(0)
, you can directly monitor count
without using a function to return. Because what is passed is object. So the value of value
can be monitored.
4. Judgment and conversion
judgment
API | meaning |
---|---|
isProxy |
Determine whether the data was created byreactive or readonly |
isReactive |
Determine whether the data isreactive created |
isReadonly |
Determine whether the data isreadonly created |
isRef |
Determine whether the data isref object |
Convert(toRefs)
More referencesUtility functions
1,unref()
If the argument is a ref, the internal value is returned, otherwise the argument itself is returned.
val = isRef(val) ? val.value : val
2,toRefs
This is more important.
toRefs
will convert all properties of a responsive object into ref
format, and then wrap them into a normal object and return them.
Look at the following example:
import {
reactive, toRefs } from 'vue'
const state = reactive({
a: 1, b: 2 })
const stateRef = toRefs(state)
console.log(stateRef) // {a: refObj, b:refObj},所以将 stateRef 展开也是响应式的。
console.log(stateRef.a.value) // 1
应用1:
When the parent passes a lot of data, but only wants to use one of them, and needs to remain responsive:
<template>
<h1>{
{ msg }}</h1>
<h1>{
{ msg2Alias }}</h1>
</template>
<script setup>
import {
toRef, toRefs, computed } from "vue";
const props = defineProps(["msg", "msg2"]);
// 下面3种方式等价,父级修改 msg2 时,msg2Alias会同步修改。
const {
msg2: msg2Alias } = toRefs(props);
const msg2Alias = toRef(props, "msg2");
const msg2Alias = computed(() => props.msg2);
</script>
应用2:
When using a method that returns a proxy object, can be deconstructed without losing responsiveness.
import {
reactive, toRefs } from "vue";
// composition function
function usePos(){
const pos = reactive({
x:0, y:0});
return pos;
}
setup(){
const {
x, y} = usePos(); // lost reactivity
const {
x, y} = toRefs(usePos()); // reactivity
}
that's all.