目录
创建项目
vue-cli:需要版本在4.5.0以上。
npm init vite@latest
setup
setup
是所有组合API的“表演舞台”。
组件中用到的数据、方法等等,都要配置在setup
中。也就是说不在需要单写data
、methods
、声明周期
等等。
setup
函数的两种返回值:
1、若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。(重点)
<template>
<h1>App组件</h1>
<p>姓名:{{name}}</p>
<p>年龄:{{age}}</p>
<button @click="sayHello">按钮</button>
</template>
<script>
export default {
name: 'App',
setup() {
let name = "Tom";
let age = 23;
const sayHello = function() {
alert(`我叫${name},今年${age}岁。`);
};
// 返回一个对象(常用)
return {
name,
age,
sayHello
};
}
}
</script>
2、若返回一个渲染函数。(了解)
h
需要两个参数,第一个是元素类型,第二个是内容。
如果返回一个渲染函数,无论模板中是什么,都只渲染渲染函数中的内容。
import { h } from '@vue/runtime-core';
export default {
name: 'App',
setup() {
let name = "Tom";
let age = 23;
const sayHello = function() {
alert(`我叫${name},今年${age}岁。`);
};
// 返回一个渲染函数
return () => h('h1','哈哈哈哈')
}
}
注意:
- vue3尽量不要与vue2配置混用。
vue2.x的配置(data、methods、computed…)中可以访问到setup的属性、方法。
但是在setup中不能访问到vue2.x的配置(data、methods、computed…)
如果有重名,setup
优先。 setup
不能是一个async
函数。因为加入了async
,返回值就不再是对象,而是promise,模板看不到return对象中的属性。
ref函数
我们写一个按钮,给按钮绑定一个点击事件,想要实现点击更新页面显示信息。
<template>
<h1>App组件</h1>
<p>姓名:{{name}}</p>
<p>年龄:{{age}}</p>
<button @click="changeInfo">修改信息</button>
</template>
<script>
export default {
name: 'App',
setup() {
let name = "Tom";
let age = 23;
function changeInfo() {
name = "李四";
age = 48;
}
return {
name,
age,
changeInfo,
};
}
}
</script>
点击以后发现页面并没有发生改变,但是我们在changeInfo()
中打印,会发现输出的name和age已经改变,但是由于vue没有检测到数据改变,所以没有渲染到页面上。
vue3有了一个新的ref
函数:
import {ref} from 'vue';
export default {
name: 'App',
setup() {
let name = ref("Tom");
let age = ref(23);
这时候我们发现点击按钮页面还是没有变化,先不在changeInfo()
里面修改数据,打印一下name,age:console.log(name.age);
发现被ref
绑定为响应式数据的name和age变成了对象。
需要加.value
触发原型对象上的getter方法改变数据。
import {ref} from 'vue';
export default {
name: 'App',
setup() {
let name = ref("Tom");
let age = ref(23);
function changeInfo() {
name.value = "李四";
age.value = 48;
console.log(name, age);
}
return {
name,
age,
changeInfo,
};
}
}
vue3的模板字符串不需要写.value
,当模板解析到ref
的时候,会自动加上.value
,所以我们写的 <p>年龄:{{age}}</p>
被vue解析成了 <p>年龄:{{age。value}}</p>
语法:cosnt xxx = ref(initValue)
- 创建一个包含响应式数据的引用对象(ref对象)
- js中操作数据:
xxx.value
- 模板中读取数据:不需要.value,直接
<p>{{xxxx}}</p>
备注:
- 接受的数据可以是基本数据类型,也可以是对象类型。
- 基本数据类型:响应式数据依然是靠
Object.defineProperty()
的get
和set
完成的。 - 对象类型的数据:内部使用了Vue3.0的一个新函数——
reactive
函数。
reactive函数
作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用ref
函数)
- 语法:
const 代理对象= reactive(源对象)
接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象) - reactive定义的响应式数据是“深层次的”。
- 内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
给上面用ref
写的job对象,换成reactive
,打印:
import {ref, reactive} from 'vue';
export default {
name: 'App',
setup() {
let name = ref("Tom");
let age = ref(23);
let job = reactive({
type: "门卫",
salary: "0k",
});
function changeInfo() {
name.value = "李四";
age.value = 48;
console.log(job);
}
return {
name,
age,
job,
changeInfo,
};
}
}
我们想要更改对象内的值,无需在.value:
function changeInfo() {
=======================
job.type = "保安";
job.salary = "1k";
}
上面的所有属性我们可以直接放在一个对象中,创建一个person
变量来接收这个对象,使用reactive
来使对象变为响应式。
import {reactive} from 'vue';
export default {
name: 'App',
setup() {
// let name = ref("Tom");
// let age = ref(23);
// let job = reactive({
// type: "门卫",
// salary: "0k",
// a:{
// b:{
// c: 300
// }
// }
// });
// let hobby = ["1", "2", "3"];
let person = reactive({
name: "Tom",
age: 18,
job: {
type: "门卫",
salary: "0k",
a: {
b: {
c: 300
}
},
hobby: ["吃饭", "睡觉", "打豆豆"],
}
})
function changeInfo() {
// name.value = "李四";
// age.value = 48;
// job.type = "保安";
// job.salary = "1k";
// job.a.b.c = 10101010;
// hobby[0] = 100;
person.name = "李四";
person.age = 22;
person.job.type = "保安";
person.job.salary = "1k";
person.job.a.b.c = 1008711;
person.job.hobby[2] = "烫头";
}
return {
person,
changeInfo,
};
}
}
Vue3的响应式原理
Vue2的响应式存在一些问题:
1、新增属性、删除属性,页面不会更新。
2、直接修改数组下标,页面不会更新。
模拟响应式:
Vue2的响应式是通过Object.defineProperty()
来实现的:
let person = {
name: "张三",
age: "30"
}
// 模拟Vue2实现响应式
let p = {}
Object.defineProperty(p, "name",{
get(){
return person.name
},
set(Newvalue) {
console.log("模拟响应式修改name属性")
return person.name = Newvalue
}
})
Vue3采用es6的Proxy代理对象和Reflect反射对象来实现响应式。
let person = {
name: "张三",
age: "30"
}
const p = new Proxy(person, {
// 查
get(target, propName){
console.log(`读取了${propName}属性`)
return Reflect.get(target, propName)
},
// 改和增
set(target, propName, value){
console.log(`修改了${propName}为${value}`);
Reflect.set(target, propName, value)
},
// 删
deleteProperty(target, propName){
console.log(`删除了${propName}属性`)
return Reflect.defineProperty(target, propName)
}
})
以上只是模拟,实际Vue的底层原理不是这么low。
reactive对比ref
- 从定义数据角度对比:
- ref用来定义:基本类型数据。
- reactive用来定义:对象(或数组)类型数据。
- 备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过
reactive
转为代理对象。
- 从原理角度对比:
- ref通过
Object.defineProperty()
的get
与set
来实现响应式(数据劫持)。 - reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
- ref通过
- 从使用角度对比:
- ref定义的数据:操作数据需要
.value
,读取数据时模板中直接读取不需要.value
。 - reactive定义的数据:操作数据与读取数据:均不需要
.value
。
- ref定义的数据:操作数据需要
setup注意点
- setup的执行时机
- 在
beforeCreate
之前执行一次,this是undefined
beforeCreate() { console.log("----start beforeCreate-----"); }, setup() { console.log("-----start setup------"); console.log(this); },
- 在
- setup接受的参数
- props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。如果不在setup顶部使用props接受外部给的数据,会报警告。
props:['age'], setup(props) { console.log(props); },
- context:上下文对象
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
。 - slots: 收到的插槽内容, 相当于
this.$slots
。 vue3舍弃了slot="xxxx"
的用法,只有v-solt:xxxx
一种用法。 - emit: 分发自定义事件的函数, 相当于
this.$emit
。
- attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
computed:计算属性
- 与Vue2.x中computed配置功能一致
- 简写形式:可以写成函数,没有考虑计算结果被修改的情况。只使用了
set
- 复杂形式:只能写成对象,考虑了计算结果被修改的情况。
- 简写形式:可以写成函数,没有考虑计算结果被修改的情况。只使用了
<template>
<h1>个人信息</h1>
姓:<input type="text" v-model="person.firstName">
名:<input type="text" v-model="person.lastName">
<br>
<p>姓名:{{person.fullName}}</p>
<br>
全名:<input type="text" v-model="person.fullName">
</template>
<script setup>
import {reactive,computed} from "vue";
const person = reactive({
firstName:"",
lastName:"",
});
// 计算属性简写:没有考虑计算属性被修改
person.fullName = computed(() => {
return person.firstName +" " + person.lastName
});
// 计算属性完整写法,考虑计算属性被修改。是一个对象
person.fullName = computed({
get(){
return person.firstName + " " + person.lastName;
},
set(value){
const nameArr = value.split(" ")
person.firstName = nameArr[0]
person.lastName = nameArr[2]
}
})
</script>
watch:监视属性
Vue3中watch
成了一个组合api,所以需要引入。Vue3中的watch
可以监视一个或者多个属性。
watch
可以接收三个参数:第一个是被监视的属性名字,第二是监视的回调函数,第二个是监视的配置:immediate
等等。
监视一个:
<template>
<h1>当前求和为:{{ sum }}</h1>
<button @click="sum++">点击+1</button>
</template>
<script>
import { ref, watch } from "vue";
export default {
setup() {
let sum = ref(0);
// 情况1:监视ref定义的单个响应式数据
watch(sum, (newValue, oldValue)=>{
console.log("sum值变化", newValue, oldValue);
})
return {
sum,
};
},
};
</script>
watch
监听的必须是响应式数据,如果是通过refs
解构出来的,则需要写成() =>
形式:
import { ref, reactive, toRefs, watch } from 'vue'
let obj = {
num: 30
}
let objRet = reactive(obj)
let { num } = toRefs(objRet)
watch(() => obj.num, () => {
})
监视多个:把被监视的属性写在一个数组里面。
// 情况2:监视ref定义的多个响应式数据
watch([sum, msg], (newValue, oldValue)=>{
console.log("sum或者msg值变化", newValue, oldValue);
})
监视的第三个参数:监视的配置
watch(sum, (newValue, oldValue)=>{
console.log("sum值变化", newValue, oldValue);
},{immediate:true})
Vue3监视属性的坑:
监视reactive的对象响应式数据,但是Vue3中存在一些bug:无法获取oldValue
:
let person = reactive({
name: "张三",
age: 23,
})
watch(person,(newValue, oldValue)=>{
console.log("变化了", newValue, oldValue);
})
执行代码会发现打印出的oldValue是错误的:
Vue3强制开启了深度监视(deep配置无效)
<template>
<h1>薪资:{{person.job.work.salary}}</h1>
<button @click="person.job.work.salary++">点击涨薪</button>
</template>
<script>
import { ref, reactive, watch } from "vue";
export default {
setup() {
let person = reactive({
job:{
work:{
salary: 2
}
}
})
// 默认开启深度监视,无法关闭
watch(person,(newValue, oldValue)=>{
console.log("变化了", newValue, oldValue);
},{deep: false})
return {
sum,
msg,
person,
}
}
}
</script>
以上代码中,我们配置了deep: false
所以person.job.work.salary
按理说应该监视不到,但是实际却是可以监听到:
监视reactive定义的一个响应式数据中的某一个属性
需要把想检测的那个属性写成方法:(该方法可以正确的监测oldValue
)
<template>
<h1>名字:{{person.name}}</h1>
<h1>年龄:{{person.age}}</h1>
<h1>薪资:{{person.job.work.salary}}</h1>
<button @click="person.name+='e'">点击修改name</button>
<button @click="person.age++">点击修改年龄</button>
<button @click="person.job.work.salary++">点击涨薪</button>
</template>
<script>
import { reactive, watch } from "vue";
export default {
setup() {
let person = reactive({
name: "张三",
age: 23,
job:{
work:{
salary: 2
}
}
})
// 情况4:监视reactive所定义的一个响应式数据中的某个属性
watch(()=>person.age,(oldValue, newValue)=>{
console.log("age改变了", oldValue, newValue);
})
return {
person,
}
}
}
</script>
上面代码想要监视person中的name属性,所以写了()=>person.name
来监视person中的name属性。
监视reactive中的某一个响应式数据的某些属性:(和监视ref一样,写成数组)
// 情况5:监视reactive所定义的一个响应式数据中的某些属性
watch([()=>person.age, ()=>person.name], (oldValue, newValue)=>{
console.log("age或者name改变了", oldValue, newValue);
})
特殊情况: 如果我们想监视person下的job,按照正常思路我们会写成:
// 特殊情况
watch(()=>person.job,(oldValue, newValue)=>{
console.log("person的job改变了", oldValue, newValue);
})
但是我们如果这么写的话,会发现监视不到,但是我们加上deep:true
开启深度监视,就可以监视到了:
// 特殊情况
watch(()=>person.job,(oldValue, newValue)=>{
console.log("person的job改变了", oldValue, newValue);
}, {deep: true})
总结:当我们监视的reactive中的某一个属性,但是该属性依然是一个对象的时候,需要deep:true
开启深度监视才能监视到。
watchEffect函数
-
watchEffect的用法是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。
-
watchEffect有点像computed:
- 但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
import { ref, reactive, watchEffect } from "vue";
export default {
setup() {
let sum = ref(0)
let msg = ref("hello")
let person = reactive({
name: "张三",
age: 23,
job:{
work:{
salary: 2
}
}
})
watchEffect(()=>{
// 使用了谁就去监听谁
const x1 = sum.value
const x2 = person.job.work.salary
console.log("指定的回调执行了", x1, x2)
})
return {
sum,
msg,
person,
}
}
}
Vue3的生命周期
Vue3依然可以使用Vue2的生命周期钩子,但是有两个被改名了:
beforeDestory
被更名为beforeUnmount
destroyde
被更名为unmounted
Vue3.0也提供了 Composition API 形式的生命周期钩子,与Vue2.x中钩子对应关系如下:
beforeCreate
===>setup()
created
=======>setup()
beforeMount
===>onBeforeMount
mounted
=======>onMounted
beforeUpdate
===>onBeforeUpdate
updated
=======>onUpdated
beforeUnmount
==>onBeforeUnmount
unmounted
=====>onUnmounted
因为变成了函数,所以需要引入import { } from "vue"
,语法:
import { ref, onBeforeMount } from "vue"
export default {
setup() {
let sum = ref(0)
onBeforeMount(()=>{
console.log("----------onBeforeMount----------");
})
return {
sum
}
}
}
toRef和toRefs
toRef
可以让一个变量接收指定的一个响应式数据的某个属性。
语法toRef(哪个对象, "对象中的哪个属性")
<template>
<h1>名字:{{name}}</h1>
<h1>年龄:{{person.age}}</h1>
<h1>薪资:{{salary}}</h1>
<button @click="name+='e'">点击修改name</button>
<button @click="person.age++">点击修改年龄</button>
<button @click="salary++">点击涨薪</button>
</template>
<script setup>
import {reactive, toRef} from "vue"
let person = reactive({
name: "张三",
age: 22,
job:{
work:{
salary: 3
}
}
})
let name = toRef(person, 'name')
let salary = toRef(person.job.work, "salary")
</script>
toRefs
语法:toRefs(要解析的响应式对象)
与toRef
功能一致,可以批量处理多个ref
对象,如果ref
是多层嵌套,toRefs
只能解析到最外面一层:
import {reactive, toRef, toRefs} from "vue"
let person = reactive({
name: "张三",
age: 22,
job:{
work:{
salary: 3
}
}
})
let salary = toRefs(person)
console.log(salary);
shallowReactive
浅响应式数据:只处理对象的最外层属性的响应式。
<template>
<h1>名字:{{person.name}}</h1>
<h1>年龄:{{person.age}}</h1>
<h1>薪资:{{person.job.work.salary}}</h1>
<button @click="person.name+='e'">点击修改name</button>
<button @click="person.age++">点击修改年龄</button>
<button @click="person.job.work.salary++">点击涨薪</button>
</template>
<script setup>
import {shallowReactive} from "vue"
let person = shallowReactive({
name: "张三",
age: 22,
job:{
work:{
salary: 3
}
}
})
</script>
shallowRef
只处理基本类型的响应式,不处理对象类型的响应式。
如果只像下面让 shallowRef
处理基本数据类型,那么可以处理未响应式。
<template>
<h1>{{x}}</h1>
<button @click="x++">点击x+1</button>
</template>
<script setup>
import {shallowReactive, shallowRef} from "vue"
let x = shallowRef(0)
</script>
当shallowRef
处理对象数据类型的时候,则不会变为响应式:
<template>
<h1>{{x}}</h1>
<button @click="x++">点击x+1</button>
</template>
<script setup>
import {shallowReactive, shallowRef} from "vue"
let x = shallowRef({
y:0
})
</script>
shallowReactive和shallowRef使用情况
- 如果有一个对象数据,结构比较深, 但变化时只是外层属性变化 ===> shallowReactive。
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换 ===> shallowRef。
readonly
设置一个响应式对象为只读模式:
<template>
<h1>和为:{{sum}}</h1>
<button @click="sum++">点击++</button>
<hr>
<h1>名字:{{person.name}}</h1>
<h1>年龄:{{person.age}}</h1>
<h1>薪资:{{person.job.work.salary}}</h1>
<button @click="person.name+='e'">点击修改name</button>
<button @click="person.age++">点击修改年龄</button>
<button @click="person.job.work.salary++">点击涨薪</button>
</template>
<script setup>
import {reactive, ref, readonly} from "vue"
let person = reactive({
name: "张三",
age: 22,
job:{
work:{
salary: 3
}
}
})
person = readonly(person)
let sum = ref(0)
</script>
上面代码把person
这个响应式数据设置为了readonly
,所以不能对他进行修改,当我们尝试更改的时候,控制台会报出警告。
shallowReadonly
让一个响应式数据变为只读的(浅只读)。
<template>
<h1>和为:{{sum}}</h1>
<button @click="sum++">点击++</button>
<hr>
<h1>名字:{{person.name}}</h1>
<h1>年龄:{{person.age}}</h1>
<h1>薪资:{{person.job.work.salary}}</h1>
<button @click="person.name+='e'">点击修改name</button>
<button @click="person.age++">点击修改年龄</button>
<button @click="person.job.work.salary++">点击涨薪</button>
</template>
<script setup>
import {reactive, ref, shallowReadonly} from "vue"
let person = reactive({
name: "张三",
age: 22,
job:{
work:{
salary: 3
}
}
})
person = shallowReadonly(person)
let sum = ref(0)
</script>
上面的代码把person
定义为了浅只读,所以我们在修改person
最外层的name,age属性的时候,是不可以的,但是修改person,job.work.salary
就可以更改,因为它是嵌套在job内部。
toRaw
将一个reactive
生成的响应式对象转为普通对象。
<template>
<h1>名字:{{person.name}}</h1>
<h1>年龄:{{person.age}}</h1>
<h1>薪资:{{person.job.work.salary}}</h1>
<button @click="person.name+='e'">点击修改name</button>
<button @click="person.age++">点击修改年龄</button>
<button @click="person.job.work.salary++">点击涨薪</button>
<button @click="showRawPerson">输出原始的信息</button>
</template>
<script setup>
import {reactive} from "vue"
let person = reactive({
name: "张三",
age: 22,
job:{
work:{
salary: 3
}
}
})
function showRawPerson() {
const p = (person)
console.log(p)
}
</script>
上面定义了一个showRawPerson
的点击事件来获取原始的person。如果不加toRaw
获取的就是Proxy
代理的数据:
而我们想要获取最原始的数据,就需要const p = toRaw(person)
:
markRaw
标记一个对象,使其永远不会再成为响应式对象。
<template>
<h1>名字:{{person.name}}</h1>
<h1>年龄:{{person.age}}</h1>
<h1>薪资:{{person.job.work.salary}}</h1>
<h2 v-show="person.car">座驾:{{person.car}}</h2>
<button @click="person.name+='e'">点击修改name</button>
<button @click="person.age++">点击修改年龄</button>
<button @click="person.job.work.salary++">点击涨薪</button>
<button @click="showRawPerson">输出原始的信息</button>
<button @click="getCar">买一辆车</button>
<button @click="changeCarName">换车</button>
</template>
<script setup>
import {reactive, toRaw, markRaw} from "vue"
let person = reactive({
name: "张三",
age: 22,
job:{
work:{
salary: 3
}
}
})
function showRawPerson() {
const p = toRaw(person)
console.log(p)
}
function getCar() {
let car = {name: "自行车", price: 200}
person.car = markRaw(car)
}
function changeCarName() {
let newName = person.car.name += "!"
console.log(newName)
return newName
}
</script>
上面我们给person.car
添加了markRaw
属性,使其不会再成为响应式对象,为了验证确实触发了点击事件,可以打印一下:
markRaw和readOnly的区别
markRaw是可以更改数据,但是无法成为响应式。
readOnly直接无法更改数据。
响应式数据的判断
isRef
:检查一个值是否为一个ref对象
isReactive
:检查一个对象是否由reactive
创建的响应式数据
isReadonly
:检查一个对象是否由readOnly
穿件的只读代理
isProxy
:检查一个对象是否由reactive
或者readOnly
方法创建的代理
新组件
Fragment
- 在Vue2中: 组件必须有一个根标签
- 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
- 好处: 减少标签层级, 减小内存占用
Suspense组件(实验性api)
异步引入组件
import {defineAsyncComponent} from "vue"
const Demo = defineAsyncComponent(()=>import('./components/Demo.vue'))
使用Suspense
包裹组件,并配置好default
与fallback
<template>
<div class="app">
<h3>我是App组件</h3>
<Suspense>
<template v-slot:default>
<Child/>
</template>
<template v-slot:fallback>
<h3>加载中.....</h3>
</template>
</Suspense>
</div>
</template>
全局api转译
- Vue2.x有许多全局api和配置
- 例如:注册全局组件、注册全局指令等。
//注册全局组件
Vue.component('MyButton', {
data: () => ({
count: 0
}),
template: '<button @click="count++">Clicked {{ count }} times.</button>'
})
//注册全局指令
Vue.directive('focus', {
inserted: el => el.focus()
}
-
Vue3.0中对这些API做出了调整:
-
将全局的API,即:
Vue.xxx
调整到应用实例(app
)上2.x 全局 API( Vue
)3.x 实例 API ( app
)Vue.config.xxxx app.config.xxxx Vue.config.productionTip 移除 Vue.component app.component Vue.directive app.directive Vue.mixin app.mixin Vue.use app.use Vue.prototype app.config.globalProperties
-
其它改变
-
data选项应始终被声明为一个函数。
-
过度类名的更改:
-
Vue2.x写法
.v-enter, .v-leave-to { opacity: 0; } .v-leave, .v-enter-to { opacity: 1; }
-
Vue3.x写法
.v-enter-from, .v-leave-to { opacity: 0; } .v-leave-from, .v-enter-to { opacity: 1; }
-
-
移除keyCode作为 v-on 的修饰符,同时也不再支持
config.keyCodes
-
移除
v-on.native
修饰符-
父组件中绑定事件
<my-component v-on:close="handleComponentEvent" v-on:click="handleNativeClickEvent" />
-
子组件中声明自定义事件
<script> export default { emits: ['close'] } </script>
-
-
移除过滤器(filter)
过滤器虽然这看起来很方便,但它需要一个自定义语法,打破大括号内表达式是 “只是 JavaScript” 的假设,这不仅有学习成本,而且有实现成本!建议用方法调用或计算属性去替换过滤器。
Vue3.2语法糖
script setup
<script setup>
是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖。相比于普通的 <script>
语法,它具有更多优势:
更少的样板内容,更简洁的代码。
能够使用纯 Typescript 声明 props 和抛出事件。
更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。
基本语法:
<script setup>
console.log('hello script setup')
</script>
当我们使用script setup
语法的时候,无需再书写export default{}
。
defineProps 和 defineEmits
当我们使用了script setup
语法糖,我们需要使用defineProps
和 defineEmits
API来声明 props
和 emits
:
<script setup>
const props = defineProps({
foo,
})
const emit = defineEmits(['change', 'delete'])
</script>
defineProps
和defineEmits
只能在script setup
中使用,而且无需引入defineProps
接收与props
选项相同的值,defineEmits
也接收emits
选项相同的值。
defineExpose
在vue3的setup
语法糖定义的变量,默认不会对外暴露
为了在 <script setup>
组件中明确要暴露出去的属性,使用 defineExpose 编译器宏:
<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
defineExpose({
a,
b
})
</script>