响应式原理实现(3)vue2和vue3

响应式系统

vue2

watcher,实现回调函数

export default class Watcher {
export default class Watcher {
    constructor(data, expOrFn, cb, options) {
        this.data = data;
        if (typeof expOrFn === "function") {
            this.getter = expOrFn;
        } else {
            this.getter = parsePath(expOrFn);
        }
        ...
        this.cb = cb;
        this.value = this.get(); // 回调函数要用到
    }
   ...
}
}

initWatch函数

// state.js
import Watcher from "./watcher";
import { pushTarget, popTarget } from "./dep";

export function initWatch(data, watch) {
    for (const key in watch) {
        const handler = watch[key];
        createWatcher(data, key, handler);
    }
}

function createWatcher(data, expOrFn, handler) {
    return $watch(data, expOrFn, handler);
}

function $watch(data, expOrFn, handler) {
    new Watcher(data, expOrFn, handler);
}

immediate

import { observe } from "./reactive";
import { initWatch } from "./state";
const options = {
    data: {
        title: "liang",
    },
    watch: {
        title: {
            handler(newVal, oldVal) {
                console.log("收到变化", newVal, oldVal);
            },
            immediate: true,
        },
    },
};
observe(options.data);
initWatch(options.data, options.watch);

options.data.title = "changeTitle";
/**
 * Get the raw type string of a value, e.g., [object Object].
 */
const _toString = Object.prototype.toString;

/**
 * Strict object type check. Only returns true
 * for plain JavaScript objects.
 */
export function isPlainObject(obj) {
    return _toString.call(obj) === "[object Object]";
}

function createWatcher(data, expOrFn, handler, options) {
    // 如果是对象,就将 handler 和 options 分离
    if (isPlainObject(handler)) {
        options = handler;
        handler = handler.handler;
    }
    return $watch(data, expOrFn, handler, options);
}
function $watch(data, expOrFn, handler, options) {
    /******新增 options*************************/
    const watcher = new Watcher(data, expOrFn, handler, options);
  /************************************/
    if (options.immediate) {
        handler.call(data, watcher.value);
    }
    return function unwatchFn() {
        watcher.teardown();
    };
}

computed

  1. 惰性的响应式数据
  2. 处理computed的值
  3. computed属性的响应式
    添加lazy属性和dirty属性,dirty为true表示watcher依赖的属性发生了变化,需要重新求值。dirty为false表示依赖的属性没有发生变化,无需重新求值
export default class Watcher {
    constructor(data, expOrFn, cb, options) {
        this.data = data;
        if (typeof expOrFn === "function") {
            this.getter = expOrFn;
        } else {
            this.getter = parsePath(expOrFn);
        }
        ...
        // options
        if (options) {
            this.deep = !!options.deep;
            this.sync = !!options.sync;
            this.lazy = !!options.lazy;
        }
        this.dirty = this.lazy;
        this.value = this.lazy ? undefined : this.get();
    }

    /**
     * Evaluate the getter, and re-collect dependencies.
     */
    get() {
        pushTarget(this); // 保存包装了当前正在执行的函数的 Watcher
        let value;
        try {
            value = this.getter.call(this.data, this.data);
        } catch (e) {
            throw e;
        } finally {
            // "touch" every property so they are all tracked as
            // dependencies for deep watching
            if (this.deep) {
                traverse(value);
            }
            popTarget();
            this.cleanupDeps();
        }
        return value;
    }

    ...

    /**
     * Evaluate the value of the watcher.
     * This only gets called for lazy watchers.
     */
    /******新增 *************************/
    evaluate() {
        this.value = this.get();
        this.dirty = false; // dirty 为 false 表示当前值已经是最新
    }
		/**********************************/
    update() {
       /******新增 *************************/
        if (this.lazy) {
            this.dirty = true;
          /************************************/
        } else if (this.sync) {
            this.run();
        } else {
            queueWatcher(this);
        }
    }
  
}

输出value之前会执行一次evaluate。
处理computed的值

export function noop(a, b, c) {}

const computedWatcherOptions = { lazy: true };

