Composition API 怎麼編寫

下面 先從setup Fuction 開始編寫Compositon API.

 

1⃣️ setup

 

新的 setup 組件選項 在 創建組件之前執行,一旦 props 被解析,並充當合成 API 的入口點。

我們從 setup 返回的所有內容 都將暴露給組件的其餘部分 (計算屬性、方法、生命週期鉤子等等) 以及組件的模板。

 

⚠️ setup執行時間:The setup executes before any of the following options are evaluated:( Components、 Props 、Data、Methods、Computed Properties、Lifecycle methods)

⚠️ 由於在執行 setup 時,尚未創建組件實例,因此在 setup 選項中沒有 this。這意味着,除了 props 之外,你將無法訪問組件中聲明的任何屬性——本地狀態、計算屬性或方法。與其他Component選項不同,setup該方法無權訪問“ this”。爲了獲得對屬性的訪問權限(之前用this獲取的),setup具有兩個可選參數。

  

a、參數

第一個是props,例如:

// src/components/UserRepositories.vue

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: { type: String }
  },
  setup(props) {
    console.log(props) // { user: '' }

    return {} // 這裏返回的任何內容都可以用於組件的其餘部分
  }
  // 組件的“其餘部分”
}

 ⚠️    setup 函數中的 props 是響應式的,當傳入新的 prop 時,它將被更新。

        但是,因爲 props 是響應式的,你不能使用 ES6 解構,因爲它會消除 prop 的響應性。

        如果需要解構 prop,可以通過使用 setup 函數中的 toRefs 來安全地完成此操作。

// MyBook.vue

import { toRefs } from 'vue'

setup(props) {
    const { title } = toRefs(props)

    console.log(title.value)
}

 

 

第二個參數是context,它可以訪問一堆有用的數據:

// MyBook.vue

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

    // 插槽 (非響應式對象)
    console.log(context.slots)

    // 觸發事件 (方法)
    console.log(context.emit)
    context.parent;
    context.root;
  } 
}
context 是一個普通的 JavaScript 對象,也就是說,它不是響應式的,這意味着可以安全地對 context 使用 ES6 解構。

// MyBook.vue
export default {
  setup(props, { attrs, slots, emit }) { //????attrs 和 slots 是有狀態的對象,它們總是會隨組件本身的更新而更新。這意味着你應該避免對它們進行解構,並始終以 attrs.x 或 slots.x 的方式引用 property。請注意,與 props 不同,attrs 和 slots 是非響應式的。如果你打算根據 attrs 或 slots 更改應用副作用,那麼應該在 onUpdated 生命週期鉤子中執行此操作
    ...
  }
}

 

b、訪問組件的property

 

執行 setup 時,組件實例尚未被創建。因此,你只能訪問以下 property:props、attrs、slots、emit

換句話說,你將無法訪問以下組件選項:data 、computed、methods

 

c、結合模板使用

如果 setup 返回一個對象,則可以在組件的模板中  像傳遞給 setup 的 props property 一樣  訪問該對象的 property:

⚠️ 注意,從 setup 返回的 refs 在模板中訪問時是被自動解開的,因此不應在模板中使用 .value

<template>
  <div>{{ readersNumber }} {{ book.title }}</div>
</template>

<script>
  import { ref, reactive } from 'vue'
  export default {
    setup() {
      const readersNumber = ref(0)
      const book = reactive({ title: 'Vue 3 Guide' })

      // expose to template
      return {
        readersNumber,
        book
      }
    }
  }
</script>

 

d、使用渲染函數

 setup 還可以返回一個渲染函數,該函數可以直接使用  在同一作用域中聲明的響應式狀態:

import { h, ref, reactive } from 'vue'

export default {
  setup() {
    const readersNumber = ref(0)
    const book = reactive({ title: 'Vue 3 Guide' })
    // Please note that we need to explicitly expose ref value here
    return () => h('div', [readersNumber.value, book.title])
  }
}

 

e、使用this

在 setup() 內部,this 不會是該活躍實例的引用

因爲 setup() 是在解析其它組件選項之前被調用的,所以 setup() 內部的 this 的行爲與其它選項中的 this 完全不同。

