script setup API

1. 基本语法

2. 响应式

  • 响应式数据需要使用响应式 API 创建
<script setup>
    // 导入响应式API
    import { ref, reactive } from "vue";

    // 创建ref对象
    const count = ref(0);
    // 创建响应式对象
    const obj = reactive({
        name: "张三",
        age: 18,
    });

    // 获取ref对象的值
    console.log(count.value);
    // 获取响应式对象的属性值
    console.log(obj.name, obj.age);
</script>

3. 组件

  1. 常规组件

    • 直接在 <script setup> 标签内,使用 import 导入组件
    • 可以直接在模板中使用
    • 组件是通过变量引用而不是基于字符串组件名注册的
    • 变量名与标签名一致
<script setup>
    // 导入组件
    import MyComponent from "./MyComponent.vue";
</script>

<template>
    <!-- 使用组件 -->
    <MyComponent />
</template>
  1. 动态组件
    • <component/> 标签中,通过使用 :is 动态绑定组件
    • is 绑定的值需是存储组件的变量(即 import 中定义的接收组件的变量)
<script setup>
    // 导入组件
    import Foo from "./Foo.vue";
    import Bar from "./Bar.vue";
</script>

<template>
    <!-- component标签会显示 is 绑定的组件 -->
    <component :is="Foo" />
    <!-- is 的值可以是一个计算属性 -->
    <component :is="someCondition ? Foo : Bar" />
</template>

4. 自定义指令

  • <script setup> 中,任何以 v 开头的驼峰式命名的变量都可以被用作一个自定义指令
  • 指令钩子 (可选)
    序号指令钩子作用
    1created组件创建完成时调用
    2beforeMount组件挂载前调用
    3mounted组件挂载完成时调用
    4beforeUpdate组件更新前调用
    5updated组件更新完成时调用
    6beforeUnmount组件卸载前调用
    7unmounted组件卸载完成时调用
<template>
    <button v-btn:arg="'传递给指令的值'">按钮</button>
</template>

<script setup>
    // 自定义指令
    const vBtn = {
        // 在绑定元素的 attribute 前
        // 或事件监听器应用前调用
        created(el, binding, vnode) {
            console.log("组件创建完成");
        },

        // 在元素被插入到 DOM 前调用
        beforeMount(el, binding, vnode) {
            console.log("组件挂载前");
        },

        // 在绑定元素的父组件
        // 及他自己的所有子节点都挂载完成后调用
        mounted(el, binding, vnode) {
            console.log("组件挂载完成");
        },

        // 绑定元素的父组件更新前调用
        beforeUpdate(el, binding, vnode, prevVnode) {
            console.log("组件更新前");
        },

        // 在绑定元素的父组件
        // 及他自己的所有子节点都更新后调用
        updated(el, binding, vnode, prevVnode) {
            console.log("组件更新完成");
        },

        // 绑定元素的父组件卸载前调用
        beforeUnmount(el, binding, vnode) {
            console.log("组件卸载前");
        },

        // 绑定元素的父组件卸载后调用
        unmounted(el, binding, vnode) {
            console.log("组件卸载完成");
        },
    };
</script>
  • 指令钩子的参数(可选)

    1. el:指令绑定到的元素。这可以用于直接操作 DOM。

    2. binding:一个对象,包含以下属性。

      (1) value:传递给指令的值。
      (2) oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用
      (3) arg:传递给指令的参数 (如果有的话)。
      (4) modifiers:一个包含修饰符的对象 (如果有的话)。
      (5) instance:使用该指令的组件实例。
      (6) dir:指令的定义对象。
      (7) 除了 el 外,其他参数都是只读的,不要更改它们。若需要在不同的钩子间共享信息,推荐通过元素的 dataset attribute 实现

    3. vnode:代表绑定元素的底层 VNode。

    4. prevNode:代表之前的渲染中指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。


  • 简化形式
    直接定义的函数,会默认在 mountedupdated 时都调用

    <template>
        <button v-btn="'red'">count++</button>
    </template>
    
    <script lang="ts" setup>
        // 自定义指令
        const vBtn = (el, binding) => {
            // 这会在 `mounted` 和 `updated` 时都调用
            el.style.color = binding.value;
        };
    </script>
    
  • 对象字面量
    使用对象,可以让指令接收多个值,指令也可以接收任何合法的 JavaScript 表达式

    <template>
        <button v-btn="{ color: 'red', text: '按钮' }">count++</button>
    </template>
    
    <script setup>
        // 自定义指令
        const vBtn = (el, binding) => {
            // 这会在 `mounted` 和 `updated` 时都调用
            el.style.color = binding.value.color;
            el.innerText = binding.value.text;
        };
    </script>
    
  • 在组件上使用
    当在组件上使用自定义指令时,它会始终应用于组件的根节点,和透传 attributes 类似。
    当应用到一个多根组件时,指令将会被忽略且抛出一个警告。
    指令不能通过 v-bind=“$attrs” 来传递给一个不同的元素。
    不推荐在组件上使用自定义指令。

  • 动态绑定参数和接收的值
    eg:v-btn:[arg,modifiers]='value'
    动态绑定参数:参数为一个数组,第一个元素是提供 arg 的变量,第二个元素是提供 modifiers 的变量;
    动态绑定接收的值:value 是一个提供 value 的变量

