2
头图

Are you surprised when you see the title? Maybe think of Reactivity is defective in design? In fact, the drunkard’s intention is not to drink! Reactivity can be said to be the most complicated place in vue3, and of course it is also the most powerful point. It sounds like a hard bone, which reminds me of what Li Yunlong said in the bright sword to face the provocation of the little Japan: it is the king of Yan. , I also have to hug him down a few beards. So today we also Reactivity the beard of 060c051e1970a9.

In vue3, it exposes all the data-responsive APIs. In vue2, this is not done. So how does vue2 do it? It is a data configuration to data which, data data is automatically made responsive data, we process called injection, it is injected into them to the component instance, as follows:

{
    //data中的数据都是响应式的,会被注入到组件实例当中
    data(){
        return{
            name:"法医",
            idol:"乔布斯",
            publicNumber:"前端猎手"
        }
    }
}

However, in vue3, it is no longer data , but setup function. We call it composition Api . If we want to use responsive data in the setup function, we inevitably need to expose responsive APIs for us to use. Next, let's take a look at what functions vue3 provides related to data responsiveness.

🍇 Get responsive data

APIs that can obtain responsive data:

  • reactive

    This api will pass in a object, and then return a object proxy proxy, and it can deeply proxy all members of the object, which means that another object is nested in the object, which is also responsive data

    give a chestnut 🌰:

    import { reactive } from 'vue'
    const state = reactive({name:"法医",age:18})
    window.state = state;

    effect display:

    image.png

    Now the object becomes responsive. Operate the object and vue can receive notifications.

  • readonly

    This api needs to pass in an object or a proxy, and it will also return an object proxy. It can only read the members in the proxy object, but cannot modify it, that is get , not set . It can also be a deep proxy All members of the object.

    give a chestnut 🌰:

    import { reactive,readonly } from 'vue'
    const imState = readonly({name:"法医",age:18})
    window.imState = imState;

    effect display:

    image.png

    Did you see it? An error was reported, saying that the target to be modified is read-only, and the modification failed

    I just said that readonly can pass an object or an agent. Let’s see what happens when an agent is passed.

    import { reactive,readonly } from 'vue'
    const state = reactive({name:"法医",age:18})
    const imState = readonly(state);//传一个代理进去
    window.imState = imState;

    effect display:

    image.png

    Assignment operation is also not allowed when passing a proxy. Anyway, after readonly , it can only be read and cannot be assigned. However, we can modify the value before readonly and then let it perform proxying.

    📢Note: reactive and readonly , these two APIs are proxy objects, there is no way to proxy ordinary values, and an error will be reported

    give a chestnut 🌰:

    import { reactive,readonly } from 'vue'
    const state = reactive("法医")

    Effect display:

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/54cc2bca665c491cb14ede3e3c3919e1~tplv-k3u1fbpfcp-watermark.image)

报了一个警告,原始值是没法代理的,那么如果需要代理普通值,那该咋办呢?那就要用到`ref`api了
  • ref

    ref can pass in any data, and finally put the data in the value of an object, such as this { value : ...} , the access to the value is responsive, if the value given to the value is an object, it will pass the reactive function Acting

    give a chestnut 🌰:

    import { reactive,readonly,ref } from 'vue'
    const state = ref({name:"法医",age:18})
    window.state = state;

    effect display:


![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/41013bd555294aa3b53adc1c6602998e~tplv-k3u1fbpfcp-watermark.image)

还有一种情况是:如果说传入的value值已经是代理了,那么会直接使用代理

 **举个栗子🌰:**
```js
import { reactive,readonly,ref } from 'vue'
const state = reactive({name:"法医",age:18});
const imState = ref(state)
window.imState = imState;
```
 **效果展示:**

![](https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9becafd7f0ab41c7b5d4f69c63a26d94~tplv-k3u1fbpfcp-watermark.image)
  • computed

    computed need to pass a function function , it returns the same value with the ref, also {value:...} , when the read value value when it decide whether or not to run the function under the circumstances, this situation is that there did not use this function, and it There is a cache. When the dependent response data does not change, the value in the cache is obtained. Only when state.name or state.age changes, it will re-run.

    give a chestnut 🌰:

    import { reactive,readonly,ref ,computed} from 'vue'
    const state = reactive({name:"法医",age:18})
    
    const result = computed(()=>{
        console.log("computed");
        return state.name + state.age
    })
    console.log(result.value);

    Effect display:


