• 技术文章 >web前端 >Vue.js

    一文详解Vue3中的script setup语法糖

    青灯夜游青灯夜游2022-11-22 20:07:20转载420

    大前端成长进阶课程:进入学习

    script setup 语法糖

    组合式 API:setup()

    基本使用

    Vue 3 的 Composition API 系列里,推出了一个全新的 setup 函数,它是一个组件选项,在创建组件之前执行,一旦 props 被解析,并作为组合式 API 的入口点。【学习视频分享:vue视频教程web前端视频

    setup 选项是一个接收 propscontext 的函数,我们参考文档进行讨论。此外,我们将 setup 返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。

    <script>
    // 这是一个基于 TypeScript 的 Vue 组件
    import { defineComponent } from 'vue'
    
    export default defineComponent({
      setup(props, context) {
        // 在这里声明数据,或者编写函数并在这里执行它
    
        return {
          // 需要给 `<template />` 用的数据或函数,在这里 `return` 出去
        }
      },
    })
    
    </script>

    新的 setup 选项是在组件创建之前, props 被解析之后执行,是组合式 API 的入口。

    注意:
    setup 中你应该避免使用 this,因为它不会找到组件实例。setup 的调用发生在 data property、computed property 或 methods 被解析之前,所以它们无法>在 setup 中被获取。

    在添加了setup的script标签中,我们不必声明和方法,这种写法会自动将所有顶级变量、函数,均会自动暴露给模板(template)使用
    这里强调一句 “暴露给模板,跟暴露给外部不是一回事

    TIP:说的通俗一点,就是在使用 Vue 3 生命周期的情况下,整个组件相关的业务代码,都可以放在 setup 里执行。

    因为在 setup 之后,其他的生命周期才会被启用,我们对比一下Vue2的Vue3生命周期的变化

    组件生命周期

    关于 Vue 生命周期的变化,可以从下表直观地了解:

    Vue 2 生命周期Vue 3 生命周期执行时间说明
    beforeCreatesetup组件创建前执行
    createdsetup组件创建后执行
    beforeMountonBeforeMount组件挂载到节点上之前执行
    mountedonMounted组件挂载完成后执行
    beforeUpdateonBeforeUpdate组件更新之前执行
    updatedonUpdated组件更新完成之后执行
    beforeDestroyonBeforeUnmount组件卸载之前执行
    destroyedonUnmounted组件卸载完成后执行
    errorCapturedonErrorCaptured当捕获一个来自子孙组件的异常时激活钩子函数

    可以看到 Vue 2 生命周期里的 beforeCreatecreated ,在 Vue 3 里已被 setup 替代。

    script setup 语法糖

    它是 Vue3 的一个新语法糖,在 setup 函数中。所有 ES 模块导出都被认为是暴露给上下文的值,并包含在 setup() 返回对象中。相对于之前的写法,使用后,语法也变得更简单。

    自动注册属性和方法无需返回,直接使用

    1.<script setup> 语法糖并不是新增的功能模块,它只是简化了以往的组合API(compositionApi)的必须返回(return)的写法,并且有更好的运行时性能。

    2.在 setup 函数中:所有 ES 模块导出都被认为是暴露给上下文的值,并包含在 setup() 返回对象中。相对于之前的写法,使用后,语法也变得更简单。

    你不必担心setup语法糖的学习成本,他是组合式API的简化,并没有新增的知识点。你只需要了解一些用法和细微的不同之处,甚至比之前写setup()还要顺手!

    使用方式也很简单,只需要在 script 标签加上 setup 关键字即可

    <script setup>
    </script>

    组件核心 API 的使用

    组件自动注册

    在 script setup 中,引入的组件可以直接使用,无需再通过components进行注册,并且无法指定当前组件的名字,它会自动以文件名为主,也就是不用再写name属性了。

    示例

    <template>
    	<Child />
    </template>
    
    <script setup>
    import Child from '@/components/Child.vue'
    </script>

    定义组件的 props

    defineProps ----> [用来接收父组件传来的 props] 代码示列

    通过defineProps指定当前 props 类型,获得上下文的props对象。

    示例:

    <script setup>
      import { defineProps } from 'vue'
    
      const props = defineProps({
        title: String,
      })
    </script>
    <!-- 或者 -->
    <script setup> 
        import { ref,defineProps } from 'vue';
        
        type Props={ 
            msg:string 
        }
        defineProps<Props>(); 
    </script>

    定义 emit

    defineEmit ----> [子组件向父组件事件传递]

    使用defineEmit定义当前组件含有的事件,并通过返回的上下文去执行 emit。

    代码示列

    <script setup>
      import { defineEmits } from 'vue'
      const emit = defineEmits(['change', 'delete'])
      
    </script>

    父子组件通信

    defineProps 用来接收父组件传来的 props ; defineEmits 用来声明触发的事件。

    //父组件
    <template>
    	<Child @getChild="getChild" :title="msg" />
    </template>
    
    <script setup>
    import { ref } from 'vue'
    import Child from '@/components/Child.vue'
    const msg = ref('parent value')
    const getChild = (e) => {
    	// 接收父组件传递过来的数据
    	console.log(e); // child value
    }
    </script>
    //子组件
    <template>
    	<div @click="toEmits">Child Components</div>
    </template>
    
    <script setup>
    // defineEmits,defineProps无需导入,直接使用
    const emits = defineEmits(['getChild']);
    const props = defineProps({
    	title: {
    		type: String,
    		defaule: 'defaule title'
    	}
    });
    
    const toEmits = () => {
    	emits('getChild', 'child value') // 向父组件传递数据
    }
    
    // 获取父组件传递过来的数据
    console.log(props.title); // parent value
    </script>

    子组件通过 defineProps 接收父组件传过来的数据,子组件通过 defineEmits 定义事件发送信息给父组件

    useSlots()useAttrs()

    获取 slots 和 attrs

    注:useContext API 被弃用,取而代之的是更加细分的 api。

    可以通过useContext从上下文中获取 slots 和 attrs。不过提案在正式通过后,废除了这个语法,被拆分成了useAttrsuseSlots

    <template>
        <component v-bind='attrs'></component>
    </template>
    <srcipt setup>
       const attrs = useAttrs();
    <script>

    使用示例:

    // 旧
    <script setup>
      import { useContext } from 'vue'
    
      const { slots, attrs } = useContext()
    </script>
    
    // 新
    <script setup>
      import { useAttrs, useSlots } from 'vue'
    
      const attrs = useAttrs()
      const slots = useSlots()
    </script>

    defineExpose API

    defineExpose ----> [组件暴露出自己的属性]

    传统的写法,我们可以在父组件中,通过 ref 实例的方式去访问子组件的内容,但在 script setup 中,该方法就不能用了,setup 相当于是一个闭包,除了内部的 template模板,谁都不能访问内部的数据和方法。

    <script setup> 的组件默认不会对外部暴露任何内部声明的属性。
    如果有部分属性要暴露出去,可以使用 defineExpose

    注意:目前发现defineExpose暴露出去的属性以及方法都是 unknown 类型,如果有修正类型的方法,欢迎评论区补充。

    如果需要对外暴露 setup 中的数据和方法,需要使用 defineExpose API。示例

    //子组件
    
    <template>
    	{{msg}}
    </template>
    
    <script setup>
    import { ref } from 'vue'
    
    let msg = ref("Child Components");
    let num = ref(123);
    
    // defineExpose无需导入,直接使用
    defineExpose({
    	msg,
    	num
    });
    </script>
    //父组件
    <template>
    	<Child ref="child" />
    </template>
    
    <script setup>
    import { ref, onMounted } from 'vue'
    import Child from '@/components/Child.vue'
    
    let child = ref(null);
    
    onMounted(() => {
    	console.log(child.value.msg); // Child Components
    	console.log(child.value.num); // 123
    })
    </script>

    定义响应变量、函数、监听、计算属性computed

    <script setup > 
    import { ref,computed,watchEffect } from 'vue';
    
    const count = ref(0); //不用 return ,直接在 templete 中使用
    
    const addCount=()=>{ //定义函数,使用同上 
        count.value++; 
    } 
    
    //创建一个只读的计算属性 ref:
    const plusOne = computed(() => count.value + 1)
    
    // 创建一个可写的计算属性 ref
    const plusOne = computed({
    get: () => count.value + 1, 
    set: (val) => { count.value = val - 1 } 
    })
    
    
    //定义监听,使用同上 //...some code else 
    watchEffect(()=>console.log(count.value)); 
    </script>

    watchEffect和watch区别

    1、watch是惰性执行,也就是只有监听的值发生变化的时候才会执行,但是watchEffect不同,每次代码加载watchEffect都会执行(忽略watch第三个参数的配置,如果修改配置项也可以实现立即执行)

    2、watch需要传递监听的对象,watchEffect不需要

    3、watch只能监听响应式数据:ref定义的属性和reactive定义的对象,如果直接监听reactive定义对象中的属性是不允许的,除非使用函数转换一下

    4、watchEffect如果监听reactive定义的对象是不起作用的,只能监听对象中的属性。

    reactive

    返回一个对象的响应式代理。

    <script setup>
    import { reactive, onUnmounted } from 'vue'
    
    const state = reactive({
        counter: 0
    })
    // 定时器 每秒都会更新数据
    const timer = setInterval(() => {
        state.counter++
    }, 1000);
    
    onUnmounted(() => {
        clearInterval(timer);
    })
    </script>
    <template>
        <div>{{state.counter}}</div>
    </template>

    使用ref也能达到我们预期的'counter',并且在模板中,vue进行了处理,我们可以直接使用counter而不用写counter.value.

    ref和reactive的关系:

    ref是一个{value:'xxxx'}的结构,value是一个reactive对象

    ref 暴露变量到模板

    曾经的提案中,如果需要暴露变量到模板,需要在变量前加入export声明:

    export const count = ref(0)

    不过在新版的提案中,无需export声明,编译器会自动寻找模板中使用的变量,只需像下面这样简单的声明,即可在模板中使用该变量

    <script setup >
    import { ref } from 'vue'
    
    const counter = ref(0);//不用 return ,直接在 templete 中使用
    
    const timer = setInterval(() => {
        counter.value++
    }, 1000)
    
    onUnmounted(() => {
        clearInterval(timer);
    })
    </script>
    <template>
        <div>{{counter}}</div>
    </template>

    其他 Hook Api

    支持 async await 异步

    注意在vue3的源代码中,setup执行完毕,函数 getCurrentInstance 内部的有个值会释放对 currentInstance 的引用,await 语句会导致后续代码进入异步执行的情况。所以上述例子中最后一个 getCurrentInstance() 会返回 null,建议使用变量保存第一个 getCurrentInstance() 返回的引用.

    <script setup>
      const post = await fetch(`/api/post/1`).then((r) => r.json())
    </script>

    <script setup> 中可以使用顶层 await。结果代码会被编译成 async setup()

    <script setup>
    const post = await fetch(`/api/post/1`).then(r => r.json())
    </script>

    另外,await 的表达式会自动编译成在 await 之后保留当前组件实例上下文的格式。

    注意
    async setup() 必须与 Suspense 组合使用,Suspense 目前还是处于实验阶段的特性。我们打算在将来的某个发布版本中开发完成并提供文档 - 如果你现在感兴趣,可以参照 tests 看它是如何工作的。

    定义组件其他配置

    配置项的缺失,有时候我们需要更改组件选项,在setup中我们目前是无法做到的。我们需要在上方再引入一个 script,在上方写入对应的 export即可,需要单开一个 script。

    <script setup> 可以和普通的 <script> 一起使用。普通的 <script> 在有这些需要的情况下或许会被使用到:

    在script setup 外使用export default,其内容会被处理后放入原组件声明字段。

    <script>
    // 普通 `<script>`, 在模块范围下执行(只执行一次)
    runSideEffectOnce()
    
    // 声明额外的选项
      export default {
        name: "MyComponent",
        inheritAttrs: false,
        customOptions: {}
      }
    </script>
    <script setup>
        import HelloWorld from '../components/HelloWorld.vue'
        // 在 setup() 作用域中执行 (对每个实例皆如此)
        // your code
    </script>
    <template>
      <div>
        <HelloWorld msg="Vue3 + TypeScript + Vite"/>
      </div>
    </template>

    注意:Vue 3 SFC 一般会自动从组件的文件名推断出组件的 name。在大多数情况下,不需要明确的 name 声明。唯一需要的情况是当你需要 <keep-alive> 包含或排除或直接检查组件的选项时,你需要这个名字。

    (学习视频分享:web前端开发编程基础视频

    以上就是一文详解Vue3中的script setup语法糖的详细内容,更多请关注php中文网其它相关文章!

    声明:本文转载于:掘金社区,如有侵犯,请联系admin@php.cn删除

    前端(VUE)零基础到就业课程:点击学习

    清晰的学习路线+老师随时辅导答疑

    自己动手写 PHP MVC 框架:点击学习

    快速了解MVC架构、了解框架底层运行原理

    专题推荐:前端 Vue.js
    上一篇:深入了解Vue中的自定义指令 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • ❤️‍🔥共22门课程,总价3725元,会员免费学• ❤️‍🔥接口自动化测试不想写代码?• 一文聊聊Vue中的KeepAlive组件• Vue.js实战入门:如何对数组和对象进行循环• 实战:vscode中开发一个支持vue文件跳转到定义的插件• 深入理解vue2中的VNode和diff算法• vue3安装太卡怎么办
    1/1

    PHP中文网