vue3.0中的computed寫法

vue3.0 computed寫法

用computed計算屬性之前別忘瞭引入

使用完畢別忘瞭導出

computed裡面還有兩個方法:get和set

  • get方法:

結果:

get方法是讀取數據時候調用的,監測到數據變化以後就自動執行:

結果:

點擊以後:

說明get在程序運行時自動調用,後面監測到數據變化就再次調用

我們改一下代碼:

點擊前結果:

點擊後結果:

所以我們能得出: 點擊button以後,執行到plusOne.value = 2時候,相當於調用瞭:

// 其中參數val就是修改的值2
set: val => {
        console.log('executing function set')
        return count.value = val - 1
      }

所以現在count的值為2-1=1,這樣的值改變引起set方法的註意,被監測到瞭,所以繼續執行:

get: () => {
        console.log('executing function get')
        return count.value + 1
      }

這樣count的值變成1+1 = 2

所以最後顯示2

vue3.0 computed的使用及其註意點

為什麼使用

當我們不想在template的html元素上寫很長的js邏輯時,我們就會想使用計算屬性computed

使用

computed

傳入一個 getter 函數,返回一個默認不可手動修改的 ref 對象。

取自官網:https://vue3js.cn/reactivity/computed.html#computed

const count = ref(1)
const plusOne = computed(() => count.value + 1)

console.log(plusOne.value) // 2

plusOne.value++ // 錯誤!
// 或者傳入一個擁有 get 和 set 函數的對象,創建一個可手動修改的計算狀態。

const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  },
})

plusOne.value = 1
console.log(count.value) // 0
// 更多文檔: https://vue3js.cn/vue-composition-api/#computed

正文

計算屬性,可能會依賴其他 reactive 的值,同時會延遲和緩存計算值

export function computed<T>(
  getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
  let getter: ComputedGetter<T>
  let setter: ComputedSetter<T>

  // 如果傳入是 function 說明是隻讀 computed
  if (isFunction(getterOrOptions)) {
    getter = getterOrOptions
    setter = __DEV__
      ? () => {
          console.warn('Write operation failed: computed value is readonly')
        }
      : NOOP
  } else {
    // 不是方法說明是自定義的 getter setter 
    getter = getterOrOptions.get
    setter = getterOrOptions.set
  }

  let dirty = true
  let value: T
  let computed: ComputedRef<T>

  // 創建 effect, 我們在看 effect 源碼時知道瞭傳入 lazy 代表不會立即執行,computed 表明 computed 上遊依賴改變的時候,會優先 trigger runner effect, scheduler 表示 effect trigger 的時候會調用 scheduler 而不是直接調用 effect
  const runner = effect(getter, {
    lazy: true,
    // mark effect as computed so that it gets priority during trigger
    computed: true,
    scheduler: () => {
      // 在觸發更新時把dirty置為true, 不會立即更新 
      if (!dirty) {
        dirty = true
        trigger(computed, TriggerOpTypes.SET, 'value')
      }
    }
  })

  // 構造一個 computed 返回
  computed = {
    __v_isRef: true,
    // expose effect so computed can be stopped
    effect: runner,
    get value() {
      // dirty為ture, get操作時,執行effect獲取最新值
      // 
      if (dirty) {
        value = runner()
        dirty = false
      }
      // dirty為false, 表示值未更新,直接返回 
      track(computed, TrackOpTypes.GET, 'value')
      return value
    },
    set value(newValue: T) {
      setter(newValue)
    }
  } as any
  return computed
}

註意事項

如果存在計算屬性依賴其他異步或者props傳進的屬性,再通過state.xxx獲取最新屬性值進行判斷,那麼就必須寫在getter回調裡,例如:

const haveFile = computed(() => state.jobInfo.fileName ? state.jobInfo.fileName.includes('http') : false);
const permitProduct = computed(() => !state.isPower || state.isDqcFinish);
const permitDownl = computed(() => !state.isPower || !haveFile.value);

我們可以看到permitProduct 和permitDownl 都依賴state.isPower這個變量值,且該變量寫在watch監聽中,隨時可能改變,因此,我們console.log寫在permitDownl 變量的下一行,可能看不到最新結果的打印:

const haveFile = computed(() => state.jobInfo.fileName ? state.jobInfo.fileName.includes('http') : false);
const permitProduct = computed(() => !state.isPower || state.isDqcFinish);
const permitDownl = computed(() => !state.isPower || !haveFile.value);
console.log('state.isPower ===>', state.isPower);

正確的做法,應該是寫在getter回調裡:

const haveFile = computed(() => state.jobInfo.fileName ? state.jobInfo.fileName.includes('http') : false);
const permitProduct = computed(() => !state.isPower || state.isDqcFinish);
const permitDownl = computed(() => {
    console.log('state.isPower ===>', state.isPower); // 此時可以獲得最新值
    return !state.isPower || !haveFile.value;
});

並且要註意的一點是,如果當前計算屬性依賴其他計算屬性,例如上面所寫的permitDownl 依賴於 haveFile,這時,寫法就必須是haveFile.value而非haveFile

計算屬性獲取值在非template區域,而是在js區域內,則必須使用propname.value去獲取值! 

總結

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: