简介:
<script setup> 是在单文件组件(SFC)中使用组合式 API 的编译时语法糖。相比于普通的<script>语法,它具有更多优势:
-
更少更简洁的代码,不需要使用
return{}
暴露变量和方法了,使用组件时不需要主动注册了 -
更好的
Typescript
支持,使用纯Typescript
声明props
和抛出事件 -
更好的运行时性能 (其模板会被编译成与其同一作用域的渲染函数,没有任何的中间代理)。
-
更好的 IDE 类型推断性能 (减少语言服务器从代码中抽离类型的工作)。
<script setup>语法糖用法
基本语法:
要使用这个语法,在script 中添加setup属性即可。
<script setup></script>
在<script setup>语法糖中以下代码都省略掉了
//在<script setup>语法糖中省略了export default导出,及注册组件,和setup钩子函数
export default {
components:
{
Child
},
setup() {
return { }
}
}
案列:
app.vue父组件
<template>
<div>
<button @click="myClick">App.Vue点我改名</button>
<div>{{ user.name }}--{{ user.age }}--{{ user.email }}</div>
<!-- toRefs的使用 -->
<div>{{ name }}--{{ age }}--{{ email }}</div>
<div v-mydir="user">计算属性包装后的name:{{ computedNmae }}</div>
<div v-if="isShow">我根据isShow对象值来决定是否加载</div>
<!-- 自定义组件 -->
<Child :mytitle="title" state="error" @myevent="childHandelClick" @myevent2="childHandelClick2"></Child>
</div>
</template>
<script setup>
import { ref, provide, toRef, toRefs, reactive, computed, markRaw, shallowRef, watch, onMounted } from 'vue';
//导入模板即注册:注册的名字就是你导入用的名称Child
import Child from "./components/Child.vue" //导入Child组件:
import BChild from "./components/BChild.vue" //导入Child组件:
//在<script setup> 中定义好对象,在Dom中就直接可以用了,不需要再return{ title,isShow ,user }出去才能用了
const title = ref("我是标题")
const isShow = ref(true);
const user = ref({
name: "张三",
age: 18,
email: "123@qq.com"
})
//toRefs的使用:将响应式对象user中的所有属性转换为单独的响应式数据暴露出去:相当于 const name=ref(user.name); const age=ref(user.age);const email=ref(user.email)
const { name, age, email } = { ...toRefs(user.value) }
//函数的使用
const myClick = () => {
user.value.name = "王五"
}
//监听事件1:用于子组件的回调
const childHandelClick = (name, age) => {
console.log(name)
console.log(age);
}
//监听事件2:用于子组件的回调
const childHandelClick2 = (data) => {
console.log(data.email);
console.log(data.url)
console.log(data.port)
}
//计算属性的使用
const computedNmae = computed(() => {
return "中华人民共和国:" + user.value.name
})
const myFun = () => {
user.value.name = "李世民"
}
//依赖注入的使用:provide是提供者:向后代组件提供数据
provide("isShow", isShow);
provide("myFun", myFun, true); //向后代组件提供一个函数,如果提供的是一个函数,第三个参数必须为true 在注入的时候用法:const myfun=inject("myFun"); 然后可以直接执行函数:myfun()
// watch 侦听器的使用:侦听一个数据源
watch(title, (newValue, oldValue) => {
console.log(`title发生了变化,老值为:${oldValue},--新值为:${newValue}`)
}, { immediate: true, deep: false })
// watch 侦听器的使用:侦听多个数据源
// watch([mytext,myselect], (newValue, oldValue)=>{console.log("新的值",newValue); console.log("老的值",oldValue)}):这段代码可以用如下解构的形式来写
watch([title, isShow], ([newTitle, newIsShow], [oldTitle, oldIsShow]) => {
console.log(`title或者isShow变化了`);
console.log("newTitle", newTitle)
console.log("oldTitle", oldTitle)
console.log("newIsShow", newIsShow)
console.log("oldIsShow", oldIsShow)
})
//钩子函数的使用
onMounted(() => {
// 自定义逻辑
})
//自定义指令:有created,beforeMount,mounted,beforeUpdate,updated,beforeUnmount,unmounted这些钩子函数
//自定义指令:必须以小写字母v开头的小驼峰 的格式来命名本地自定义指令:指令中有多个钩子函数,我们常用就以下两种
//举列:名字为vHostKey的指令 Dom中的写法应该是:v-host-key 或者v-hostKey 或者v-HostKey
const vMydir = {
//el表示指令所绑定组件的元素
//binding表示指令所传递的值:例如:<div v-mydir="user"> 这指令传递的值就是user对象
mounted: (el, binding, vnode) => {
el.style.background = 'red'
console.log(binding.value.name); //输出:张三
console.log(binding.value.age); //输出:18
console.log(binding.value.email); //输出:123@qq.com
},
updated: (el, binding, vnode) => {
}
}
</script>
Child.vue子组件
<template>
<div>
{{ props.mytitle }} <!--或者直接用mytitle,不用props.mytitle也行-->
</div>
<div>
{{ props.state }} <!--或者直接用mytitle,不用props.mytitle也行-->
</div>
<div><input type="text"></div>
<button @click="handelClick">向父组件发起问候传递参数</button>
<button @click="handelClick2">向父组件发起问候传递参数2</button>
<button @click="handelClick3">调用祖组件提供的的myFun函数</button>
</template>
<script setup>
import { inject, ref, defineProps, defineEmits } from 'vue';
//父组件向子组件传值:具体可参阅:vue3.0中父组件与子组件的通信传值props与$emit :VOA模式文章
//这里接收父组件传递过来的state和mytitle值
//简写:const props = defineProps({state:String,mytitle:String})
const props = defineProps({
state: {
required: true, //true 表示属性必传;false 表示可以不传属性;
type: String, //表示属性只能是String类型的。也可以指定多个类型如type:[String,Number]
default() {// 如果该参数没用值则指定一个默认值abc ;也可以直接default: "abc"
return "abc"
},
//也可以写成:validator:value=>['success','warning','danger','error'].indexOf(value)>-1
validator(val) {
//检查参数的值是否是"success"或者"warning"或者"danger"或者"error",如果是则返回true,不是则返回false
return ["success", "warning", "danger", "error"].includes(val)
}
},
mytitle: {
type: String
}
})
//注入:祖组件通过provide提供了isShow对象和myFun函数,这里接收
const isShow = inject("isShow");
const myFun = inject("myFun");
//获取父组件的监听,向父组件通信传递参数
const emits = defineEmits(["myevent", "myevent2"]); //获取父组件的"myevent","myevent2"监听事件
const handelClick = () => {
emits("myevent", "王五", 19) //调用父组件的myevent监听事件,并传值
}
const handelClick2 = () => {
emits("myevent2", { email: "woo@qq.com", url: "http://localhost:5173/", port: 8080 }) //调用父组件的myevent2监听事件,并传值
}
const handelClick3 = () => {
isShow.value = !isShow.value;
myFun(); //执行祖组件提供的函数
}
</script>
<script setup>中的动态组件使用
App.vue父组件
<template>
<div>
<button @click="switchComponent">App.Vue点我切换组件</button>
<!-- 动态组件 -->
<component :is="Child" :mytitle="title" state="error" />
<component :is="isShow?Child:BChild" :mytitle="title" state="error" />
<!-- keep-alive组件内不要使用注释,会被解析为子节点 -->
<!-- include:包含:包含中的组件存在缓存,组件中录入的数据在组件切换走后再切换回来数据依然保留 -->
<!-- exclude:排除:排除中的组件不存在缓存,除此之外都存在缓存 -->
<KeepAlive include="Child,BChild">
<component :is="componentName" :mytitle="title" state="error"></component>
</KeepAlive>
</div>
</template>
<script setup>
import { ref, provide, toRefs, reactive, computed, markRaw, shallowRef } from 'vue';
//导入模板即注册:注册的名字就是你导入用的名称Child
import Child from "./components/Child.vue" //导入Child组件:
import BChild from "./components/BChild.vue" //导入Child组件:
const title = ref("我是标题")
const isShow = ref(true);
// 使用<keep-alive>包裹动态组件的时候,动态组件的名称应该用shallowRef或者markRaw来进行包裹。
// 说明:
// 在 Vue 3 中,如果用 ref 或 reactive 将一个组件包装成响应式对象,可能会引发不必要的性能开销。因为这会使 Vue 尝试去追踪组件的变化,而实际上组件实例并不需要被追踪。组件本身不应该是响应式的,只有它的 props 和 state 才应该是响应式的。
// 所以,当需要引用一个组件时,应该使用 shallowRef或者 markRaw,这样可以避免将整个组件变成响应式的,只会跟踪引用的变化
const componentName = shallowRef(Child);
//切换组件
const switchComponent = () => {
isShow.value = !isShow.value;
switch (componentName.value) {
case BChild:
componentName.value = Child;
break
case Child:
componentName.value = BChild;
break
}
}
//组件名称不要用ref对象,直接用字符串即可:如下代码
// let componentName = "Child";
// const switchComponent = () => {
// isShow.value = !isShow.value;
// switch (componentName) {
// case "Child":
// componentName = "BChild";
// break
// case "BChild":
// componentName = "Child";
// break
// }
// }
</script>
Child.vue子组件
<template>
<div>
{{ props.mytitle }} <!--或者直接用mytitle,不用props.mytitle也行-->
</div>
<div>
{{ state }} <!--或者直接用mytitle,不用props.mytitle也行-->
</div>
<div><input type="text"></div>
</template>
<script setup>
import { inject, ref, defineProps, defineEmits } from 'vue';
//父组件向子组件传值:具体可参阅:vue3.0中父组件与子组件的通信传值props与$emit :VOA模式文章
//这里接收父组件传递过来的state和mytitle值
const props = defineProps({ state: { type: String }, mytitle: { type: String } });
</script>
BChild.vue子组件
<template>
<div>
我是BChild.vue<input type="text">
</div>
</template>