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>
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
}
}
}
总结:



浙公网安备 33010602011771号