vue3 - 可復用 & 組合 - 組合式API - 介紹(未完)


原文地址 v3.cn.vuejs.org

# 什么是組合式 API?

如果我們能夠將與同一個邏輯關注點相關的代碼配置在一起,這樣會更好。而這正是組合式 API 使我們能夠做到的。

# 組合式 API 基礎

# setup 組件選項

新的 setup 組件選項在創建組件之前執行,一旦 props 被解析,就作為組合式 API 的入口點。

WARNING

由於在執行 setup 時,組件實例尚未被創建,因此在 setup 選項中沒有 this。這意味着,除了 props 之外,你將無法訪問組件中聲明的任何屬性——本地狀態計算屬性方法

setup 選項應該是一個接受 propscontext 的函數,我們將在稍后討論。

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

    return {}  // 這里返回的任何內容都可以用於組件的其余部分
  }
 
}

#ref 的響應式變量

在 Vue 3.0 中,我們可以通過一個新的 ref 函數使任何響應式變量在任何地方起作用,如下所示:

import { ref } from 'vue'

const counter = ref(0)


1
2
3

ref 接受參數,並將其包裹在一個帶有 value property 的對象中返回,然后可以使用該 property 訪問或更改響應式變量的值:

import { ref } from 'vue'

const counter = ref(0)

console.log(counter) 
console.log(counter.value) 

counter.value++
console.log(counter.value) 


1
2
3
4
5
6
7
8
9

將值封裝在一個對象中,看似沒有必要,但為了保持 JavaScript 中不同數據類型的行為統一,這是必須的。這是因為在 JavaScript 中,NumberString 等基本類型是通過值傳遞的,而不是通過引用傳遞的:

在任何值周圍都有一個封裝對象,這樣我們就可以在整個應用中安全地傳遞它,而不必擔心在某個地方失去它的響應性。

提示

換句話說,ref 為我們的值創建了一個響應式引用。在整個組合式 API 中會經常使用引用的概念。

回到我們的例子,讓我們創建一個響應式的 repositories 變量:

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


setup (props) {
  const repositories = ref([])
  const getUserRepositories = async () => {
    repositories.value = await fetchUserRepositories(props.user)
  }

  return {
    repositories,
    getUserRepositories
  }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

完成!現在,每當我們調用 getUserRepositories 時,repositories 都將發生變化,視圖也會更新以反映變化。我們的組件現在應該如下所示:

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

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  setup (props) {
    const repositories = ref([])
    const getUserRepositories = async () => {
      repositories.value = await fetchUserRepositories(props.user)
    }

    return {
      repositories,
      getUserRepositories
    }
  },
  data () {
    return {
      filters: { ... }, 
      searchQuery: '' 
    }
  },
  computed: {
    filteredRepositories () { ... }, 
    repositoriesMatchingSearchQuery () { ... }, 
  },
  watch: {
    user: 'getUserRepositories' 
  },
  methods: {
    updateFilters () { ... }, 
  },
  mounted () {
    this.getUserRepositories() 
  }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

我們已經將第一個邏輯關注點中的幾個部分移到了 setup 方法中,它們彼此非常接近。剩下的就是在 mounted 鈎子中調用 getUserRepositories,並設置一個監聽器,以便在 user prop 發生變化時執行此操作。

我們將從生命周期鈎子開始。

# 生命周期鈎子注冊內部 setup

為了使組合式 API 的功能比選項式 API 更加完整,我們還需要一種在 setup 中注冊生命周期鈎子的方法。這要歸功於從 Vue 導出的幾個新函數。組合式 API 上的生命周期鈎子與選項式 API 的名稱相同,但前綴為 on:即 mounted 會看起來像 onMounted

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

讓我們將其添加到 setup 函數中:

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


setup (props) {
  const repositories = ref([])
  const getUserRepositories = async () => {
    repositories.value = await fetchUserRepositories(props.user)
  }

  onMounted(getUserRepositories) 

  return {
    repositories,
    getUserRepositories
  }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

現在我們需要對 user prop 的變化做出反應。為此,我們將使用獨立的 watch 函數。

# watch 響應式更改

就像我們在組件中使用 watch 選項在 user property 上設置偵聽器一樣,我們也可以使用從 Vue 導入的 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)
})


1
2
3
4
5
6

每當 counter 被修改時,例如 counter.value=5,偵聽將觸發並執行回調 (第二個參數),在本例中,它將把 'The new counter value is:5' 記錄到我們的控制台中。

以下是等效的選項式 API:

