在 Vue3 中,setup 函数是一个新引入的概念,它代替了之前版本中的 data、computed、methods 等选项,用于设置组件的初始状态和逻辑。setup 函数的引入使得组件的逻辑更加清晰和灵活,本文将主要介绍Setup的基本用法和少量原理
setup()
钩子是在组件中使用组合式 API 的入口,通常只在以下情况下使用:
其他情况下,都应优先使用 <script setup> 语法。
我们可以使用响应式 API 来声明响应式的状态,在 setup()
函数中返回的对象会暴露给模板和组件实例。其它的选项也可以通过组件实例来获取 setup()
暴露的属性。
<script> import { ref } from 'vue' export default { setup() { const count = ref(0) // 返回值会暴露给模板和其他的选项式 API 钩子 return { count } }, mounted() { console.log(this.count) // 0 } } </script> <template> <button @click="count++">{{ count }}</button> </template>
请注意在模板中访问从 setup
返回的 ref 时,它会自动浅层解包,因此你无须再在模板中为它写 .value
。当通过 this
访问时也会同样如此解包。
setup()
自身并不含对组件实例的访问权,即在setup()
中访问this
会是undefined
。你可以在选项式 API 中访问组合式 API 暴露的值,但反过来则不行。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>组合式API</title> </head> <body> <div id="app"> {{ count }} <button @click="add">加1</button> </div> </body> <script src="../lib/vue.global.js"></script> <script> const { ref, onMounted } = Vue Vue.createApp({ setup () { const count = ref(0) const add = () => { count.value += 1 } onMounted(() => { console.log(1111) }) return { count, add } }, data () { return { count: 10 } }, methods: { add () { this.count += 10 } }, mounted () { console.log('2222') } }).mount('#app') </script> </html>
生命周期先执行 组合式API 后执行选项式API,其余以组合式API为优先
setup
函数的第一个参数是组件的 props
。和标准的组件一致,一个 setup
函数的 props
是响应式的,并且会在传入新的 props 时同步更新。
{ props: { title: String, count: Number }, setup(props) { console.log(props.title) console.log(props.count) } }
请注意如果你解构了
props
对象,解构出的变量将会丢失响应性。因此我们推荐通过props.xxx
的形式来使用其中的 props。
如果你确实需要解构 props
对象,或者需要将某个 prop 传到一个外部函数中并保持响应性,那么你可以使用 toRefs() 和 toRef() 这两个工具函数:
{ setup(props) { // 将 `props` 转为一个其中全是 ref 的对象,然后解构 const { title } = toRefs(props) // `title` 是一个追踪着 `props.title` 的 ref console.log(title.value) // 或者,将 `props` 的单个属性转为一个 ref const title = toRef(props, 'title') } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>组合式API</title> </head> <body> <div id="app"> <button @click="num++">加1</button> {{ num }} <my-root :num="num"></my-root> </div> </body> <script src="../lib/vue.global.js"></script> <template id="root"> <div>{{ num }} -- {{ test }}</div> </template> <script> const { ref, onMounted, computed } = Vue const Root = { props: ['num'], template: '#root', // setup (props) { // 千万不要对 props 解构 // console.log('111') // return { // test: computed(() => props.num) // 继续保持响应式 // } // } setup ({ num }) { console.log(num) return { test: computed(() => num) // 失去了响应式 - test的值不会发生改变 } } } Vue.createApp({ setup () { const num = ref(10000) return { num } }, components: { MyRoot: Root } }).mount('#app') </script> </html>
传入 setup
函数的第二个参数是一个 Setup 上下文对象。上下文对象暴露了其他一些在 setup
中可能会用到的值:
{ setup(props, context) { // 透传 Attributes(非响应式的对象,等价于 $attrs) console.log(context.attrs) // 插槽(非响应式的对象,等价于 $slots) console.log(context.slots) // 触发事件(函数,等价于 $emit) console.log(context.emit) // 暴露公共属性(函数) console.log(context.expose) } }
该上下文对象是非响应式的,可以安全地解构:
{ setup(props, { attrs, slots, emit, expose }) { ... } }
attrs
和 slots
都是有状态的对象,它们总是会随着组件自身的更新而更新。这意味着你应当避免解构它们,并始终通过 attrs.x
或 slots.x
的形式使用其中的属性。此外还需注意,和 props
不同,attrs
和 slots
的属性都不是响应式的。如果你想要基于 attrs
或 slots
的改变来执行副作用,那么你应该在 onBeforeUpdate
生命周期钩子中编写相关逻辑。
expose
函数用于显式地限制该组件暴露出的属性,当父组件通过模板引用访问该组件的实例时,将仅能访问 expose
函数暴露出的内容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>setup上下文对象</title> </head> <body> <div id="app"> <my-com ref="comref" class="myBox" style="color: red" id="box" msg="hello msg" @my-event="getData"> <template #header> header </template> <div>content</div> <template #footer> footer </template> </my-com> </div> </body> <template id="com"> <div> <h1>子组件</h1> <button @click="sendData">发送数据</button> <slot name="header"></slot> <slot></slot> <slot name="footer"></slot> </div> </template> <script src="../lib/vue.global.js"></script> <script> const { createApp, ref, onMounted } = Vue const Com = { template: '#com', setup (props, context) { // attrs 获取透传过来的值 // slots 如果使用了插槽 // emit 子组件给父组件传值 // expose 子组件暴露给父组件可以调用的属性和方法 ---- options API ref获取子组件的实例 console.log(props) console.log(context.attrs) // ref 不在透传之列 console.log(context.slots) const sendData = () => { // 子组件给父组件传值 context.emit('my-event', 1000) } // 自定义的属性和方法,供给父组件使用 const a = ref(1) const b = ref(2) const c = ref(3) const fn = () => { a.value = 100 } // 暴露出去的是对象 context.expose({ a, b, fn }) return { sendData } } } Vue.createApp({ setup () { const getData = (val) => { // 接收子组件的值 console.log('666', val) } const comref = ref() // comref 就是模版中ref="comref" onMounted(() => { console.log('com', comref.value) // {} console.log('a', comref.value.a) // 1 console.log('b', comref.value.b) // 2 console.log('c', comref.value.c) // undefined 因为没有暴露 comref.value.fn() console.log('a', comref.value.a) // 100 }) return { getData, comref } }, components: { MyCom: Com } }).mount('#app') </script> </html>
在父组件通过ref获取子组件的实例的属性和方法的需求中,需要注意:
1.如果子组件是 选项式API组件,基本不需要做任何操作
2.如果子组件是 组合式API组件,需要通过 context.expose 暴露给父组件需要使用的属性和方法
3.如果父组件使用 选项式API, 可以通过 this.$refs.refName 访问到子组件想要你看到的属性和方法
4.如果父组件使用 组合式API,需要在setup中先创建 refName,然后再访问子组件想要你看到的属性和方法(const refName = ref() refName.value.X)
setup
也可以返回一个渲染函数,此时在渲染函数中可以直接使用在同一作用域下声明的响应式状态:
{ setup() { const count = ref(0) return () => h('div', count.value) } }
返回一个渲染函数将会阻止我们返回其他东西。对于组件内部来说,这样没有问题,但如果我们想通过模板引用将这个组件的方法暴露给父组件,那就有问题
我们可以通过调用 expose() 解决这个问题:
{ setup(props, { expose }) { const count = ref(0) const increment = () => ++count.value expose({ increment }) return () => h('div', count.value) } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>渲染函数</title> </head> <body> <div id="app"> <button @click="add">加1</button> <my-child ref="child"></my-child> </div> </body> <script src="../lib/vue.global.js"></script> <script> const { h, ref } = Vue const Child = { // 写法1: // template: `<div>child</div>` // 写法2: // render () { // return [ // h('div', 'child!') // ] // } // 写法3 setup (props, { expose }) { const count = ref(10) const increment = () => { count.value += 1 } expose({ increment }) // 返回一个函数 函数返回 渲染函数的结果 return () => h('div', 'child!!' + count.value) } } Vue.createApp({ components: { MyChild: Child }, setup () { const child = ref() const add = () => { child.value.increment() } return { child, add } } }).mount('#app') </script> </html>
到此这篇关于vue3中的setup()函数详解的文章就介绍到这了,更多相关vue3 setup()函数内容请搜索插件窝以前的文章或继续浏览下面的相关文章希望大家以后多多支持插件窝!