以.vue
结尾的文件中script
标签包含setup
属性称之为单文件组件
vue<template></template> <script setup></script> <style></style>
常规setup
函数中需要将定义的属性或方法返回,但是单文件组件则不需要
vue<template> <h1>单文件组件</h1> <h4 @click="oldmen">{{name}} is {{age}}</h4> </template> <script setup> import { ref } from "@vue/reactivity"; const name = ref('xm') const age = ref(24) const oldmen = ()=> { age.value++ } </script> <style></style>
在单文件组件内,只需要import引入子组件后就可以直接在<template></template>
中使用
vue<template> <h1>单文件组件</h1> <h4 @click="oldmen">{{name}} is {{age}}</h4> <ComponentsTestsVue></ComponentsTestsVue> </template> <script setup> import { ref } from "@vue/reactivity"; import ComponentsTestsVue from "./ComponentsTests.vue"; const name = ref('xm') const age = ref(24) const oldmen = ()=> { age.value++ } </script> <style></style>
使用
:is
动态绑定被注册的组件名或导入的组件对象 一般建议使用shallowRef
或者markRaw
对组件的Proxy代理
取消掉,节省性能
使用:is
在多个组件间切换,被切换掉的组件会被卸载,可以通过<keepalive></keepalive>
组件使得被卸载的组件任然保持活跃状态
vue<template> <h1>单文件组件</h1> <button v-for="item in comlist" :key="item.name" @click="changeCom(item.id)">{{item.name}}</button> <component :is="currentTab.name"></component> </template> <script setup> import { reactive, markRaw } from "vue"; import ComponentsTestsVue from "./ComponentsTests.vue"; import ComponentsTests2Vue from "./ComponentsTests2.vue"; import ComponentsTests3Vue from "./ComponentsTests3.vue"; const comlist = reactive([ {'id':1,'name':'test1组件'}, {'id':2,'name':'test2组件'}, {'id':3,'name':'test3组件'}, ]) let currentTab = reactive({'name': markRaw(ComponentsTestsVue)}) const changeCom = (v) =>{ switch (v) { case 1 : currentTab.name = markRaw(ComponentsTestsVue) break; case 2 : currentTab.name = markRaw(ComponentsTests2Vue) break; case 3 : currentTab.name = markRaw(ComponentsTests3Vue) break; default : currentTab.name = markRaw(ComponentsTestsVue) break; } } </script> <style></style>
defineProps
规定要接受的参数,可以搭配withDefaults
设置默认值v-bind
传值props
都应该符合单向数据流原则,即不允许子组件修改父组件传递过来的值props
传递的值为数组或对象类型,子组件内无法更改,但是可以更改数组或对象内部的值(因为数组和对象都是引用数据类型)所以应当避免此类情况vue<script lang='ts' setup> const LoadPageProps = { title: '页面正在加载', subTitle: '请稍等~' } </script> <template> <LoadPage :title="LoadPageProps.title" :subTitle="LoadPageProps.subTitle"/> <!-- 等价与 --> <LoadPage v-bind="LoadPageProps"/> </template> <style scope></style>
对于props
有两种声明方式
传入defineProps()
的参数会作为运行时的props选项使用
ts<script setup lang="ts">
const props = defineProps({
foo: { type: String, required: true },
bar: Number
})
props.foo // string
props.bar // number | undefined
</script>
指的是在使用defineProps()
是使用泛型函数形式
泛型参数必须为:
注意点 接口或对象字面类型可以包含从其他文件导入的类型引用,但是泛型参数本身不能是一个导入的类型 对于
.vue
文件是单独编译的,并不会抓取分析源类型,所以暂时不支持
<script setup lang="ts"> const props = defineProps<{foo:string,Bar?:Number}>() props.foo // string props.Bar // number | undefined // 配合接口使用 interface props_interface { foo: string, Bar?: number } const props = defineProps<props_interface>() props.foo // string props.Bar // number | undefined </script>
通过编辑器宏withDefaults
实现
ts<script setup lang="ts">
// 配合接口使用
interface props_interface {
foo: string,
Bars?: number
}
const props = withDefaults(defineProps<props_interface>(),{
foo: 'hello world',
Bars: 13
})
props.foo // string
props.Bars // number
</script>
需要更改
vite.config.ts
配置
// 插件配置 plugins: [ vue({ reactivityTransform: true }) ],
ts<script setup lang="ts">
// 对 defineProps() 的响应性解构
// 默认值会被编译为等价的运行时选项
const { name, count = 100 } = defineProps<Props>()
</script>
用于组件的emits
标注类型
宏defineEmits
必须在setup
的顶级作用域下,不能在子函数中
ts<script setup lang="ts">
// 对emits标注类型
// 运行时声明
const emit = defineEmits(['change', 'update'])
function changeChildren() {
emit('change',)
}
// 基于类型
const emit = defineEmits<{
(e: 'change', id: number): void
(e: 'update', value: string): void
}>()
function changeChildren() {
emit('change') // const emit: (e: "change", id: number) => void (+1 overload)
}
</script>
即在main.ts
中注册
tsimport MyComponent from './App.vue'
app.component('MyComponent', MyComponent)
// app.component(MyComponent)
在单文件组件中引入的子组件会自动注册,无需手动注册
vue<script setup> import NoData from './NoData.vue' </script> <template> <NoData /> </template>
本文作者:RKLS
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!