Сделайте так, чтобы Vue3 Composition API существовал за пределами вашего проекта Vue.

JavaScript Vue.js
Сделайте так, чтобы Vue3 Composition API существовал за пределами вашего проекта Vue.
  • Автор: Чен Даютоу
  • гитхаб:KRISACHAN

как новая функцияComposition API,существуетVue3Он был выпущен за некоторое время до официального релиза.

Из введения документации,Composition APIпредставляет собой набор малонавязчивых, функциональных API, которые позволяют нам быть более гибкими».комбинация"логика компонента.

не только вVueв других фреймворках или нативныхJSЕго тоже можно очень хорошо использовать.Ниже мы выберем еще несколько важных.Composition API, на нескольких простых примерах, чтобы увидеть, как использовать его в других проектах.

Примечание. В этой статье перечислены только наиболее важные API в каждой категории. Если вы хотите просмотреть их все, вы можете щелкнуть ссылки ниже, чтобы просмотреть их:

GitHub.com/v UE JS/v UE-вы…

reactive API

createReactiveObject

createReactiveObjectфункцияreactive APIЯдро для созданияreactiveобъект .

Прежде чем поделиться API, давайте взглянем на его основную реализацию. Из-за ограниченного места в этой статье показана только упрощенная версия кода после понимания, код выглядит следующим образом:

