沐鳴測速註冊_讓Vue3 Composition API 存在於你 Vue 以外的項目中

作者:陳大魚頭

github: KRISACHAN

作為新特性 Composition API ,在 vue3 正式發布之前一段時間就發布過了。

距文檔介紹, Composition API 是一組低侵入式的、函數式的 API,使得我們能夠更靈活地「組合」組件的邏輯。

不僅在 Vue 中,在其他的框架或原生 js 也可以很好地被使用,下面我們就選取幾個比較重要的 Composition API ,通過一些簡單的例子來看看如何在其他項目中使用。

注:本文僅列出各個分類下比較重要的 API,想要查看全部可以點擊下方鏈接進行查看:https://github.com/vuejs/

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.x 的 Vue.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 是 effect 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 對象。ref 對象擁有一個指向內部值的單一屬性 .value。如果傳入 ref 的是一個對象,將調用 reactive 方法進行深層響應轉換。

示例如下:

const count = ref({
  name: '魚頭',
  type: '帥哥'
})
console.log(count.value.type) // 帥哥
count.value.type = '超級大帥哥'
console.log(count.value.type) // 超級大帥哥

shallowRef

創建一個 ref ,將會追蹤它的 .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

傳入一個 getter 函數,返回一個默認不可手動修改的 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

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9941