這在和其它選項式 API 一起使用 setup() 時可能會導致混淆。

 

2⃣️、ref函數  & 模版引用


a、ref函數
<template>
  <div>Capacity: {{ capacity }}</div>
</template>
<script>
import { ref } from "vue";
export default {
  setup() {
    const capacity = ref(3); // 基本上,它將原始整數(3)包裝在一個對象中,這將使我們能夠跟蹤更改。以前data()選項 已經將我們的原始(容量)包裝在一個對象內。
    return { capacity }; //最後一步,我們需要顯式返回一個對象,該對象的屬性需要模板正確呈現。這個返回的對象是我們如何在中公開需要訪問哪些數據的方法renderContext。像這樣明確表示有點冗長,但這也是故意的。它有助於長期維護,因爲我們可以控制暴露給模板的內容,並跟蹤定義模板屬性的位置。 
  } 
}; 

</script>

 

b、模版引用

 

作爲模板使用的 ref 的行爲與任何其他 ref 一樣:它們是 響應式的,可以傳遞到 (或從中返回) 複合函數中。

 

爲了獲得 對模板內元素或組件實例的引用,我們可以像往常一樣聲明 ref 並從 setup() 返回。

<template> 
  <div ref="root">This is a root element</div>
</template>

<script>
  import { ref, onMounted } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      onMounted(() => {
        // DOM元素將在初始渲染後分配給ref
        console.log(root.value) // <div>這是根元素</div>
      })

      return {
        root
      }
    }
  }
</script>

 

 

  JSX 中的用法 、v-for 中的用法

  具體地址:https://vue3js.cn/docs/zh/guide/composition-api-template-refs.html

 

3⃣️、provide/inject

通常父傳遞給子組件,可用 props。 但是當有深層結構的傳遞,props就滿足不了要求,可以讓父組件provide, 子(孫子)組件inject.

可以看成“ long range props”。

位置:兩者都只能在當前活動實例的 setup() 期間調用。

 

vue2中👇

app.component('todo-list', {
  data() {
    return {
      todos: ['Feed a cat', 'Buy tickets']
    }
  },
  provide() {

//情況1
return { todoLength: this.todos.length //⚠️ 要訪問 組件實例 property,我們需要將 provide 轉換爲 返回對象的函數。 }

//情況2
    return {
      todoLength: Vue.computed(() => this.todos.length) //如果我們想 對祖先組件中的更改 做出反應。
    }
  },
  template: `
    ...
  `
})

vue3中👇

<!-- src/components/MyMap.vue -->
<template>
  <MyMarker />
</template>

<script>
import { provide, reactive, readonly, ref } from 'vue'
import MyMarker from './MyMarker.vue

export default {
  components: {
    MyMarker
  },
  setup() {
    const location = ref('North Pole') //爲了使 依賴值和注入值 產生 響應性,在提供值時要使用 reactive,ref 函數.
    const geolocation = reactive({
      longitude: 90,
      latitude: 135
    })

    const updateLocation = () => {
      location.value = 'South Pole' //有時我們需要在 注入數據的組件內部更新注入的數據。在這種情況下,我們建議 提供一個方法來負責改變響應式 property
    }

    provide('location', readonly(location)) // provide倆參數,第一個是name(必填),第二個是默認值(可選)。
    provide('geolocation', readonly(geolocation)) // 確保通過provide 傳遞的數據不會被 注入的組件更改,
    provide('updateLocation', updateLocation)
  }
}
</script>

 

//使用注入
<script>
import { inject } from 'vue'
export default {
  setup() {
     const userLocation = inject('location', 'The Universe') //顯式導入,inject函數倆參數:要注入的property的名稱(必填);一個默認的值(可選)
     const userGeolocation = inject('geolocation') 
const updateUserLocation = inject('updateLocation')
     return {
userLocation, userGeolocation,
updateUserLocation
      } 
}
}
</script>


文檔地址:https://vue3js.cn/docs/zh/guide/component-provide-inject.html

 

3⃣️、setup中的鉤子

組合式 API 上的生命週期鉤子與選項式 API 的名稱相同,但前綴爲 on:即 mounted 看起來像 onMounted

 

4⃣️、watch 響應式更改

 

它接受 3 個參數:

  • 一個響應式引用 或 我們想要偵聽的 getter 函數
  • 一個回調
  • 可選的配置選項
import { ref, watch } from 'vue'

const counter = ref(0)
watch(counter, (newValue, oldValue) => {
  console.log('The new counter value is: ' + counter.value)
})
//每當 counter 被修改時 counter.value=5,watch 將觸發並執行回調 (第二個參數),在本例中,它將把 'The new counter value is:5' 記錄到我們的控制檯中

 

5⃣️、toRefs

 

 toRefs 是 爲了確保 偵聽器能夠對 user prop 所做的更改 做出反應。

 

 

  toRefs 是 爲了確保 偵聽器能夠對 user prop 所做的更改 做出反應。

 

6⃣️、獨立的 computed 屬性

與 ref 和 watch 類似,也可以使用 從 Vue 導入的 computed 函數,在 Vue 組件外部創建計算屬性。

computed 函數返回一個作爲 computed 的第一個參數傳遞的 getter 類回調的輸出的一個只讀的響應式引用。

爲了訪問新創建的計算變量的 value,我們需要像使用 ref 一樣使用 .value property。

 

import { ref, computed } from 'vue'

const counter = ref(0)
const twiceTheCounter = computed(() => counter.value * 2)

counter.value++
console.log(counter.value) // 1
console.log(twiceTheCounter.value) // 2

 

7⃣️、組合式函數

(現在只有兩個功能,獲取用戶數據➕搜索功能。對於其他的邏輯關注點我們也可以這樣做,setup隨着功能的增多,會越來越大👇)

// src/components/UserRepositories.vue `setup` function
import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch, toRefs, computed } from 'vue'