// computed properties are just getters during SSR
export function initComputed(data, computed) {
    const watchers = (data._computedWatchers = Object.create(null)); // 保存当前所有的 watcher,并且挂在 data 上供后边使用

    for (const key in computed) {
        const userDef = computed[key];
        const getter = typeof userDef === "function" ? userDef : userDef.get; // 如果是对象就取 get 的值
        // create internal watcher for the computed property.
        watchers[key] = new Watcher(
            data,
            getter || noop,
            noop,
            computedWatcherOptions
        );

        // component-defined computed properties are already defined on the
        // component prototype. We only need to define computed properties defined
        // at instantiation here.
        defineComputed(data, key, userDef);
    }
}

defineComputed 是将computed函数定义为data的属性,就可以和正常属性一样使用computed

const sharedPropertyDefinition = {
    enumerable: true,
    configurable: true,
    get: noop,
    set: noop,
};
export function defineComputed(target, key, userDef) {
    // 初始化 get 和 set
    if (typeof userDef === "function") {
        sharedPropertyDefinition.get = createComputedGetter(key);
        sharedPropertyDefinition.set = noop;
    } else {
        sharedPropertyDefinition.get = userDef.get
            ? createComputedGetter(key)
            : noop;
        sharedPropertyDefinition.set = userDef.set || noop;
    }
    // 将当前属性挂到 data 上
    Object.defineProperty(target, key, sharedPropertyDefinition);
}
function createComputedGetter(key) {
    return function computedGetter() {
        const watcher = this._computedWatchers && this._computedWatchers[key]; // 拿到相应的 watcher
        if (watcher) {
            if (watcher.dirty) {
                watcher.evaluate();
            }
            return watcher.value;
        }
    };
}

computed属性的响应式


export default class Watcher {
    constructor(data, expOrFn, cb, options) {
        this.data = data;
        if (typeof expOrFn === "function") {
            this.getter = expOrFn;
        } else {
            this.getter = parsePath(expOrFn);
        }
        this.depIds = new Set(); // 拥有 has 函数可以判断是否存在某个 id
        this.deps = [];
        this.newDeps = []; // 记录新一次的依赖
        this.newDepIds = new Set();
        ...
        this.dirty = this.lazy;
        this.value = this.lazy ? undefined : this.get();
    }

    /**
     * Add a dependency to this directive.
     */
    addDep(dep) {
        const id = dep.id;
        // 新的依赖已经存在的话,同样不需要继续保存
        if (!this.newDepIds.has(id)) {
            this.newDepIds.add(id);
            this.newDeps.push(dep);
            if (!this.depIds.has(id)) {
                dep.addSub(this);
            }
        }
    }
    /**
     * Evaluate the value of the watcher.
     * This only gets called for lazy watchers.
     */
    evaluate() {
        this.value = this.get();
        this.dirty = false;
    }
  	/******新增 *************************/
    /**
     * Depend on all deps collected by this watcher.
     */
    depend() {
        let i = this.deps.length;
        while (i--) {
            this.deps[i].depend();
        }
    }
   /************************************/
}
function createComputedGetter(key) {
    return function computedGetter() {
        const watcher = this._computedWatchers && this._computedWatchers[key];
        if (watcher) {
            if (watcher.dirty) {
                watcher.evaluate();
            }
            if (Dep.target) {
                watcher.depend();
            }
            return watcher.value;
        }
    };
}

vue3

vue3中的nextick放到后面渲染的时候讲。

