本文是我新开的坑的第一篇文章,这个坑就是vue3,接下来我会围绕着vue3进行一系列的动作,包含但不限于:

  • 残缺的源码解析
  • jsx工程最佳实际
  • 个性化的jsx计划
  • vue3生态库开发(目前有一个正在进行)
  • 以及可能的自定义一个vue3的runtime

对于源码解析,网站曾经上线,vue3源码解析,最佳实际,网站是逐行代码模式的解析,更多关注于源码,而在掘金上分享的文章则相似于总结,会用更复合一篇文章的构造来写。如果你想继续跟进vue3源码,能够关上后面的网站关注我。

那么,开始!

vue3最大的变动莫过于其对于响应式原理的重构,以及其新公布的composition api,本文聚焦于前者,来深度分析一下vue3中响应式到底是怎么实现的。

咱们以reactiveAPI 为例,

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

咱们看下面的例子,这个例子很简略,创立了一个组件,他有一个响应式的数据对象,而后render外面的input的value绑定的是state.a以及他的onChange会批改state.a。这是非常简单且直观的一个数据绑定的例子,而这个逻辑能实现的根本原因,是咱们在调用state.a = xxx的时候,vue会从新渲染咱们return的render函数,来更新节点

而篇文章就是要来看一下,咱们通过reactive创立的对象,到底有什么魔力,可能帮咱们实现这个工作。

其实自身 API 是很简略的,传入一个对象,返回一个 reactive 对象,创立的办法是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  );}
function createReactiveObject(  target: Target,  isReadonly: boolean,  baseHandlers: ProxyHandler<any>,  collectionHandlers: ProxyHandler<any>) {  // 后面都是一些对象是否曾经proxy等的判断逻辑,这里就不展现了  const observed = new Proxy(    target,    collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers  );  def(    target,    isReadonly ? ReactiveFlags.READONLY : ReactiveFlags.REACTIVE,    observed  );  return observed;}

那么这里最重要的就是new Proxy了,能够说了解 vue3 的响应式原理过程就是了解这个proxy创立的过程,而理解这个过程,次要就是看第二个参数,在这里就是collectionHandlers或者baseHandlers,大部分是后者,前者次要针对,Set、Map 等。

那么咱们就来看baseHandlers

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

可见 vue 代理了这几个操作,那么咱们一个个看这几个操作做了啥:

function get(target: object, key: string | symbol, receiver: object) {  // ...外部key的货足  // 对于数组的一些非凡解决  const targetIsArray = isArray(target);  if (targetIsArray && hasOwn(arrayInstrumentations, key)) {    return Reflect.get(arrayInstrumentations, key, receiver);  }  // 获取申请值  const res = Reflect.get(target, key, receiver);  // ...如果是外部值的获取则间接返回res  if (!isReadonly) {    track(target, TrackOpTypes.GET, key);  }  // 返回的一些解决  return res;}

这里的重点就在于track(target, TrackOpTypes.GET, key);,这个是咱们失常获取值的时候都会执行到的代码:

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,      });    }  }}

好了,重点来了,咱们逐行剖析。

第一个if,就是依据环境变量的shouldTrack来判断是否要进行跟踪,如果你曾经看过我的源码解析中的processComponent的章节,那么你当初应该就是恍然大悟的感觉。因为在执行processComponent外面的setup的时候,咱们顺便敞开了track,而那时候就是把shouldTrack改为了false

let depsMap = targetMap.get(target)这行,targetMap是一个 map,用来记录所有的响应对象。之后如果目前没有记录该对象,那么就从新记录。

这个 target 对应的也是一个 map,他会对每个 key 建设一个 set。

最初要记录这次的 effect 了,这里所谓的effect是什么呢?就是以后正在调用这个对象的函数。在咱们的例子外面,就是return回去的render函数,这个函数在执行的时候会调用state.a所以会进入proxy对于get的代理,这个时候 proxy 就调用了track,那么这时候的activeEffect就是这个 render 办法。这里的effect就是,当state.a改变的时候,咱们须要从新执行该 render 办法来进行渲染。

那么他是什么时候被设置的呢?在mount章节的时候咱们提到了,在执行render办法的时候,咱们执行这样一句代码instance.update = effect(function componentEffect()...),就是在这里调用的effect办法外面,把activeEffect记录为componentEffect,而这个componentEffect外面则运行了render办法。

另外这个getHandler外面有句代码也挺有意思的:

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

在获取属性的时候,如果返回的值是对象的时候,才会对其执行reactive,这也就是提早解决,而且readonly的话是不执行reactive的。

OK,到这里咱们曾经晓得了在执行render函数的时候,因为咱们调用了state.a所以这个函数就相当于依赖state.a,这在vue3外面被称为effect

那么下一篇文章,咱们就来讲一讲,这些effectstate.a变动的时候是如何被调用的,敬请期待。