function createReactiveObject(
  target, // 要监听目标
  isReadonly, // 是否只读
  baseHandlers, // target 为 Object 或 Array 时的处理器,支持对数据的增删改查
  collectionHandlers // target 为 Map/WeakMap 或 Set/WeakSet 时的处理器,支持对数据的增删改查
) {
    if (target[ReactiveFlags.RAW] && !(isReadonly && target[ReactiveFlags.IS_REACTIVE]) {
      // 当 target 已经是一个 Proxy 时,直接返回
      // 例外情况:在 Proxy 里调用 readonly()
    	return target
    }
    // 当前对象已被监听过时,就直接返回被监听的对象
    if (existingProxy) {
      return existingProxy
    }
    // 如果是 Object Array Map/WeakMap Set/WeakSet 以外只能监听到值的数据,直接返回
    if (targetType === TargetType.INVALID) {
      return target
    }
    // 根据参数类型生成对应的 Proxy 对象,以及添加对应的处理器
    const proxy = new Proxy(
      target,
      targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
    )
    proxyMap.set(target, proxy)
    return proxy
}

reactive

Принимает простой объект и возвращает реактивный прокси для этого простого объекта. Эквивалент 2.xVue.observable()

Пример выглядит следующим образом:

import {
  reactive,
  isReactive // 判断是否是 reactive 对象
} from 'https://unpkg.com/@vue/reactivity/dist/reactivity.esm-browser.js'
const obj = {
  nested: {
    foo: 1
  },
  array: [{ bar: 2 }]
}
const value = 10

const observedObj = reactive(obj)
const observedValue = reactive(value)

console.log(isReactive(observedObj)) // true
console.log(isReactive(observedValue)) // true

shallowReactive

Создавайте поверхностный реактивный прокси только для частных (первого уровня) свойств объекта, не делайте глубокий рекурсивный реактивный прокси для «свойств свойства», а просто оставьте его как есть.

Пример выглядит следующим образом:

const obj = {
  nested: {
    foo: 1
  },
  array: [{ bar: 2 }]
}
const value = 10

const unobservedObj = shallowReactive(obj)
const unobservedValue = shallowReactive(value)

console.log(isReactive(observedObj)) // false
console.log(isReactive(observedValue)) // false

effect API

createReactiveEffect

createReactiveEffectЯвляется ли ядром API эффекта, используемый для создания заданных пользователем слушателейreactiveфункция объекта

Прежде чем поделиться API, давайте взглянем на его основную реализацию. Из-за ограниченного места в этой статье показана только упрощенная версия кода после понимания, код выглядит следующим образом:

function createReactiveEffect(
	fn, // 用户创建的 reactive 对象变动执行回调
  options = {
    lazy, // 是否执行用户函数
    scheduler, // 收集数据记录
    onTrack, // 追踪用户数据的回调
    onTrigger, // 追踪变更记录
    onStop, // 停止执行
    allowRecurse // 是否允许递归
  }
) {
    const effect = function reactiveEffect() {
      if (!effectStack.includes(effect)) {
        cleanup(effect) // 清空 effect
        try {
          enableTracking() // 往追踪用户数据的栈内添加当前 effect
          effectStack.push(effect) // 往 effect 栈内添加 effect
          activeEffect = effect // 将活动 effect 变成当前 effect
          return fn() // 执行回调
        } finally { // 删除当前记录
          effectStack.pop()
          resetTracking()
          activeEffect = effectStack[effectStack.length - 1]
        }
      }
    }
		effect.id = uid++
    effect._isEffect = true
    effect.active = true
    effect.raw = fn
    effect.deps = []
    effect.options = options
    return effect
}

effect

effectфункцияeffect APIОсновной. кWeakMapЭто тип данных, который используется для хранения пользовательских функций.подписчик.

Пример выглядит следующим образом:

let dummy
const counter = reactive({ num: 0 })
effect(() => (dummy = counter.num))
console.log(dummy === 0) // true
counter.num = 7
console.log(dummy === 7) // true

ref API

RefImpl

RefImplэто ядро ​​ref API, используемое для созданияrefобъект

Прежде чем поделиться API, давайте взглянем на его основную реализацию.Код выглядит следующим образом:

class RefImpl {
  private _value // 存储当前 ref 对象的值

  public __v_isRef = true // 确定是否为 ref 对象的变量 (只读)

  constructor(
    private _rawValue, // 用户传入的原始值
    public readonly _shallow = false // 当前 ref 对象是否为 shallowRef
  ) {
    // convert:如果传入的原始值为对象,则会被 convert 函数转换为 reactive 对象
    this._value = _shallow ? _rawValue : convert(_rawValue)
  }

  get value() {
    // 用于追踪用户输入的值变化
    // track:effect api 的 track 函数,用于追踪用户行为,当前则是追踪用户的 get 操作
    // toRaw:effect api 的 toRaw 函数,将 this 转化为用户输入值
    track(toRaw(this), TrackOpTypes.GET, 'value')
    return this._value
  }

  set value(newVal) {
    if (hasChanged(toRaw(newVal), this._rawValue)) {
      // 当前 ref 对象有变化时
      // _rawValue / _value 变更
      // trigger:effect api 的 trigger 函数,根据用户传入的值与操作类型来进行操作,当前则为将用户传入的值添加到值 map 里
      this._rawValue = newVal
      this._value = this._shallow ? newVal : convert(newVal)
      trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
    }
  }
}

ref

Принимает значение параметра и возвращает реактивный и изменяемый объект ссылки. Объекты ref имеют одно свойство, указывающее на внутреннее значение.value. Если переданная ссылка является объектом, она вызоветreactiveметод глубокой реактивной трансформации.

Пример выглядит следующим образом:

const count = ref({
  name: '鱼头',
  type: '帅哥'
})
console.log(count.value.type) // 帅哥
count.value.type = '超级大帅哥'
console.log(count.value.type) // 超级大帅哥

shallowRef

Создайте реф, который будет отслеживать его.valueизменить операцию, но это не влияет на измененную.valueВыполняйте реактивные преобразования прокси (т. е. изменения не вызываютreactive)

Пример выглядит следующим образом:

const __shallowRef = shallowRef({ a: 1 })
let dummy
effect(() => {
  dummy = __shallowRef.value.a
})
console.log(dummy) // 1

__shallowRef.value.a = 2
console.log(dummy) // 1
console.log(isReactive(__shallowRef.value)) // false

customRef

customRefдля пользовательскогоref, вы можете явно управлять отслеживанием зависимостей и триггерным ответом, принимать фабричную функцию, для отслеживания используются два параметраtrackИ используется для запуска ответаtriggerИ вернуть сgetа такжеsetсвойства объекта.

Пример выглядит следующим образом:

let value = 1
let _trigger

const custom = customRef((track, trigger) => ({
  get() {
    track()
    return value
  },
  set(newValue) {
    value = newValue
    _trigger = trigger
  }
}))

let dummy
effect(() => {
  dummy = custom.value
})
console.log(dummy) // 1

custom.value = 2
console.log(dummy) // 1

_trigger()
console.log(dummy) // 2

triggerRef

triggerRefдля активного срабатыванияshallowRef

Пример выглядит следующим образом:

const __shallowRef = shallowRef({ a: 1 })
let dummy
effect(() => {
  dummy = __shallowRef.value.a
})
console.log(dummy) // 1

__shallowRef.value.a = 2
console.log(dummy) // 1
console.log(isReactive(__shallowRef.value)) // false

triggerRef(__shallowRef)
console.log(dummy) // 2

computed API

ComputedRefImpl

ComputedRefImplэто ядро ​​ref API, используемое для созданияcomputedобъект

Прежде чем поделиться API, давайте взглянем на его основную реализацию. Из-за ограниченного места в этой статье показана только упрощенная версия кода после понимания, код выглядит следующим образом:

class ComputedRefImpl {
  private _value // 当前值
  private _dirty = true // 当前值是否发生过变更

  public effect // effect 对象

  public __v_isRef = true; // 指定为 ref 对象
  public [ReactiveFlags.IS_READONLY]: boolean // 是否只读

  constructor(
    getter, // getter
    private _setter, // setter
    isReadonly // 是否只读
  ) {
    this.effect = effect(getter, {
      lazy: true,
      scheduler: () => {
        if (!this._dirty) {
          // 将变更状态变为 true
          // trigger:effect api 的 trigger 函数,根据用户传入的值与操作类型来进行操作,当前则为将用户传入的值添加到值 map 里
          this._dirty = true
          trigger(toRaw(this), TriggerOpTypes.SET, 'value')
        }
      }
    })

    this[ReactiveFlags.IS_READONLY] = isReadonly
  }

  get value() {
    if (this._dirty) {
      // 返回当前值
      // 将变更状态变为 false
      this._value = this.effect()
      this._dirty = false
    }
   	// track:effect api 的 track 函数,用于追踪用户行为,当前则是追踪用户的 get 操作
    track(toRaw(this), TrackOpTypes.GET, 'value')
    return this._value
  }

  set value(newValue) {
    this._setter(newValue)
  }
}

computed

Передайте функцию получения, которая возвращает объект ref, который по умолчанию нельзя изменить вручную. или передать в собственностьgetа такжеsetОбъект функции, которая создает изменяемое вручную состояние вычисления.

Пример выглядит следующим образом:

const count1 = ref(1)
const plus1 = computed(() => count1.value + 1)
console.log(plus1.value) // 2
plus1.value++ // Write operation failed: computed value is readonly

const count2 = ref(1)
const plus2 = computed({
  get: () => count2.value + 1,
  set: val => {
    count2.value = val - 1
  }
})
console.log(plus2.value) // 2
plus2.value = 0
console.log(plus2.value) // 0