export function effect<T = any>(
  fn: () => T,
  options?: ReactiveEffectOptions,
): ReactiveEffectRunner {
  // fn嵌套了effect => effect(() => {effect(fn)})
  if ((fn as ReactiveEffectRunner).effect instanceof ReactiveEffect) {
    fn = (fn as ReactiveEffectRunner).effect.fn
  }
  // 实例化ReactiveEffect
  const _effect = new ReactiveEffect(fn, NOOP, () => {
    if (_effect.dirty) {
      _effect.run()
    }
  })
  if (options) {
    // 把option浅拷贝到_effect身上
    extend(_effect, options)
    // 记录effect作用域
    if (options.scope) recordEffectScope(_effect, options.scope)
  }
  if (!options || !options.lazy) {
    // 立即执行effect.run()
    // 这就是定义一个effect传入的副作用函数会立即执行
    _effect.run()
  }
  // 通过bind改变_effect的this指向,返回函数
  const runner = _effect.run.bind(_effect) as ReactiveEffectRunner
  // runnner的effect赋值为_effect
  runner.effect = _effect
  return runner
}
export class ReactiveEffect<T = any> {
  active = true // 标记为激活状态
  deps: Dep[] = [] // 保存了该reactiveEffect所依赖的所有响应式对象的dep对象
  computed?: ComputedRefImpl<T>
  allowRecurse?: boolean
  onStop?: () => void
  onTrack?: (event: DebuggerEvent) => void
  onTrigger?: (event: DebuggerEvent) => void
  _dirtyLevel = DirtyLevels.Dirty
  _trackId = 0
  _runnings = 0
  _shouldSchedule = false
  _depsLength = 0
  constructor(
    public fn: () => T,
    public trigger: () => void,
    public scheduler?: EffectScheduler,
    scope?: EffectScope, // 这个在组件那块用
  ) {
    // 该副作用所属的作用域
    recordEffectScope(this, scope)
  }
  // run函数: lazy为false直接调的_effect.run
  run() {
    this._dirtyLevel = DirtyLevels.NotDirty
    // 非激活的情况下只需要执行函数,不需要收集依赖。
    if (!this.active) {
      return this.fn()
    }
    // shouldTrack为全局变量,当前副作用是否需要被追踪
    let lastShouldTrack = shouldTrack
    let lastEffect = activeEffect

    try {
      shouldTrack = true
      activeEffect = this
      this._runnings++
      preCleanupEffect(this)
      return this.fn()
    } finally {
      postCleanupEffect(this)
      this._runnings--
      activeEffect = lastEffect
      shouldTrack = lastShouldTrack
    }
  }

  stop() {
    if (this.active) {
      preCleanupEffect(this)
      postCleanupEffect(this)
      this.onStop && this.onStop()
      this.active = false
    }
  }
}

上面的代码是effect的核心。
接下来看watch的实现,首先看一下使用

watch(x, (newValue, oldValue) => {
    console.log(`x is ${newValue}`)
})
watch(() => {
    () => x.value + y.value
    (newValue, oldValue) => {
        console.log(`sum of x + y is: ${newValue}`)
    }
})
watch([x, () => y.value], ([newX, newY]) => {
    console.log(`x is ${newX} and y is ${newY}`)
})

第一个参数: ref(包括计算属性) 响应式对象 getter函数 多个数据源组成的数组
第二个参数: 发生变化的时候的回调函数,接受三个参数,新值,旧值,以及用来注册副作用清理的回调函数
第三个参数: immediate: 创建的时候立即触发回调。deep: 在深层级变更的时候触发回调。flush: 调整回调函数的刷新时机。onTrack/onTrigger:调试侦听器的依赖。
watch本质上是利用了副作用函数重新执行时的可调度性,一个watch本身会创建一个effect,当这个effect依赖的响应式数据发生变化的时候,会执行该effect的调度函数,即scheduler

