多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## `setup`函数是组合式API的入口 > 新的`setup`函数在组件被创建**之前**执行,一旦`props`被解析完成,它就将被作为组合式 API 的入口,`setup`接收两个参数,分别是`props`和`context` ~~~javascript export default { components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList }, props: { user: { type: String, required: true } }, setup(props) { console.log(props) // { user: '' } return {} // 这里返回的任何内容都可以用于组件的其余部分 } // 组件的“其余部分” } ~~~ ## 在`setup`函数中使用响应式变量和方法 ~~~javascript import { fetchUserRepositories } from '@/api/repositories' import { ref , reactive , toRefs } from 'vue' //通过ref和reactive创建响应式变量 setup (props) { //响应变量,通过repositories.value 去访问值 const repositories = ref([]) //使用reactive创建响应式数据,reactive的参数必须是一个对象才是响应式数据,读取值通过data.name const data = reactive({ name:'张三' }) //方法 const getUserRepositories = async () => { repositories.value = await fetchUserRepositories(props.user) } //暴露给模版使用 return { repositories, data, getUserRepositories } } ~~~ ## 在`setup`函数中使用生命周期 在`setup`中通过导入的形式注册生命周期 ~~~javascript // src/components/UserRepositories.vue `setup` function 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) // 在 `mounted` 时调用 `getUserRepositories` return { repositories, getUserRepositories } } ~~~ ## 在`setup`函数中使用侦听器和计算属性 在`setup`中通过导入 `watch`和`computed`使用侦听器和计算属性 ~~~javascript //导入watch 侦听器函数 import { ref, watch } from 'vue' //watch 第一个参数可以是一个基本类型的值或者一个函数 const counter = ref(0) //侦听ref的值 watch(counter, (newValue, oldValue) => { console.log('The new counter value is: ' + counter.value) }) const data = reactive({name:'张三'}) //侦听reactive的值,传递一个函数 watch(() => data.name , (newValue, oldValue) => { console.log('The new counter value is: ' + counter.value) }) //使用计算属性 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 ~~~ ## 响应式解构`toRefs`和`toRef` ~~~javascript import { toRefs } from 'vue' setup(props) { // 解构props,如果是ES6解构 const { title } = props,外部的title变化,传进来的title不是最新的值 const { title } = toRefs(props) console.log(title.value) const data = reactive({ name:'张三' }) //解构reactive响应式对象 const { name } = toRefs(data) //console.log(name.value) 对name的修改是响应式的,data.name 会同步变化 //const { name } = data 这样解构,数据不是响应式的 } ~~~ 如果`title`是可选的 prop,没有传`title`时`toRefs`将不会为`title`创建一个 ref 。此时可以使用`toRef`替代它: ~~~javascript import { toRef } from 'vue' setup(props) { const title = toRef(props, 'title') console.log(title.value) } ~~~ ## `setup`函数的两个参数`props`和`context` `setup`函数中的第一个参数是`props`。`setup`函数中的`props`是响应式的,当传入新的 prop 时,它将被更新。如果要解构请使用`toRefs`或者`toRef` ~~~javascript export default { props: { title: String }, setup(props) { console.log(props.title) } } ~~~ `setup`函数中的第二个参数是`context`。`context`是一个普通 JavaScript 对象,暴露了其它可能在`setup`中有用的值: * attrs * slots * emit * expose ~~~javascript 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) } } ~~~ `context`是一个普通的 JavaScript 对象,它不是响应式的,可以安全地对`context`使用 ES6 解构 ~~~javascript export default { setup(props, { attrs, slots, emit, expose }) { ... } } ~~~ >[warning] `attrs`和`slots`是有状态的对象,它们总是会随组件本身的更新而更新。避免对它们进行解构,并始终以`attrs.x`或`slots.x`的方式使用属性。 > ## 在`setup`函数中使用模版引用(`ref`) 由于在setup函数中`this`指向不是组件实例,所以`this`并不能使用,也就无法通过`this.$refs`来获取指定组件,所以要引用组件需要以下步骤: 1. 定义一个`ref`响应式变量,值为null 2. 把变量名赋值到元素的`ref`属性中 ~~~ xml <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>This is a root element</div> }) return { root } } } </script> ~~~ 在vue3中如果想通过模版应用`ref`去调用组合式API组件的方法,那么该组件必须使用`expose`显性的暴露出组件可访问的方法或变量。 ~~~ <script> import { ref , toRef } from "vue"; export default { emits: ["myEvent"], props: { msg: String, }, setup(props, { emit ,expose }) { const msg = toRef(props,'msg'); let count = ref(0); const onClick = () => { count.value++; emit("myEvent"); }; //暴露出属性或方法,在ref引用时才可以访问到 expose({ onClick, msg }) return { count, onClick, }; }, }; ~~~ ## 在`setup`函数中使用 `Provide / Inject` ### 父组件使用 Provide 在`setup()`中使用`provide`时,我们首先从`vue`显式导入`provide`方法。 ~~~ xml <!-- src/components/MyMap.vue --> <template> <MyMarker /> </template> <script> //需要导入provide使用 import { provide } from 'vue' import MyMarker from './MyMarker.vue' export default { components: { MyMarker }, setup() { //提供给子组件访问的属性或方法 let name = '柴柴老师' // 使用provide配置项注入数据 key - value provide('name', name) provide('location', 'North Pole') provide('geolocation', { longitude: 90, latitude: 135 }) } } </script> ~~~ ### 子组件使用 inject ~~~ xml <!-- src/components/MyMarker.vue --> <script> //需要导入inject使用 import { inject } from 'vue' export default { setup() { //inject 第一个参数是接收的provide名,第二个参数是默认值 const userLocation = inject('location', 'The Universe') const userGeolocation = inject('geolocation') return { userLocation, userGeolocation } } } </script> ~~~ ### 依赖注入的响应性 `provide`默认情况下传递的数据不是响应式的,即祖先组件修改 `provide`数据,子组件接收的并不会响应式的变化,如果想要传递响应数据也非常简单,只需要将传递的数据使用ref或者reactive生成即可 ~~~xml <!-- src/components/MyMap.vue --> <template> <MyMarker /> </template> <script> import { provide, reactive, ref } from 'vue' import MyMarker from './MyMarker.vue' export default { components: { MyMarker }, setup() { //在祖先组件修改provide数据,使用inject接收的子组件数据会相应的变化 const location = ref('North Pole') const geolocation = reactive({ longitude: 90, latitude: 135 }) provide('location', location) provide('geolocation', geolocation) } } </script> ~~~ ## 在`setup`中访问路由和当前路由 ~~~ import { useRouter, useRoute } from 'vue-router' export default { setup() { const router = useRouter() const route = useRoute() function pushWithQuery(query) { router.push({ name: 'search', query: { ...route.query, }, }) } }, } ~~~