5. defineProps() 接收父组件传递的属性

  • defineProps() 显式声明 接收父组件传递的参数

    1. 使用字符串数组来声明 prop
    <!-- 父组件文件 -->
    <template>
        <Children :num="num" :str="str" :bool="bool" :arr="arr" :obj="obj"></Children>
    </template>
    <script lang="ts" setup>
        // 导入子组件
        import Children from "../children.vue";
        import { ref, reactive } from "vue";
    
        const num = ref(0);
        const str = ref("标题");
        const bool = ref(true);
        const arr = reactive([1, 2, 3]);
        const obj = reactive({
            name: "张三",
            age: 18,
        });
    </script>
    
    <!-- 子组件文件 -->
    <template>
        <div>子组件</div>
    </template>
    <script lang="ts" setup>
        const props = defineProps(["num", "str", "bool", "arr", "obj"]);
        console.log(props.num);
        console.log(props.str);
        console.log(props.bool);
        console.log(props.arr);
        console.log(props.obj);
    </script>
    
    1. 使用对象形式来声明 prop
      key 是 prop 的名称,值是该 prop 预期类型的构造函数
    <!-- 父组件文件 -->
    <template>
        <Children :num="num" :str="str" :bool="bool" :arr="arr" :obj="obj"></Children>
    </template>
    <script lang="ts" setup>
        // 导入子组件
        import Children from "../children.vue";
        import { ref, reactive } from "vue";
    
        const num = ref(0);
        const str = ref("标题");
        const bool = ref(true);
        const arr = reactive([1, 2, 3]);
        const obj = reactive({
            name: "张三",
            age: 18,
        });
    </script>
    
    <!-- 子组件文件 -->
    <template>
        <div>子组件</div>
    </template>
    <script lang="ts" setup>
        const props = defineProps({
            num: Number,
            str: String,
            bool: Boolean,
            arr: Array,
            obj: Object,
        });
        console.log(props.num);
        console.log(props.str);
        console.log(props.bool);
        console.log(props.arr);
        console.log(props.obj);
    </script>
    
    1. Prop 校验
      要声明对 props 的校验,你可以向 defineProps() 宏提供一个带有 props 校验选项的对象
    <script lang="ts" setup>
        const props = defineProps({
            // 基础类型检查
            // (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
            propA: Number,
    
            // 多种可能的类型
            propB: [String, Number],
    
            // 必传,且为 String 类型
            propC: {
                type: String,
                required: true,
            },
    
            // Number 类型的默认值
            propD: {
                type: Number,
                default: 100,
            },
    
            // 对象类型的默认值
            propE: {
                type: Object,
                // 对象或数组的默认值
                // 必须从一个工厂函数返回。
                // 该函数接收组件所接收到的原始 prop 作为参数。
                default(rawProps) {
                    return { message: "hello" };
                },
            },
    
            // 自定义类型校验函数
            propF: {
                validator(value) {
                    // The value must match one of these strings
                    return ["success", "warning", "danger"].includes(value);
                },
            },
    
            // 函数类型的默认值
            propG: {
                type: Function,
                // 不像对象或数组的默认,这不是一个
                // 工厂函数。这会是一个用来作为默认值的函数
                default() {
                    return "Default function";
                },
            },
        });
    </script>
    
    1. 单向数据流
      (1) 所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。
      (2) 避免子组件意外修改父组件的状态的情况,使应用的数据流容易变得混乱而难以理解。
      (3) 不推荐在子组件中去更改一个 prop。若这么做了,Vue 会在控制台上向你抛出警告