export function watch<T = any, Immediate extends Readonly<boolean> = false>(
  source: T | WatchSource<T>,
  cb: any,
  options?: WatchOptions<Immediate>,
): WatchStopHandle {
  if (__DEV__ && !isFunction(cb)) {
    warn(
      `\`watch(fn, options?)\` signature has been moved to a separate API. ` +
        `Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` +
        `supports \`watch(source, cb, options?) signature.`,
    )
  }
  return doWatch(source as any, cb, options)
}
export function watch<T = any, Immediate extends Readonly<boolean> = false>(
  source: T | WatchSource<T>,
  cb: any,
  options?: WatchOptions<Immediate>,
): WatchStopHandle {
  if (__DEV__ && !isFunction(cb)) {
    warn(
      `\`watch(fn, options?)\` signature has been moved to a separate API. ` +
        `Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` +
        `supports \`watch(source, cb, options?) signature.`,
    )
  }
  return doWatch(source as any, cb, options)
}
function doWatch(
  source: WatchSource | WatchSource[] | WatchEffect | object,
  cb: WatchCallback | null,
  {
    immediate,
    deep,
    flush,
    once,
    onTrack,
    onTrigger,
  }: WatchOptions = EMPTY_OBJ,
): WatchStopHandle { 
  if (cb && once) {
    const _cb = cb
    cb = (...args) => {
      _cb(...args)
      unwatch()
    }
  }

  const instance = currentInstance
  const reactiveGetter = (source: object) =>
    deep === true
      ? source // traverse will happen in wrapped getter below
      : // for deep: false, only traverse root-level properties
        traverse(source, deep === false ? 1 : undefined)
  let getter: () => any
  let forceTrigger = false // 强制触发副作用函数执行
  let isMultiSource = false // 侦听的是否为多个源
  // 如果source是ref对象,则创建source.value的getter函数
  if (isRef(source)) {
    getter = () => source.value
    // 判断数据源是否为浅响应
    forceTrigger = isShallow(source)
  } else if (isReactive(source)) {
    getter = () => reactiveGetter(source)
    forceTrigger = true
  } else if (isArray(source)) {
    isMultiSource = true
    forceTrigger = source.some(s => isReactive(s) || isShallow(s))
    getter = () =>
      source.map(s => {
        if (isRef(s)) {
          return s.value
        } else if (isReactive(s)) {
          return reactiveGetter(s)
        } else if (isFunction(s)) {
          return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
        } else {
          __DEV__ && warnInvalidSource(s)
        }
      })
  } else if (isFunction(source)) {
    if (cb) {
      // 有传入,处理的是watch的情况
      getter = () =>
        callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
    } else {
      // 没有传入,处理的是watcheffect的情况
      getter = () => {
        if (cleanup) {
          cleanup()
        }
        return callWithAsyncErrorHandling(
          source,
          instance,
          ErrorCodes.WATCH_CALLBACK,
          [onCleanup],
        )
      }
    }
  } else {
    getter = NOOP
    __DEV__ && warnInvalidSource(source)
  }

  // 2.x array mutation watch compat
  if (__COMPAT__ && cb && !deep) {
    const baseGetter = getter
    getter = () => {
      const val = baseGetter()
      if (
        isArray(val) &&
        checkCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance)
      ) {
        traverse(val)
      }
      return val
    }
  }
 // 处理的是watch的场景
 // 递归读取对象的属性值, 相应书数据,需要递归读取数据源中的每个属性
  if (cb && deep) {
    const baseGetter = getter
    getter = () => traverse(baseGetter())
  }
 // 清除富国用函数
  let cleanup: (() => void) | undefined
  let onCleanup: OnCleanup = (fn: () => void) => {
    cleanup = effect.onStop = () => {
      callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP)
      cleanup = effect.onStop = undefined
    }
  }


  let oldValue: any = isMultiSource
    ? new Array((source as []).length).fill(INITIAL_WATCHER_VALUE)
    : INITIAL_WATCHER_VALUE
  const job: SchedulerJob = () => {
    if (!effect.active || !effect.dirty) {
      return
    }
    if (cb) {
      // watch(source, cb)
      const newValue = effect.run()
      // 如果侦听的数据源是响应式数据,需要深度侦听,则deep为true
      // 如果需要强制触发副作用函数执行,则forceTrigger为true
      // 如果新旧值发生了变化
      if (
        deep ||
        forceTrigger ||
        (isMultiSource
          ? (newValue as any[]).some((v, i) => hasChanged(v, oldValue[i]))
          : hasChanged(newValue, oldValue)) ||
        (__COMPAT__ &&
          isArray(newValue) &&
          isCompatEnabled(DeprecationTypes.WATCH_ARRAY, instance))
      ) {
        // cleanup before running cb again
        if (cleanup) {
          cleanup()
        }
        // 传入这个函数,在执行完成之后更新新旧值
        callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
          newValue,
          // pass undefined as the old value when it's changed for the first time
          oldValue === INITIAL_WATCHER_VALUE
            ? undefined
            : isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE
              ? []
              : oldValue,
          onCleanup,
        ])
        oldValue = newValue
      }
    } else {
      // watchEffect
      effect.run()
    }
  }
  // 让调度任务作为侦听器的回调,这样调度器就知道允许自己派发更新
  job.allowRecurse = !!cb

  let scheduler: EffectScheduler
  // sync 表示同步的watcher
  if (flush === 'sync') {
    scheduler = job as any // the scheduler function gets called directly
  } else if (flush === 'post') {
    // 放到微任务队列中,等待dom更行结束后执行
    scheduler = () => queuePostRenderEffect(job, instance && instance.suspense)
  } else {
    // 调度器函数默认的执行方式,在组件更新之前执行,如果组件还没有挂载,则在组件挂载之前同步执行回调函数
    job.pre = true
    if (instance) job.id = instance.uid
    scheduler = () => queueJob(job)
  }
  // 初始化getter函数和调度器函数scheduler后, 调用reactiveeffect
  const effect = new ReactiveEffect(getter, NOOP, scheduler)
  const scope = getCurrentScope()
  const unwatch = () => {
    effect.stop()
    if (scope) {
      remove(scope.effects, effect)
    }
  }
  if (__DEV__) {
    effect.onTrack = onTrack
    effect.onTrigger = onTrigger
  }
  // initial run
  if (cb) {
    if (immediate) {
      job()
    } else {
      oldValue = effect.run()
    }
  } else if (flush === 'post') {
    queuePostRenderEffect(
      effect.run.bind(effect),
      instance && instance.suspense,
    )
  } else {
    effect.run()
  }
  if (__SSR__ && ssrCleanup) ssrCleanup.push(unwatch)
  return unwatch
}
export function traverse(
  value: unknown,
  depth = Infinity,
  seen?: Set<unknown>,
) {
  if (depth <= 0 || !isObject(value) || (value as any)[ReactiveFlags.SKIP]) {
    return value
  }

  seen = seen || new Set()
  if (seen.has(value)) {
    return value
  }
  seen.add(value)
  depth--
  if (isRef(value)) {
    traverse(value.value, depth, seen)
  } else if (isArray(value)) {
    for (let i = 0; i < value.length; i++) {
      traverse(value[i], depth, seen)
    }
  } else if (isSet(value) || isMap(value)) {
    value.forEach((v: any) => {
      traverse(v, depth, seen)
    })
  } else if (isPlainObject(value)) {
    for (const key in value) {
      traverse(value[key], depth, seen)
    }
    for (const key of Object.getOwnPropertySymbols(value)) {
      if (Object.prototype.propertyIsEnumerable.call(value, key)) {
        traverse(value[key as any], depth, seen)
      }
    }
  }
  return value
}