![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d0f14b8654a94861a9100de3088923b6~tplv-k3u1fbpfcp-watermark.image)

反正以上四个api不管它怎么进行处理,就一个目的,那就是把数据变成响应式数据。

那么在开发中到底用哪个?

1. 如果想要一个对象变为响应式数据,可以使用`reactive`或者`ref`
2. 如果说让对象所有属性只能读,就用`readonly`
3. 如果想让一个非对象数据变成响应式数据,就用`ref`
4. 如果想要根据已知的响应式数据得到一个新的响应式数据,就用`computed`

📢 注意:用`ref`的时候要拿到数据必须是`ref.value`哈,不然拿不到,切记!😁

🍉 Monitor data changes

  • watchEffect

    const stop = watchEffect(() => {
      //watchEffect 函数会立即执行,然后监听函数中会用到的响应式数据,响应式数据变化后会再次执行
    })
    
    //通过调用stop函数就会停止监听
    stop();//停止监听

give a chestnut 🌰:

  import { reactive, ref, watchEffect } from "vue";

  const state = reactive({a: 1,b: 2,});
  const count = ref(0);

  watchEffect(() =>{
      console.log(state.a,count.value);//先会立即执行一次
  })
  
  state.a++;//这里依赖数据改变再次执行一次

effect display:

image.png

watchEffect reason why the 060c051e197a2d function knows that the dependency has changed is because the data in it is responsive. When reading the data, the get method is used, and get will collect the dependency. Also note that if the dependent data is changed many times at the same time, the final result will be displayed once, because the running process is asynchronous, it will be executed in the micro-queue, and it will run after the data is changed, as in the following example:

give a chestnut 🌰:

  import { reactive, ref, watchEffect } from "vue";

  const state = reactive({a: 1,b: 2,});
  const count = ref(0);

  watchEffect(() =>{
      console.log(state.a,count.value);
  })
  //运行多次
  state.a++;
  state.a++;
  state.a++;
  state.a++;
  state.a++;
  state.a++;
  state.a++;
  state.a++;

effect display:

image.png

It can be seen from the final result that it only runs twice in the end, once is executed immediately, and the second is after the data is changed.

  • watch

This watch equivalent to 060c051e197b51 of $watch . This watch is a bit troublesome because it needs to manually specify which values to monitor for changes. When it changes, it will give new value of old value of 160c051e197b58 at the same time.

give a chestnut 🌰:

  import { reactive, ref, watch } from "vue";

  const state = reactive({a: 1,b: 2,});
  const count = ref(0);

  watch(()=>state.a,(newValue,oldValue) =>{
      console.log("新值",newValue,"旧值",oldValue);
  })
  state.a++;//修改依赖

effect display:

image.png

📢 Note: It should be noted that watch with watchEffect difference is watch not run immediately function will be performed only when the value of dependency, watch will pass two parameters passed in the above example is ()=>state.a , Then why not pass state.a directly? If you pass state.a directly, it is equivalent to passing a 1 in, so the data is not responsive, as follows:

give a chestnut 🌰: directly pass state.a

  import { reactive, ref, watch } from "vue";

  const state = reactive({a: 1,b: 2,});
  const count = ref(0);

  watch(state.a,(newValue,oldValue) =>{
      console.log("新值",newValue,"旧值",oldValue);
  })
  state.a++;//修改依赖

effect display:

image.png

If you directly pass state.a , a warning will be reported here. The translation is: Invalid monitoring source: 1 The monitoring source can only be getter/effect functions, refs, passive objects or these types of arrays , which means that the parameters here can only be It is responsive data.

When passing a ()=>state.a function into it, it runs in the watch, so that the dependencies will be collected. When using reactive api, you must pass a function like this. When using ref api, the first parameter in watch can be written as count, because count is an object, as follows:

give a chestnut 🌰: directly pass count

  import { reactive, ref, watch } from "vue";

  const state = reactive({a: 1,b: 2,});
  const count = ref(0);

  watch(count,(newValue,oldValue) =>{
      console.log("新值",newValue,"旧值",oldValue);
  })
  count.value++;//修改依赖

effect display:

image.png

If a count.value is passed in, an error will also be reported, because count.value also gets the attribute value, as follows:

give a chestnut 🌰: directly pass count

  import { reactive, ref, watch } from "vue";

  const state = reactive({a: 1,b: 2,});
  const count = ref(0);

  watch(count.value,(newValue,oldValue) =>{
      console.log("新值",newValue,"旧值",oldValue);
  })
  count.value++;//修改依赖

effect display:

image.png

Judging from the results of the operation, a warning was also reported, so this one must be paid attention to 😚

By the way, I almost forgot, watch can monitor multiple data, as follows:

give a chestnut 🌰: pass count and ()=>state.a

  import { reactive, ref, watch } from "vue";

  const state = reactive({a: 1,b: 2,});
  const count = ref(0);

  watch([()=>state.a,count],([newValue1,newValue2],[oldValue1,oldValue2]) =>{
      console.log("新值",newValue1,newValue2,"旧值",oldValue1,oldValue2);
  })
  count.value++;
  state.a++;

effect display:

image.png

📢 Note: Whether it is watch or watchEffect , when the dependency changes, the callback function is executed asynchronously, and of course it will wait for execution in the micro-queue.

Generally speaking, watchEffect is the most convenient, because it will automatically track the changes of dependencies and does not need to be manually specified, but sometimes we have to use watch , for example: we don’t want the callback function to be executed at the beginning, we just want it It is executed when the data changes. At this time, only watch can be used. When the data changes, we need to know old value of 160c051e197ea0 is? At this time, we also need to use watch . The last one is that we need to monitor some data that is not used in the callback function. For example, output console.log("I want to change"), which cannot be done watchEffect

🍋 Judgment

reactive when obtaining response data, namely 060c051e197f02, readonly , ref , computed . There are two forms of return, one is the object proxy, and the other is {value:...} . Sometimes we may not wake up. The brain is confused, so vue3 provides 4 kinds of apis to distinguish which way to obtain the responsive data:

  • isProxy: Used to determine whether a certain data is obtained by reactive or readonly
  • isReative: Determine whether a certain data was created by reative , you can see this link for details: https://v3.vuejs.org/api/basic-reactivity.html#isreactive
  • isReadonly: Determine whether a certain data is created by readonly
  • isRef: Determine whether a certain data is a ref object

## 🍆 Conversion

ref data, we don’t know what it is, and sometimes we don’t care about it, whether proxy , ghosts don’t know what it is!

At this time we can use unref

  • unref

unref equivalent to: isRef(val) ? val.value : val , if it is ref, then take the val.value , if not, take the value of val

give a chestnut 🌰:

 function useNewTodo(todos){
     todos = unref(todos);
     //...其它代码
 }
  • toRef

toRef will turn a certain attribute in a responsive object into data in ref format

give a chestnut 🌰:

import { reactive,  toRef } from "vue";

const state = reactive({ name: "法医", age: 18 });

const nameRef = toRef(state,"name");

console.log(nameRef);//最终是 {value:...}这种形式

nameRef.value = "前端猎手";//当修改这个值后,state里面的数据也会受到影响
console.log(state.name);

effect display:

image.png

  • toRefs

Convert all the attributes of a responsive object into ref format, and then wrap it in the plain-object ordinary object and return

give a chestnut 🌰:

import { reactive, toRefs } from "vue";

const state = reactive({ name: "法医", age: 18 });

const stateAsRefs = toRefs(state);
console.log(stateAsRefs);

/*
stateAsRefs 它不是一个代理对象了,而是一个普通对象,如下格式:

{
   name:{value:ObjectRefImpl},
   age:{value:ObjectRefImpl}
}

*/

effect display:

image.png

Why do you want to do this?

give a chestnut 🌰:
We need to mix two responsive data together. If you use the expansion operator directly, it will be over, and the data will lose responsiveness.

setup() {
   const state1 = reactive({a:1,b:2});
   const state2 = reactive({c:3,d:4});
   return {
     ...state1,// 失去响应式,相当于在这写了一个a:1,b:2
     ...state2,// 失去响应式,相当于在这写了一个c:3,d:4
   };
 },

So how to solve it? Just put a toRefs

 setup() {
  const state1 = reactive({a:1,b:2});
  const state2 = reactive({c:3,d:4});
  return {
    ...toRefs(state1),// 具有响应式
    ...toRefs(state2),// 具有响应式
  };
},

😊 Okay, the above is my sharing, I hope it can be helpful to everyone, welcome everyone to discuss duck in the comment area~

I hope my friends will like it and support it~ 😘, I will be more motivated🤞, good night!


法医
129 声望16 粉丝

我很荣幸成为一名前端开发者,我不是大神,但我正在为之努力!