SoFunction
Updated on 2025-04-04

Detailed explanation of the implementation and differences between reactive and ref in vue3

Preface

Reactive and Ref are both APIs of Vue3 that implement responsive systems. How do they implement responsiveness? What is the difference between reactive and ref? After reading this article, I believe the answer is about to come out.

effect

Reactive in vue3 implements the responsiveness of data. Speaking of data responsiveness, effect is the key, and the effect() method is an method exposed to creators. Parameter 1 is a callback function. The code written in the callback function can be used to simulate the data of the responsiveness of the setup execution function being written in the template template. After the data is updated, the callback function in the effect is called again to achieve the purpose of updating the view when the data is updated. Effect can also be called data-related dependencies, that is, the code that records responsive data. Parameter 2 is an object with a lazy attribute that specifies whether the callback function of the effect is executed for the first time.

<body>
    <div >hello</div>
    <script>
        let data={
            name:'A Wolf on the Prairie'
        }
        effect(()=>{
            let app = ('app')
            =
        },{lazy:true})
          setTimeout(()=>{
           ='cut'
        },1000)
    </script>
</body>

Implement the effect method, save the callback function when the data is updated, call the callback function again to reach the update view

export function effect(fn, options: any = {}) {//Update the view, that is, the bar view uses data to execute it once  const effect = createReactEffect(fn, options);
  if (!) {//If lazy: false execute    effect();
  }
  return effect;
}
let uid = 0;
//Every time the call will create an effectlet activeeffect;//Set the current effect as a global variable for collectionlet effectStack = [];//Save effect because there may be multiple effectsfunction createReactEffect(fn, options) {
  const effect = function reactiveEffect() {
    if (!(effect)) {
      try {
        (effect);
        activeeffect = effect;
        fn();
      } finally {
        ();
        activeeffect = effectStack[ - 1];
      }
    }
  };
   = uid++; //Difference effect  effect._isEffect = true; //Difference is effect responsive effect   = fn; //Save the callback function to itself   = options; //Save user attribute lazy
  return effect;
}

reactive

After understanding the effect, the next step is to implement the specific process of reactive:

  • Responsive data is used to collect dependencies when using proxy proxy
  • Set execution dependency when responsive data is used as proxy proxy

1. Get collects dependencies when responsive data is used as proxy proxy

function createGetter(isReadonly = false, shall = false) {
  return function get(target, key, receiver) {
    const res = (target, key, receiver); //taget[key]

    if (!isReadonly) {
      //Judge whether it is read-only collect dependencies      Track(target, , key);//Track collects dependencies A responsive attribute key corresponds to an effect to save it    }

    if (shall) {
      //Only one layer      return res;
    }
    if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res);
    }
    return res;
  };
}
//Collect effect Get data trigger get Collect dependency data changes update view corresponding to key and effect11let targetMap = new WeakMap(); //Stack structure (target, new Map(key(key effect one-to-one correspondence), new Set(activeeffect)))export function Track(target, type, key) {
  //The corresponding key  if (activeeffect == undefined) {
    return;
  }
  let depMAp = (target);
  if (!depMAp) {
    (target, (depMAp = new Map()));
  }
  let dep = (key);
  if (!dep) {
    (key, (dep = new Set()));
  }
  if (!(activeeffect)) {
    (activeeffect);
  }
}

2. Set execution dependency when responsive data is used as proxy proxy

function createSetter(shall = false) {
  return function set(target, key, value, receive) {
    const oldValue = target[key];

    let hasKey =
      (oldValue) && isIntegerKey(key)
        ? Number(key) < 
        : hasOwn(target, key);
    const result = (target, key, value, receive); //Get the latest value The object data has been updated    if (!hasKey) {
      // Added      trigger(target, , key, value);
    } else {
      // Modify the array      if (hasChange(value, oldValue)) {
        trigger(target, , key, value, oldValue);
      }
    }

    return result;
  };
}
//Trigger update dependency effectexport function trigger(target, type, key?, newValue?, oldValue?) {
  const depsMap = (target);
  let effectSet = new Set();
  if (!depsMap) return;
  const add = (effectAdd) => {
    if (effectAdd) {
      ((effect) => (effect));
    }
  };
  add((key)); //Get the effect of the current attribute  //Processing array  if (key == "length" && (target)) {
    ((dep, key) => {
      if (key === "length" || key >= newValue) {
        add(dep);
      }
    });
  }else{
    //Processing objects    if(key !== undefined){
      add((key))
    }
    switch(type){
      case : 
      if((target)&& isIntegerKey(key)){
        add(('target'))
      }
    }
  }
  ((effect: any) => {
    if(){
      (effect)
    }else{
      effect()
    }
  });
  • The data wrapped in reactive is responsive. First, the callback function must be saved to facilitate data update and call again to update the view.
  • When proxying the object with proxy, the code snippet used in the view, that is, the related effect and key are saved one by one.
  • When the data changes, use proxy set method, use key to find the corresponding effect to execute

ref

I believe everyone knows to use reactive to implement complex data proxy to implement simple data proxy with ref, because vue uses es6 proxy to proxy the entire object, while ref wraps simple data with an object and accesses it with .value. If you use ref to process an object responsively, the crtoReactive method will be called again to proxy the properties of the .value. It is much easier to implement ref based on the reactive API that has been implemented above.

The specific steps are:

1. If it is simple data, use a proxy

2. If it is complex data, use the createReactive method to proxy it again.

export function ref(target){
    return creatRef(target)
}

//Create instance objectclass RefImpl{
    public _v_isRef=true
    public _value
    
    public _shallow
    public _rawValue
    constructor(public target,public shallow){
        this._shallow=shallow
        this._rawValue = shallow ? target : toRaw(target)
        this._value = shallow ? target : toReactive(target)
    }
    get value(){
        Track(this,,"value")
        return 
    }
    set value(newVAlue){
        if(newVAlue!==this._value)
         this._rawValue=newVAlue 
         this._value = isObject(newVAlue) ? newVAlue : toReactive(newVAlue)//This is the key to ref without losing responsiveness. If the new value is reproxy, reproxy        trigger(this,,"value",newVAlue)


    }
}
function creatRef(target,shallow=false){
    //Create a ref instance object    return new RefImpl(target,shallow)
}

This is the end of this article about the implementation and differences between reactive and ref in vue3. For more related vue3 reactive ref content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!