// in our component
setup (props) {
  // 使用 `toRefs` 創建對 props 的 `user` property 的響應式引用
  const { user } = toRefs(props)
  const repositories = ref([])

const getUserRepositories = async () => { // 更新 `props.user ` 到 `user.value` 訪問引用值 repositories.value = await fetchUserRepositories(user.value) } onMounted(getUserRepositories) // 在用戶 prop 的響應式引用上設置一個偵聽器 watch(user, getUserRepositories)

const searchQuery = ref('') const repositoriesMatchingSearchQuery = computed(() => { return repositories.value.filter( repository => repository.name.includes(searchQuery.value) ) }) return { repositories, getUserRepositories, searchQuery, repositoriesMatchingSearchQuery } }

所以要先將一個功能邏輯的代碼提取到一個獨立的組合式函數(單獨的文件),然後再調用👇。

// 例 :🌰 創建一個獨立的組合式函數->  useUserRepositories 

// src/composables/useUserRepositories.js

import { fetchUserRepositories } from '@/api/repositories'
import { ref, onMounted, watch } from 'vue'

export default function useUserRepositories(user) {
  const repositories = ref([])
  const getUserRepositories = async () => {
    repositories.value = await fetchUserRepositories(user.value)
  }

  onMounted(getUserRepositories)
  watch(user, getUserRepositories)

  return {
    repositories,
    getUserRepositories
  }
}

如何使用該函數 👇


// src/components/UserRepositories.vue
import { toRefs } from 'vue'
import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import useRepositoryFilters from '@/composables/useRepositoryFilters'

export default {

components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
    props: { user: { type: String } }, 

setup(props) {

const { user } = toRefs(props)
const { repositories, getUserRepositories } = useUserRepositories(user) const { searchQuery,repositoriesMatchingSearchQuery } = useRepositoryNameSearch(repositories)
const { filters, updateFilters, filteredRepositories } = useRepositoryFilters(repositoriesMatchingSearchQuery)
return { // 因爲我們並不關心未經過濾的倉庫 // 我們可以在 `repositories` 名稱下暴露過濾後的結果 repositories: filteredRepositories, getUserRepositories, searchQuery, filters, updateFilters } } }

 

總結: 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章