vue3.x新特性之setup函數,看完就會用了


最近有小伙伴跟我聊起setup函數,因為習慣了vue2.x的寫法導致了,setup用起來覺得奇奇怪怪的,在一些api混編的情況下,代碼變得更加混亂了,個人覺得在工程化思想比較強的團隊中使用setup確實能更好的使用模塊化開發,但是用得不好的話也確實降低了代碼的可讀性。本篇文章是從使用角度來聊聊setup的實際使用。

setup 使用

1、setup和以前的api(data,methods,computed等)並不沖突,也是可以相互訪問的,值得注意的是setup里面不能用this,並且在setup執行的時候組件實例還未創建完畢,故不也能使用data,methods,computed定義的變量和函數。如下混編示例:

import { getCurrentInstance} from 'vue'

setup() {
  const count = 10; // 非響應式的。

  const { proxy } = getCurrentInstance(); // 這里 proxy 相當於this
  console.log(proxy.msg) // 不能在setup調用時訪問data里面的屬性

  return {
    count,
    consoleMsg: () => {
      console.log(proxy.msg) // 123,點擊按鈕時可以訪問了
    }
  }
},
data() {
  return {
    msg: '123'
  }
},
created() {
  console.log(this.count) // 10,這時可以訪問setup拋出的屬性了。
}

2、setup 還可以返回一個渲染函數,不過返回一個渲染函數將阻止我們返回任何其它的東西,當我們想暴漏函數給其父組件使用的時候,可以使用expose來處理這個問題。示例如下:

import { h, ref } from 'vue'

export default {
  setup(props, { expose }) {
    const count = ref(0) // 創建一個響應式的變量
    const increment = () => ++count.value

    expose({
      increment
    })

    return () => h('div', count.value) // 請注意這里我們需要顯式使用 ref 的 value
  }
}

看到這里就完全可以使用setup來做項目了,接下來就是封山開路遇水搭橋,碰到不會的就各種查,磕磕碰碰總能成功。然而一篇帖子不能寫到這里就結束了,后面還有一大堆相關的知識點呢。

響應式

為什么聊響應式呢,因為setup里面返回的變量雖然可以直接在模版語法中使用,但是它並不是響應式的,如上面第一個示例,我們如果在模版中使用了{{ count }}來展示count的值,然后我們改變count的值,值改變了,但是顯示不會變化。

// 非響應式示例,等價於錯誤示例,請盡量不要復制這塊代碼到你項目中去了。
<template>
  <div>
    <span>{{ count }}</span>
    <button @click="count ++; consoleValue(count)">Increment count</button>
  </div>
</template>

<script>
  export default {
    setup() {
      let count = 10; // 這里不是響應式的。
      return {
        count,
        consoleValue: (value) => {
          console.log(value); // 這里值發生了變化
        }
      }
    }
  }
</script>

響應式這塊在官網api比較多,只說幾個用得比較多的。
1、上面有提到的ref,ref 接收參數並將其包裹在一個帶有 value property 的對象中返回,然后可以使用該 property 訪問或更改響應式變量的值。最簡單的例子,上面示例改成響應式的,如下:

<template>
  <div>
    <span>{{ count }}</span>
    <button @click="count ++">Increment count</button>
    <button @click="doubleCount">double count</button>
  </div>
</template>

<script>
  import { ref } from 'vue'

  export default {
    setup() {
      let count = ref(10);
      return {
        count,
        doubleCount: () => {
          // 值得注意在setup里面需要使用.value來訪問
          count.value *= 2;
        }
      }
    }
  }
</script>

2、reactive返回對象的響應式副本。這個比較好理解,跟以前2.x時代差別不大。

<template>
  <div>
    <span>{{ countObj.count }}</span>
    <button @click="countObj.count ++">Increment count</button>
    <button @click="doubleCount">double count</button>
  </div>
</template>

<script>
  import { reactive } from 'vue'

  export default {
    setup() {
      const countObj = reactive({ count: 10 });
      return {
        countObj,
        doubleCount: () => {
          countObj.count *= 2;
        }
      }
    }
  }
</script>

3、toRef 和 toRefs 這兩個函數都是為了獲取一個響應式的子項,並且跟以前的響應式數據進行關聯

<template>
  <div>
    <div>{{ countObj.count }} {{countObj.changeTimes}}</div>
    <div>{{ count }} {{ changeTimes }}</div>
    <button @click="count ++; changeTimes ++">increment count</button>
    <button @click="doubleCount">double count</button>
  </div>
</template>

<script>
  import { reactive, toRefs, toRef } from 'vue'

  export default {
    setup() {
      const countObj = reactive({ count: 10, changeTimes: 0 });
      const { count } = toRefs(countObj);
      const changeTimes = toRef(countObj, 'changeTimes');
      return {
        countObj,
        count,
        changeTimes,
        doubleCount: () => {
          countObj.count *= 2;
          changeTimes.value ++;
        }
      }
    }
  }
</script>

注意:toRefs 只會為源對象中包含的 property 生成 ref。如果要為特定的 property 創建 ref,則應當使用toRef,簡單粗暴的理解toRef可以給源對象添加一個關聯的響應式屬性,如:(本想寫在上面示例中,不過感覺不清晰,就單獨列了一塊偽代碼)

const countObj = reactive({ count: 10 });
const testV = toRef(countObj, 'test');

testV.value = 10;
console.log(countObj.test); // 10

setup 參數

使用 setup 函數時,它將接收兩個參數:
Props
setup 函數中的第一個參數是 props。正如在一個標准組件中所期望的那樣,setup 函數中的 props 是響應式的,當傳入新的 prop 時,它將被更新。

export default {
  props: {
    title: String
  },
  setup(props) {
    console.log(props.title)
  }
}

但是,因為 props 是響應式的,你不能使用 ES6 解構,它會消除 prop 的響應性,上面說到的toRefs可以很好的解決這個問題。

import { toRefs } from 'vue'

setup(props) {
  const { title } = toRefs(props)
  console.log(title.value)
}

如果 title 是可選的 prop,則傳入的 props 中可能沒有 title 。在這種情況下,toRefs 將不會為 title 創建一個 ref 。你需要使用 toRef 替代它:

import { toRef } from 'vue'
setup(props) {
  const title = toRef(props, 'title')
  console.log(title.value)
}

Context
傳遞給 setup 函數的第二個參數是 contextcontext 是一個普通 JavaScript 對象,暴露了其它可能在 setup 中有用的值:

export default {
  setup(props, context) {
    // Attribute (非響應式對象,等同於 $attrs)
    console.log(context.attrs)

    // 插槽 (非響應式對象,等同於 $slots)
    console.log(context.slots)

    // 觸發事件 (方法,等同於 $emit)
    console.log(context.emit)

    // 暴露公共 property (函數)
    console.log(context.expose)
  }
}

setup生命周期鈎子

在setup中可以訪問到以下生命周期鈎子:

  • onBeforeMount
  • onMounted
  • onBeforeUpdate
  • onUpdated
  • onBeforeUnmount
  • onUnmounted
  • onErrorCaptured
  • onRenderTracked
  • onRenderTriggered
  • onActivated
  • onDeactivated

這些函數接受一個回調函數,當鈎子被組件調用時將會被執行,如:

export default {
  setup() {
    // mounted
    onMounted(() => {
      console.log('Component is mounted!')
    })
  }
}

原創不易,轉載請注明出處,歡迎留言提議。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM