setup(props,contenxt)
setup() 钩子是在组件中使用组合式 API 的入口,官方解释只在以下情况下使用:
- 需要在非单文件组件中使用组合式 API 时;
- 需要在基于选项式 API 的组件中集成基于组合式 API 的代码时;
其他情况下,都应优先使用 <script setup> 语法;
基本语法
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const foo = function(){...}
// 返回值会暴露给模板和其他的选项式 API 钩子
return {
count,
foo
}
},
}
</script>
<template>
<button @click="count++">{{ count }}</button>
</template>
参数props
第一个参数 props 是一个对象接收父组件传递过来的数据,并且 props 是响应式的,在传入新的 props 时同步更新。
需要注意一点是在使用前必须接收,否则值为undefined;
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}
如果通过解构 props 对象,则解构出来的变量将会丢失响应性。在非解构情况下通过 props.xxx 的形式来使用接收的 props;
如果需要解构 props 对象,或者需要将某个 prop 传到一个外部函数中并保持响应性,那么可以使用 toRefs() 和 toRef() 这两个工具函数;
//官网例子
import { toRefs, toRef } from 'vue'
export default {
setup(props) {
// 将 `props` 转为一个其中全是 ref 的对象,然后解构
const { title } = toRefs(props)
// `title` 是一个追踪着 `props.title` 的 ref
console.log(title.value)
// 或者,将 `props` 的单个属性转为一个 ref
const title = toRef(props, 'title')
}
}
toRef和toRefs就是为了方便把响应式数据 ref、reactive 展开丢出去,方便在模版中应用;
参数contenxt
第二个参数是一个 Setup 上下文对象。上下文对象暴露了其他一些在 setup 中可能会用到的值attrs, slots, emit, expose...,该上下文对象是非响应式的,可以直接解构;
//官网例子
export default {
setup(props, context) {
// 透传 Attributes(非响应式的对象,等价于 $attrs)
console.log(context.attrs)
// 插槽(非响应式的对象,等价于 $slots)
console.log(context.slots)
// 触发事件(函数,等价于 $emit)
console.log(context.emit)
// 暴露公共属性(函数)
console.log(context.expose)
}
}
<script setup>
<script setup> 其实就是 组合式 API 的语法糖。相比于普通的 <script> 语法,它具有更多优势:
- 更少的样板内容,更简洁的代码。
- 能够使用纯 TypeScript 声明 props 和自定义事件。
- 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
- 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。
基本语法
要启用该语法,需要在 <script> 代码块上添加 setup attribute:
// 官网例子
<script setup>
console.log('hello script setup')
</script>
上面的代码在编译时会转换为 setup() API的语法内容,代码会在每次组件实例被创建的时候执行;
顶层的绑定会被暴露给模板
其实也就是变量、方法都像vue2中的data()函数中的数据,<template>可以直接使用,不用注册
<script setup>
const msg = 'Hello!' //变量
import { capitalize } from './helpers' //第三方库
import son from './son.vue' //子组件
function log() {console.log(msg)} //函数
</script>
<template>
<button @click="log">{{ msg }}</button>
<div>{{ capitalize('hello') }}</div>
<son></son>
<template>
defineProps() 和 defineEmits()
与setup()函数相比,<script setup>只能通过defineProps 和 defineEmits方法来使接收传递数据。他们不需要导入,且会随着 <script setup> 的处理过程一同被编译掉;
defineProps() 使用语法
defineEmits() 使用语法
//父组件
<template>
<props-value @sEmit="sEmit"></props-value>
</template>
<script setup>
import propsValue from "./components/pages/propsValue.vue"
const sEmit = function(vals){
console.log(vals)
}
</script>
// 子组件
<template>
<button @click="send">子组件发送"hello word"</button>
</template>
<script setup>
const emits = defineEmits(["sEmit"])
const send = function(){
emits("sEmit","hello word")
}
</script>
defineExpose
<script setup> 对外暴露属性需要使用defineExpose(), 不需要导入 直接使用
//父组件
<template>
<props-value ref="children"></props-value>
</template>
<script setup>
import propsValue from "./components/pages/propsValue.vue"
let children = ref(null)
console.log(children)
</script>
在父组件中使用ref绑定子组件暴露的数据
//子组件
<template>
</template>
<script setup>
import {ref,reactive} from "vue"
const a = ref(100)
const b = reactive("name")
defineExpose({
a,
b
})
</script>
useSlots() 和 useAttrs()
顶层 await
<script setup> 中可以使用顶层 await。结果代码会被编译成 async setup()
<script setup>
const post = await fetch(`/api/post/1`).then((r) => r.json())
</script>
另外,await 的表达式会自动编译成在 await 之后保留当前组件实例上下文的格式。