6. defineEmits() 接收父组件传递的自定义事件

  • defineEmits() 显式声明 自定义事件
    <script setup> 使用自定义事件:
    (1) 创建父组件监听自定义事件时要触发的方法 changMyMsg(val)
    该方法有一个参数 value,是子组件传给父组件的数据

    function changMyMsg(val) {
        msg.value += val;
    }
    

    (2) 在父组件中设置一个自定义事件 changMsg ,并绑定上述步骤定义的方法 changMyMsg()

    <Children @changMsg="changMyMsg"></Children>
    

    (3) 在子组件中声明要接收的 父组件传递过来的自定义事件

    const emit = defineEmits(["changMsg"]);
    

    (4) 在子组件中定义一个方法,用于子组件监听事件触发。该方法中调用 emit() 函数
    emit() 函数有两个参数:
    参数一:父组件传递过来的自定义事件的名称
    参数二:子组件要传给父组件的数据

    function changFatherMsg(num) {
        emit("changMsg", num);
    }
    

    (5) 在子组件上绑定一个监听事件,并给该监听事件绑定步骤(4)中定义的方法 changFatherMsg()

    <button @click="changFatherMsg(2)">按一下 +2</button>
    

    通过 emit 修改父组件参数的完整实例

    <!-- 父组件 -->
    <template>
        <Children :msg="msg" @changMsg="changMyMsg"></Children>
    </template>
    <script setup>
        import Children from "./children.vue";
        import { ref } from "vue";
    
        const msg = ref(0);
        function changMyMsg(val) {
            // val:子组件传递给父组件的数据
            msg.value += val;
        }
    </script>
    
    <!-- 子组件 -->
    <template>
        <p>msg:{{ msg }}</p>
        <button @click="changFatherMsg(2)">按一下 msg+2</button>
    </template>
    <script setup>
        // 声明接收父组件传递给子组件的 porps
        const props = defineProps(["msg"]);
        // 声明接收父组件传递给子组件的 自定义事件
        const emit = defineEmits(["changMsg"]);
    
        function changFatherMsg(num) {
            // 参数一:自定义事件名
            // 参数二:要传递给父组件的数据
            emit("changMsg", num);
        }
    </script>
    <!-- 
        子组件修改父组件参数的流程:
            (1) 点击子组件中的按钮 => 触发子组件中的 changFatherMsg 函数 
            (2) changFatherMsg 函数触发内部的 emit 函数
            (3) emit 函数触发父组件的 changMyMsg 函数,并将子组件的数据传递给 changMyMsg 函数
            (4) changMyMsg 函数触发,修改父组件的数据
    -->
    

7. defineExpose() 对外暴露子组件的属性

  • <script setup> 模式下,父组件无法直接通过 ref 直接获取子组件的属性
  • 需要子组件通过 defineExpose 编译器宏来显式指定在 <script setup> 组件中要暴露出去的属性
  • 父组件才能在组件挂载完成后,通过 ref对象.vlaue 获取子组件暴露出来的属性
<!-- 父组件 -->
<template>
    <Children ref="childrenDOM"></Children>
</template>
<script setup>
    import Children from "./children.vue";
    import { onMounted, ref } from "vue";

    const childrenDOM = ref(null);

    // 只有在子组件挂载完成后,才能获取 ref对象
    onMounted(() => {
        // 获取子组件暴露的属性
        console.log(childrenDOM.value);
    });
</script>

<!-- 子组件 -->
<template>
    <button>按钮</button>
</template>

<script setup>
    const num = 0;
    const str = "字符串";

    // 子组件要暴露出去的属性
    defineExpose({
        num,
        str,
    });
</script>

8. defineOptions() 声明组件选项

  • 用来直接在
<script setup>
    defineOptions({
        // 设置组件名称
        name:'children',
        // 属性继承:不允许
        inheritAttrs: false,
    })
</script>

9. defineSlots()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值