vue3.0的script-setup以及全新属性API 2021.08.26

703 阅读2分钟

什么是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需要使用definePropsdefineEmits来声明propsemits,用法如下:

// 父组件 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

useSlotsuseAttrsslotsattrs功能一致,使用时需要调用各自的函数得到对应的setupContext.slotssetupContext.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>