什么是script-setup呢?
script setup是在单文件组件(SFC)中使用组合式api的一个语法糖,编译器检测script中是否含有setup,如果有会自动将script标签中的所有变量、常量、函数、import引入的文件暴露给模板
基本语法
使用script setup这种写法时,只需在script标签上添加上setup即可开始自己的代码,编译时编译器会根据相应的关键字和使用情况区分是否是data、method、组件等
<script setup>
import cmp from './component.vue' // 导入组件,模板中可直接使用cmp组件
import { ref, computed } from 'vue'
let counter = ref(1) // 定义变量counter,模板中可直接使用counter
const add = (a, b) => a + b // 方法add被暴露给模板
let double = computed(() => counter * 2) // 计算属性double被暴露
onMounted(() => { // 生命周期钩子函数
console.log('onMounted!');
})
</script>
// 等价于
<script>
import cmp from '.component.vue'
import { ref, computed } from 'vue'
export default {
components: {
cmp
},
setup() {
let counter = ref(1)
const add = (a, b) => a + b
let double computed(() => counter * 2)
return { // 暴露给模板
counter,
add,
double
}
}
}
</script>
script-setup相比于setup写法上更简洁了,不用手动暴露,顶层的变量、函数均暴露给模板
与普通的script一起使用
从上面我们知道script-setup标签中的实际就是setup函数中部分,但是setup函数以外部分我们如何去写呢?比如组件名name,看下面代码:
<script>
import { defineComponent } from 'vue'
export default defineComponent ({
name: 'ComponentName'
});
</script>
<script setup>
// setup函数中内容
// ...
</script>
上面代码一段是导出默认组件,而第二段是script-setup,这两个script在同一个模板中导出最终被解析成一段代码,即:
<script>
export default {
name: 'ComponentName',
setup() {
// setup函数内容
// ...
return {}
}
}
</script>
如果普通函数中与script-setup中有相同的变量、方法、导入文件等,setup中的同名变量或函数或文件会覆盖普通script标签中的同名变量或函数或文件
defineProps 和 defineEmits
在script-setup需要使用defineProps和defineEmits来声明props和emits,用法如下:
// 父组件 parent.vue
<child :counter="counter" />
// 子组件 child.vue
<script setup>
const props = defineProps({ // 使用defineProps接收父组件中的参数
counter: Number // 父组件传递的参数
})
</script>
父组件向子组件传递参数,子组件中使用defineProps声明
子组件中如何向父组件传递参数呢?
// 子组件 child.vue
<tempalte>
<button @click="handleAdd">+1</button>
</tempalte>
<script setup>
import { ref } from 'vue'
const emits = defineEmits([
'click'
])
const counter = ref(0)
const handleAdd = () => {
emits('click', ++counter.value)
}
</script>
// 父组件 parent.vue
<template>
<child @click="getValue"></child>
</template>
<script setup>
const getValue = (val) => {
console.log(val)
}
</script>
defineExpose
使用script-setup后,setup中的属性不会向父组件中暴露,为了能满足这一需求,引入了defineExpose来暴露任何你想暴露的属性
// 子组件 child.vue
<template>
<div>child</div>
</template>
<script setup>
import { ref } from "@vue/reactivity";
function log() {
console.log('child log message~~');
return 'This is log message~~';
}
const message = ref('This is a ref message~~');
defineExpose({
message,
log
})
</script>
// 父组件 parent.vue
<template>
<div>parent</div>
<child ref="childRef" />
<el-button type="primary" size="small" @click="handleExpose">获取子组件暴露的数据和方法</el-button>
</template>
<script setup>
import { ref } from '@vue/reactivity';
import Child from './components/child.vue';
const childRef = ref(null);
const handleExpose = () => {
console.log('defineExpose child data @@ ', childRef.value.message, childRef.value.log());
}
</script>
useSlots 和 useAttrs
useSlots、useAttrs同slots和attrs功能一致,使用时需要调用各自的函数得到对应的setupContext.slots和setupContext.attrs
import { useAttrs, useSlots } from "@vue/runtime-core";
const attrs = useAttrs();
console.log('attrs=== ', attrs);
const slots = useSlots();
console.log('slots @@ ', slots);
provide 和 inject
// 父组件 parent.vue
provide('test', 'This is a test message ~ ');
provide('test2', 'This is a test2 message ~ ');
// 子组件 child.vue
const injects = inject('test');
const injects2 = inject('test2');
console.log('test----- ', injects, injects2);
顶层await
script setup中可以使用顶层await,代码会被编译成async setup():
<script setup>
let res = await api.getData();
console.log('res=', res);
</script>