This is the first article of my new pit, which is VUe3. Next, I will carry out a series of actions around VUe3, including but not limited to:

  • Complete source code analysis
  • JSX engineering best practices
  • Personalized JSX solution
  • Vue3 Ecosystem development (one is currently in progress)
  • And possibly customize a VuE3 Runtime

About the source code analysis, the website has been online, vue3 source code analysis, best practices, the website is line by line form of code analysis, more focus on the source code, and in the nuggets to share the article is similar to the summary, will use a more complex article structure to write. If you want to continue to follow vue3 source, you can open the previous website to follow me.

So, go!

The biggest changes in VUe3 are the reconfiguration of the responsive principle and the newly released Composition API. This article focuses on the former to give an in-depth analysis of how responsive is implemented in VUe3.

Let’s take reactiveAPI as an example,

const Comp = {
    setup() {
        const state = reactive({
            a: 'jokcy'
        })
        
        return (a)= > {
            return <input value={state.a} onChange={(e)= > state.a = e.targent.value} />
        }
    }
}
Copy the code

Let’s look at the example above. This example is very simple. Create a component that has a responsive data object, and then render the input value bound to state.a and its onChange will modify state.a. This is a very simple and intuitive data binding example, and the fundamental reason this logic works is that when we call state.a = XXX, vue will re-render our return render function to update the node

The purpose of this article is to look at the magic of the objects we’re creating reactive to help us do that.

The API itself is very simple, passing in an object, returning a Reactive object, and creating a method called createReactiveObject

export function reactive(target: object) {
  // if trying to observe a readonly proxy, return the readonly version.
  if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
    return target;
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers
  );
}
Copy the code
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>
) {
  // The object is already a proxy

  const observed = new Proxy(
    target,
    collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers
  );
  def(
    target,
    isReadonly ? ReactiveFlags.READONLY : ReactiveFlags.REACTIVE,
    observed
  );
  return observed;
}
Copy the code

So the most important thing here is new Proxy, so to understand the responsive principle of VUe3 is to understand the process of creating this Proxy, and to understand this process is to look at the second parameter, in this case collectionHandlers or baseHandlers, Set, Map, etc.

So baseHandlers:

export const mutableHandlers: ProxyHandler<object> = {
  get.set,
  deleteProperty,
  has,
  ownKeys,
};
Copy the code

Vue proxies for these operations, so let’s look at what each operation does:

function get(target: object, key: string | symbol, receiver: object) {
  / /... Cargo foot of internal key

  // Some special handling of arrays
  const targetIsArray = isArray(target);
  if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
    return Reflect.get(arrayInstrumentations, key, receiver);
  }

  // Get the request value
  const res = Reflect.get(target, key, receiver);

  / /... If it is an internal value fetch, return res directly

  if(! isReadonly) { track(target, TrackOpTypes.GET, key); }// Return some processing

  return res;
}
Copy the code

Track (target, trackoptypes.get, key); This is the code we normally execute when we get the value:

export function track(target: object, type: TrackOpTypes, key: unknown) {
  if(! shouldTrack || activeEffect ===undefined) {
    return;
  }
  let depsMap = targetMap.get(target);
  if(! depsMap) { targetMap.set(target, (depsMap =new Map()));
  }
  let dep = depsMap.get(key);
  if(! dep) { depsMap.set(key, (dep =new Set()));
  }
  if(! dep.has(activeEffect)) { dep.add(activeEffect); activeEffect.deps.push(dep);if (__DEV__ && activeEffect.options.onTrack) {
      activeEffect.options.onTrack({
        effect: activeEffect,
        target,
        type, key, }); }}}Copy the code

All right, here’s the point. Let’s go line by line.

The first if is shouldTrack for the environment variable, and if you’ve seen the processComponent section in my source code parsing, you should be in for a surprise now. Because we specifically turned track off when we executed the setup in processComponent, we changed shouldTrack to false.

Let depsMap = targetmap. get(target) this line, targetMap is a map, used to record all response objects. Then if the object is not currently logged, it is logged again.

This target also corresponds to a map, which creates a set for each key.

And finally, what’s the effect? Is the function that is currently calling this object. In our example, it is the render function that returns, which calls state. A when executed, so it enters the proxy. For the proxy of get, the proxy calls track. The activeEffect is the Render method. Effect is that when state.a changes, we need to re-execute the render method to render.

So when was he set up? Instance.update = effect(function componentEffect()) instance.update = effect(function componentEffect()) In the effect method called here, activeEffect is recorded as componentEffect, and this componentEffect runs the Render method.

GetHandler also has an interesting code:

if (isObject(res)) {
  return isReadonly ? readonly(res) : reactive(res);
}
Copy the code

When a property is fetched, it is reactive if the value returned is an object, which is deferred processing, and readonly is not reactive.

OK, so far we know that when we execute the render function, because we call state.a, the function is dependent on state.a, which is called effect in VUe3.

In the next article, we will look at how these effects are invoked when state.a changes.