export default {
  data() {
    return {
      counter: 0
    }
  },
  watch: {
    counter(newValue, oldValue) {
      console.log('The new counter value is: ' + this.counter)
    }
  }
}


1
2
3
4
5
6
7
8
9
10
11
12

有關 watch 的詳細信息,請參閱我們的深入指南

現在我們將其應用到我們的示例中:

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


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

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

  onMounted(getUserRepositories)

  
  watch(user, getUserRepositories)

  return {
    repositories,
    getUserRepositories
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

你可能已經注意到在我們的 setup 的頂部使用了 toRefs。這是為了確保我們的偵聽器能夠對 user prop 所做的變化做出反應。

注釋:可能是因為 props.user 是字符串,不是引用類型所以結構后就不再響應。如果被結構的目標是引用類型應該會保持響應?

有了這些變化,我們就把第一個邏輯關注點移到了一個地方。我們現在可以對第二個關注點執行相同的操作——基於 searchQuery 進行過濾,這次是使用計算屬性。

# 獨立的 computed 屬性

refwatch 類似,也可以使用從 Vue 導入的 computed 函數在 Vue 組件外部創建計算屬性。讓我們回到我們的 counter 例子:

import { ref, computed } from 'vue'

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

counter.value++
console.log(counter.value) 
console.log(twiceTheCounter.value) 


1
2
3
4
5
6
7
8

在這里,computed 函數返回一個作為 computed 的第一個參數傳遞的 getter 類回調的輸出的一個_只讀_的響應式引用。為了訪問新創建的計算變量的 value,我們需要像使用 ref 一樣使用 .value property。

讓我們將搜索功能移到 setup 中:

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


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

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

  onMounted(getUserRepositories)

  
  watch(user, getUserRepositories)

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

  return {
    repositories,
    getUserRepositories,
    searchQuery,
    repositoriesMatchingSearchQuery
  }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

對於其他的邏輯關注點我們也可以這樣做,但是你可能已經在問這個問題了——這不就是把代碼移到 setup 選項並使它變得非常大嗎?嗯,那是真的。這就是為什么在繼續其他任務之前,我們將首先將上述代碼提取到一個獨立的組合式函數。讓我們從創建 useUserRepositories 開始:

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
  }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

然后是搜索功能:

import { ref, computed } from 'vue'

export default function useRepositoryNameSearch(repositories) {
  const searchQuery = ref('')
  const repositoriesMatchingSearchQuery = computed(() => {
    return repositories.value.filter(repository => {
      return repository.name.includes(searchQuery.value)
    })
  })

  return {
    searchQuery,
    repositoriesMatchingSearchQuery
  }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

現在在單獨的文件中有了這兩個功能,我們就可以開始在組件中使用它們了。以下是如何做到這一點:

import useUserRepositories from '@/composables/useUserRepositories'
import useRepositoryNameSearch from '@/composables/useRepositoryNameSearch'
import { toRefs } from 'vue'

export default {
  components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
  props: {
    user: {
      type: String,
      required: true
    }
  },
  setup (props) {
    const { user } = toRefs(props)

    const { repositories, getUserRepositories } = useUserRepositories(user)

    const {
      searchQuery,
      repositoriesMatchingSearchQuery
    } = useRepositoryNameSearch(repositories)

    return {
      
      
      repositories: repositoriesMatchingSearchQuery,
      getUserRepositories,
      searchQuery,
    }
  },
  data () {
    return {
      filters: { ... }, 
    }
  },
  computed: {
    filteredRepositories () { ... }, 
  },
  methods: {
    updateFilters () { ... }, 
  }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

此時,你可能已經知道了其中的奧妙,所以讓我們跳到最后,遷移剩余的過濾功能。我們不需要深入了解實現細節,因為這不是本指南的重點。

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,
      required: true
    }
  },
  setup(props) {
    const { user } = toRefs(props)

    const { repositories, getUserRepositories } = useUserRepositories(user)

    const {
      searchQuery,
      repositoriesMatchingSearchQuery
    } = useRepositoryNameSearch(repositories)

    const {
      filters,
      updateFilters,
      filteredRepositories
    } = useRepositoryFilters(repositoriesMatchingSearchQuery)

    return {
      
      
      repositories: filteredRepositories,
      getUserRepositories,
      searchQuery,
      filters,
      updateFilters
    }
  }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

我們完成了!

請記住,我們只觸及了組合式 API 的表面以及它允許我們做什么。要了解更多信息,請參閱深入指南。


免責聲明!

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



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