script setup 语法糖

组合式 API:setup()

基本运用

Vue 3 的 Composition API 系列里,推出了一个全新的setup函数,它是一个组件选项,在创立组件之前履行,一旦 props 被解析,并作为组合式 API 的进口点。

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的调用发生在dataproperty、computedproperty 或methods被解析之前,所以它们无法>在setup中被获取。

在添加了setup的script标签中,咱们不必声明和办法,这种写法会主动将一切尖端变量、函数,均会主动露出给模板(template)运用
这里强调一句 “露出给模板,跟露出给外部不是一回事

TIP:
说的浅显一点,便是在运用 Vue 3 生命周期的状况下,整个组件相关的业务代码,都能够放在setup里履行。

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

组件生命周期

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

Vue 2 生命周期 Vue 3 生命周期 履行时间阐明
beforeCreate setup 组件创立前履行
created setup 组件创立后履行
beforeMount onBeforeMount 组件挂载到节点上之前履行
mounted onMounted 组件挂载完结后履行
beforeUpdate onBeforeUpdate 组件更新之前履行
updated onUpdated 组件更新完结之后履行
beforeDestroy onBeforeUnmount 组件卸载之前履行
destroyed onUnmounted 组件卸载完结后履行
errorCaptured onErrorCaptured 当捕获一个来自子孙组件的反常时激活钩子函数

能够看到 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 lang="ts"> 
    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

  1. useAttrs:见名知意,这是用来获取 attrs 数据,可是这和 vue2 不同,里边包括了class特点办法
<template>
    <component v-bind='attrs'></component>
</template>
<srcipt setup lang='ts'>
   const attrs = useAttrs();
<script>
  1. useSlots: 望文生义,获取插槽数据。

运用示例:

// 旧
<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

  1. useCSSModule:CSS Modules 是一种 CSS 的模块化和组合系统。vue-loader 集成 CSS Modules,能够作为模仿 scoped CSS。允许在单个文件组件的setup中拜访CSS模块。此 api 本人用的比较少,不过多做介绍。
  2. useCssVars: 此 api 暂时材料比较少。介绍v-bind in styles时提到过。
  3. useTransitionState: 此 api 暂时材料比较少。
  4. useSSRContext: 此 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>声明的选项,例如inheritAttrs或经过插件启用的自界说的选项。
  • 声明命名导出。
  • 运行副作用或许创立只需求履行一次的目标。

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

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

留意:Vue 3 SFC 一般会主动从组件的文件名推断出组件的 name。在大多数状况下,不需求明确的 name 声明。仅有需求的状况是当你需求<keep-alive>包括或排除或直接查看组件的选项时,你需求这个姓名。

参阅

Gaby

Vue

[木亦Sam](上手后才知道 ,Vue3 的 script setup 语法糖是真的爽 – ())