原理类似于

function watch(souce,callBack,options = {}) {
        let getter
        if(typeof souce === 'function') {
           getter =  souce
        }else {
           getter = ()=> traverse(souce)
        }
        let newVal,oldVal;
        let work = ()=>{
            //调用effectFn,得到新的newVal
             newVal =  effectFn()
            //当响应式数据变化时候,会执行回调函数callBack()
             callBack(newVal,oldVal)
             //新值替换旧值,当作旧值。
             oldVal = newVal
        }
        //我们对传入的对象souce进行循环遍历,把所有属性进行监听
       const effectFn = effect(()=>getter(),{
        lazy:true//lazy 是懒执行effect
        scheduler:work
       }) 
       if(options.immediate) {
           work()
       }else{
           //我们自己触发点一次effect调用,它肯定优先effect的执行。因为只有当响应式数据变化时候,才会执行effectFn。这是我们手动执行,得到一个初试值oldVal。
          oldVal =  effectFn()
       }
 }
export function triggerEffects(
  dep: Dep,
  dirtyLevel: DirtyLevels,
  debuggerEventExtraInfo?: DebuggerEventExtraInfo,
) {
  pauseScheduling()
  for (const effect of dep.keys()) {
    // dep.get(effect) is very expensive, we need to calculate it lazily and reuse the result
    let tracking: boolean | undefined
    if (
      effect._dirtyLevel < dirtyLevel &&
      (tracking ??= dep.get(effect) === effect._trackId)
    ) {
      effect._shouldSchedule ||= effect._dirtyLevel === DirtyLevels.NotDirty
      effect._dirtyLevel = dirtyLevel
    }
    if (
      effect._shouldSchedule &&
      (tracking ??= dep.get(effect) === effect._trackId)
    ) {
      if (__DEV__) {
        // eslint-disable-next-line no-restricted-syntax
        effect.onTrigger?.(extend({ effect }, debuggerEventExtraInfo))
      }
      effect.trigger()
      if (
        (!effect._runnings || effect.allowRecurse) &&
        effect._dirtyLevel !== DirtyLevels.MaybeDirty_ComputedSideEffect
      ) {
        effect._shouldSchedule = false
        if (effect.scheduler) {
          queueEffectSchedulers.push(effect.scheduler)
        }
      }
    }
  }
  resetScheduling()
}
export function pauseScheduling() {
  pauseScheduleStack++
}
export function resetScheduling() {
  pauseScheduleStack--
  while (!pauseScheduleStack && queueEffectSchedulers.length) {
    queueEffectSchedulers.shift()!()
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值