vue3入门- script setup详解下

defineProps() 和 defineEmits()

为了在声明 propsemits 选项时获得完整的类型推导支持,我们可以使用 definePropsdefineEmits API,它们将自动地在 <script setup> 中可用:

html 复制代码
<template>
  <div>
    <h1>{{ props.foo }}</h1>
    <button @click="handleChange">Change</button>
    <button @click="handleDelete">Delete</button>
  </div>
</template>

<script setup>
const props = defineProps({
  foo: String
})

const emit = defineEmits(['change', 'delete'])

function handleChange() {
  emit('change', props.foo)
}

function handleDelete() {
  emit('delete')
}
</script>
  • definePropsdefineEmits 都是只能在 <script setup> 中使用的编译器宏。他们不需要导入,且会随着 <script setup> 的处理过程一同被编译掉。
  • defineProps 接收与 props 选项相同的值,defineEmits 接收与 emits 选项相同的值。
  • definePropsdefineEmits 在选项传入后,会提供恰当的类型推导。

针对TS类型的 props/emit 声明

propsemit 也可以通过给 definePropsdefineEmits 传递纯TS类型参数的方式来声明:

typescript 复制代码
const props = defineProps<{
  foo: string
  bar?: number
}>()

const emit = defineEmits<{
  (e: 'change', id: number): void
  (e: 'update', value: string, status: boolean): void
}>()

// 3.3+:另一种更简洁的语法
const emit = defineEmits<{
  change: [id: number] // 具名元组语法
  update: [value: string, status: boolean]
}>()
  • definePropsdefineEmits 要么使用运行时声明 ,要么使用类型声明。同时使用两种声明方式会导致编译报错。

    • 示例 1: 使用运行时声明

      js 复制代码
      const props = defineProps({
        foo: String,
        bar: Number
      })
      
      const emit = defineEmits(['change', 'delete'])
    • 示例 2: 使用类型声明

      js 复制代码
      const props = defineProps<{
        foo: string
        bar?: number
      }>()
      
      const emit = defineEmits<{
        (e: 'change', id: number): void
        (e: 'delete'): void
      }>()
    • 错误示例: 同时使用运行时声明和类型声明

      js 复制代码
      // ❌ 会导致编译报错
      const props = defineProps<{
        foo: string
      }>({
        bar: Number
      })
      typescript 复制代码
      // ❌ 会导致编译报错
      const emit = defineEmits<{
        (e: 'change', id: number): void
      }>(['delete'])
  • 使用类型声明的时候,静态分析会自动生成等效的运行时声明,从而在避免双重声明的前提下确保正确的运行时行为。

    • 在开发模式下,编译器会试着从类型来推导对应的运行时验证。例如这里从 foo: string 类型中推断出 foo: String。如果类型是对导入类型的引用,这里的推导结果会是 foo: null (与 any 类型相等),因为编译器没有外部文件的信息。
    • 在生产模式下,编译器会生成数组格式的声明来减少打包体积 (这里的 props 会被编译成 ['foo', 'bar'])。

在 Vue 3.2 及以下版本中,defineProps() 的泛型类型参数只能使用类型字面量或本地接口的引用:

typescript 复制代码
interface Props {
  foo: string
  bar?: number
}

const props = defineProps<Props>()

在 Vue 3.3 及以上版本中,可以在类型参数的位置引用导入的类型或有限的复杂类型:

typescript 复制代码
import { SomeType } from './types'

const props = defineProps<SomeType>()

然而,仍然无法使用需要实际类型分析的复杂类型,例如条件类型:

typescript 复制代码
// ❌ 不支持
type ConditionalProps<T> = T extends string ? { foo: T } : { bar: T }

const props = defineProps<ConditionalProps<number>>()

但可以在单个 prop 的类型上使用条件类型:

typescript 复制代码
// ✅ 支持
const props = defineProps<{
  foo: string | number
  bar: string extends 'test' ? number : boolean
}>()

响应式 Props 解构

在 Vue 3.5 及以上版本中,从 defineProps 返回值解构出的变量是响应式的。当在同一个 <script setup> 块中的代码访问从 defineProps 解构出的变量时,Vue 的编译器会自动在前面添加 props.

js 复制代码
const { foo } = defineProps(['foo'])

watchEffect(() => {
  // 在 3.5 之前仅运行一次
  // 在 3.5+ 版本中会在 "foo" prop 改变时重新运行
  console.log(foo)
})

以上编译成以下等效内容:

js 复制代码
const props = defineProps(['foo'])

watchEffect(() => {
  // `foo` 由编译器转换为 `props.foo`
  console.log(props.foo)
})

此外,你可以使用 JavaScript 原生的默认值语法声明 props 的默认值。这在使用基于类型的 props 声明时特别有用。

js 复制代码
interface Props {
  msg?: string
  labels?: string[]
}

const { msg = 'hello', labels = ['one', 'two'] } = defineProps<Props>()

使用类型声明时的默认 props 值

在 3.5 及以上版本中,当使用响应式 Props 解构时,可以自然地声明默认值。但在 3.4 及以下版本中,默认情况下并未启用响应式 Props 解构。为了用基于类型声明的方式声明 props 的默认值,需要使用 withDefaults 编译器宏:

js 复制代码
interface Props {
  msg?: string
  labels?: string[]
}

const props = withDefaults(defineProps<Props>(), {
  msg: 'hello',
  labels: () => ['one', 'two']
})

上面代码会被编译为等价的运行时 propsdefault 选项。此外,withDefaults 辅助函数提供了对默认值的类型检查,并确保返回的 props 的类型删除了已声明默认值的属性的可选标志。

在使用 withDefaults 时,如果默认值是可变引用类型(如数组或对象),应将其封装在函数中,以避免意外修改和外部副作用。以下是一个示例:

js 复制代码
interface Props {
  items?: string[]
  config?: {
    theme: string
    layout: string
  }
}

const props = withDefaults(defineProps<Props>(), {
  items: () => ['item1', 'item2', 'item3'], // 使用函数返回数组
  config: () => ({ theme: 'dark', layout: 'grid' }) // 使用函数返回对象
})

在上述代码中,itemsconfig 的默认值是通过函数返回的。这确保了每个组件实例都能获得独立的默认值副本,而不会因为共享同一个引用而导致意外的修改。

  • 错误示例

如果直接使用对象或数组作为默认值,可能会导致所有组件实例共享同一个引用,从而引发意外行为:

js 复制代码
const props = withDefaults(defineProps<Props>(), {
  items: ['item1', 'item2', 'item3'], // ❌ 直接使用数组
  config: { theme: 'dark', layout: 'grid' } // ❌ 直接使用对象
})

在这种情况下,修改一个组件实例的 itemsconfig 会影响其他实例的值,这是不符合预期的。

  • 使用默认值解构

当使用默认值解构时,不需要封装在函数中,因为解构操作会为每个实例创建独立的值:

js 复制代码
const { items = ['item1', 'item2', 'item3'], config = { theme: 'dark', layout: 'grid' } } = defineProps<Props>()

这种方式同样可以确保每个组件实例获得自己的默认值副本。

defineModel()

这个宏可以用来声明一个双向绑定 prop,通过父组件的 v-model 来使用。组件 v-model 指南中也讨论了示例用法。

在底层,这个宏声明了一个 model prop 和一个相应的值更新事件 。如果第一个参数是一个字符串字面量,它将被用作 prop 名称;否则,prop 名称将默认为 modelValue。在这两种情况下,你都可以再传递一个额外的对象,它可以包含 prop 的选项和 model ref 的值转换选项。

js 复制代码
// 声明 "modelValue" prop,由父组件通过 v-model 使用
const model = defineModel()
// 或者:声明带选项的 "modelValue" prop
const model = defineModel({ type: String })

// 在被修改时,触发 "update:modelValue" 事件
model.value = "hello"

// 声明 "count" prop,由父组件通过 v-model:count 使用
const count = defineModel("count")
// 或者:声明带选项的 "count" prop
const count = defineModel("count", { type: Number, default: 0 })

function inc() {
  // 在被修改时,触发 "update:count" 事件
  count.value++
}

在实际项目中,子组件可以用 defineModel 实现双向绑定:

// 子组件 CInput.vue

html 复制代码
<template>
  <input v-model="modelValue" placeholder="输入内容" />
</template>

<script setup lang="ts">
const modelValue = defineModel<string>({ default: '' })
</script>

// 父组件

html 复制代码
<template>
  <c-input v-model="inputval" />
  <div>{{ inputval }}</div>
</template>
<script setup>
import { ref } from 'vue';
import CInput from './components/CInput.vue';
const inputval = ref('')
</script>

WARNING

如果为 defineModel prop 设置了一个 default 值且父组件没有为该 prop 提供任何值,会导致父组件与子组件之间不同步。在下面的示例中,父组件的 myRef 是 undefined,而子组件的 model 是 1:

js 复制代码
// 子组件:
const model = defineModel({ default: 1 })

// 父组件
const myRef = ref()
js 复制代码
<Child v-model="myRef"></Child>

解决方法:在父组件初始化时为 v-model 绑定的变量设置默认值,使其与子组件的默认值保持一致。

js 复制代码
// 父组件
const myRef = ref(1) // 初始化为与子组件默认值一致

修饰符和转换器

为了获取 v-model 指令使用的修饰符,我们可以像这样解构 defineModel() 的返回值:

// 父组件

html 复制代码
<c-input v-model.trim="inputval" />

// 子组件

js 复制代码
const [modelValue, modelModifiers] = defineModel()

// 对应 v-model.trim
if (modelModifiers.trim) {
  // ...
}

当存在修饰符时,我们可能需要在读取或将其同步回父组件时对其值进行转换。我们可以通过使用 getset 转换器选项来实现这一点:

// 子组件

js 复制代码
const [modelValue, modelModifiers] = defineModel({
  // get() 省略了,因为这里不需要它
  set(value) {
    // 如果使用了 .trim 修饰符,则返回裁剪过后的值
    if (modelModifiers.trim) {
      return value.trim()
    }
    // 否则,原样返回
    return value
  }
})

在 TypeScript 中使用

definePropsdefineEmits 一样,defineModel 也可以接收类型参数来指定 model 值和修饰符的类型:

  1. defineModel<string>()
typescript 复制代码
const modelValue = defineModel<string>()
// ^? Ref<string | undefined>
  • defineModel<string>() 定义了一个 v-model,其类型为 string
  • 返回值是一个 Ref<string | undefined>,表示 modelValue 是一个响应式引用,可能是 string 类型,也可能是 undefined
  • 默认情况下,v-model 的值是可选的,因此会包含 undefined
  1. defineModel<string>({ required: true })
typescript 复制代码
const modelValue = defineModel<string>({ required: true })
// ^? Ref<string>
  • 这里通过传递选项 { required: true },将 v-model 的值设置为必填。
  • 这意味着 modelValue 不再可能是 undefined,它的类型变为 Ref<string>
  • 这种用法适合需要确保 v-model 始终有值的场景。
  1. defineModel<string, "trim" | "uppercase">()
typescript 复制代码
const [modelValue, modifiers] = defineModel<string, "trim" | "uppercase">()
// ^? Record<'trim' | 'uppercase', true | undefined>
  • 这里定义了一个带有修饰符的 v-model,例如 v-model.trimv-model.uppercase
  • 返回值是一个数组:
    1. modelValueRef<string | undefined>,表示响应式的模型值。
    2. modifiers 是一个对象,类型为 Record<'trim' | 'uppercase', true | undefined>,表示修饰符的状态。
      • 如果某个修饰符被使用(如 v-model.trim),对应的值为 true
      • 如果未使用,则为 undefined

Record 语法

Record 是 TypeScript 中的一个实用类型(Utility Type),用于构造一个对象类型,其键和值的类型都可以被明确指定。

Record 通常用于:

  1. 定义固定键值对的对象类型。
  2. 动态生成对象类型,避免手动重复定义。
typescript 复制代码
Record<Keys, Type>
  • Keys: 对象的键的类型,通常是字符串字面量类型或联合类型。
  • Type: 对象的值的类型。
typescript 复制代码
Record<'trim' | 'uppercase', true | undefined>

这段代码的含义是:创建一个对象类型,该对象的键是 trimuppercase,值是 trueundefined

等价于:

typescript 复制代码
{
  trim: true | undefined;
  uppercase: true | undefined;
}
typescript 复制代码
const options: Record<'trim' | 'uppercase', true | undefined> = {
  trim: true,
  uppercase: undefined,
};

在这个例子中,options 是一个对象,必须包含 trimuppercase,并且它们的值只能是 trueundefined

defineExpose()

使用 <script setup> 的组件是默认关闭 的------即通过模板引用或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。

可以通过 defineExpose 编译器宏来显式指定在 <script setup> 组件中要暴露出去的属性:

js 复制代码
<script setup>
import { ref } from 'vue'

const a = 1
const b = ref(2)

defineExpose({
  a,
  b
})
</script>

当父组件通过模板引用的方式获取到当前组件的实例,获取到的实例会像这样 { a: number, b: number } (ref 会和在普通实例中一样被自动解包)

以下是一个关于如何使用 defineExpose 的实例讲解,展示如何在 <script setup> 中显式暴露属性,以便外部组件可以访问这些属性。

示例代码

  • 父组件 (ParentComponent.vue)

    html 复制代码
    <template>
      <ChildComponent ref="childRef" />
      <button @click="callChildMethod">调用子组件方法</button>
    </template>
    
    <script setup>
    import ChildComponent from './ChildComponent.vue';
    
    const childRef = useTemplateRef('childRef');
    
    function callChildMethod() {
      if (childRef.value) {
        childRef.value.exposedMethod(); // 调用子组件暴露的方法
      }
    }
    </script>
  • 子组件 (ChildComponent.vue)

    vue 复制代码
    <template>
      <div>子组件内容</div>
    </template>
    
    <script setup>
    import { defineExpose } from 'vue';
    
    function exposedMethod() {
      console.log('子组件方法被调用');
    }
    
    // 使用 defineExpose 显式暴露方法
    defineExpose({
      exposedMethod,
    });
    </script>

defineSlots()

defineSlots 是 Vue 3 <script setup> 的编译宏,用于类型化插槽,让 TypeScript 能够对插槽名称和插槽 props 进行类型检查和智能提示。它只在编译阶段生效,不会影响运行时。

  • 类型安全:确保插槽名称和 props 类型正确,减少运行时错误。
  • IDE 提示:在编辑器中获得插槽相关的自动补全和类型提示。

基本语法如下

typescript 复制代码
<script setup lang="ts">
const slots = defineSlots<{
  default(props: { msg: string }): any
}>()
// slots.default({ msg: 'hello' }) // 类型检查
</script>
  • 泛型参数是一个对象,键为插槽名称,值为函数类型。
  • 函数的参数是插槽 props 的类型,返回值类型通常用 any

多插槽示例

假设有一个组件支持多个插槽:

typescript 复制代码
<script setup lang="ts">
const slots = defineSlots<{
  header(props: { title: string }): any
  default(props: { msg: string }): any
  footer(props: { count: number }): any
}>()
</script>
  • header 插槽要求 title 为字符串。
  • default 插槽要求 msg 为字符串。
  • footer 插槽要求 count 为数字。

父组件如何传递插槽

html 复制代码
<template>
  <Child class="child-style" expand>
    <template #header="{ title }">
      <div>header部分描述:{{ title }}</div>
    </template>
    <template #default="{ count }">
      <p>default部分描述:{{ count }}</p>
    </template>
    <template #footer="{ msg }">
      <div>footer部分描述:{{ msg }}</div>
    </template>
  </Child>
</template>
<script setup>
import Child from './components/Child.vue';
</script>
  • 父组件传递的插槽参数会被类型检查,确保类型正确。

子组件如何传值给父组件

html 复制代码
<template>
    <slot name="header" headerDesc="顶部部分描述"></slot>
    <slot defaultDesc="插槽描述">默认插槽描述</slot>
    <slot name="footer" footerDesc="底部部分描述"></slot>
</template>
<script setup lang="ts">
const slots = defineSlots<{
  header(props: { headerDesc: string }):any
  footer( props: { footerDesc: string } ): any
  default( props: { defaultDesc: string } ): any
}>()
</script>

类型推断和 IDE 提示

<script setup> 中,使用 slots.header({ title: 'Hello' }) 时,IDE 会自动提示 title 必须是字符串,传递错误类型会报错。

可选插槽和返回值类型

你可以将插槽定义为可选:

typescript 复制代码
<script setup lang="ts">
const slots = defineSlots<{
  header?(props: { title: string }): any
  default(props: { msg: string }): any
}>()
</script>
  • header? 表示 header 插槽是可选的。

返回值类型可以更具体:

typescript 复制代码
<script setup lang="ts">
const slots = defineSlots<{
  default(props: { msg: string }): VNode[]
}>()
</script>

useSlots() 和 useAttrs()

<script setup> 中使用 slotsattrs 的情况相对较少,因为可以直接通过模板中的 $slots$attrs 访问它们。然而,在某些特定场景下,仍然可以使用 useSlotsuseAttrs 辅助函数来获取插槽和属性。

以下是一个实际使用 useSlotsuseAttrs 的示例:

  • 子组件 (ChildComponent.vue)

    html 复制代码
    <template>
      <div v-bind="attrs">
        <slot name="header" />
        <p>子组件内容</p>
        <slot />
      </div>
    </template>
    
    <script setup>
    import { useSlots, useAttrs } from 'vue'
    
    const slots = useSlots()
    const attrs = useAttrs()
    
    // 检查是否提供了名为 "header" 的插槽
    if (!slots.header) {
      console.warn('未提供 header 插槽')
    }
    </script>
  • 父组件 (ParentComponent.vue)

    html 复制代码
    <template>
      <ChildComponent class="custom-class">
        <template #header>
          <h1>这是标题插槽内容</h1>
        </template>
        <p>这是默认插槽内容</p>
      </ChildComponent>
    </template>
    
    <script setup>
    import ChildComponent from './ChildComponent.vue'
    </script>

说明:

  1. useSlots:

    • 在子组件中使用 useSlots 检查是否提供了特定的插槽(如 header)。
    • 如果未提供插槽,输出警告信息。
  2. useAttrs:

    • 使用 useAttrs 获取父组件传递的属性(如 class="custom-class")。
    • 使用 v-bind="attrs" 将这些属性绑定到子组件的根元素。
  3. 父组件:

    • 通过 #header 提供了一个具名插槽。
    • 默认插槽内容直接放置在 <ChildComponent> 标签内。

运行结果:

  • 子组件会渲染标题插槽内容、默认插槽内容,并应用父组件传递的 class 属性。
  • 如果父组件未提供 header 插槽,子组件会在控制台输出警告信息。

泛型

可以使用 <script> 标签上的 generic 属性声明泛型类型参数:

js 复制代码
<script setup lang="ts" generic="T">
defineProps<{
  items: T[]
  selected: T
}>()
</script>

generic 的值与 TypeScript 中位于 <...> 之间的参数列表完全相同。例如,你可以使用多个参数,extends 约束,默认类型和引用导入的类型:

js 复制代码
<script
  setup
  lang="ts"
  generic="T extends string | number, U extends Item"
>
import type { Item } from './types'
defineProps<{
  id: T
  list: U[]
}>()
</script>

为了在 ref 中使用泛型组件的引用,你需要使用 vue-component-type-helpers 库,因为 InstanceType 在这种场景下不起作用。

typescript 复制代码
<script
  setup
  lang="ts"
>
import componentWithoutGenerics from '../component-without-generics.vue'; // 一个没有泛型的普通组件。
import genericComponent from '../generic-component.vue'; // 一个带有泛型的组件。

import type { ComponentExposed } from 'vue-component-type-helpers';

// 适用于没有泛型的组件
ref<InstanceType<typeof componentWithoutGenerics>>();

// 适用于有泛型的组件
ref<ComponentExposed<typeof genericComponent>>();

这段代码展示了如何在 Vue 3 的 <script setup> 中使用 TypeScript 来处理组件的引用,尤其是泛型组件的引用。以下是逐步的详细解释:

  1. 导入类型工具
typescript 复制代码
import type { ComponentExposed } from 'vue-component-type-helpers';
  • vue-component-type-helpers 库中导入了 ComponentExposed 类型工具。
  • ComponentExposed 是一个辅助类型,用于提取组件的暴露类型(即组件实例的类型),特别适用于泛型组件。
  1. 处理没有泛型的组件引用
typescript 复制代码
ref<InstanceType<typeof componentWithoutGenerics>>();
  • ref 是 Vue 的响应式 API,用于创建一个响应式引用。
  • InstanceType<typeof componentWithoutGenerics>
    • InstanceType 是 TypeScript 的内置工具类型,用于获取构造函数的实例类型。
    • typeof componentWithoutGenerics 获取组件的类型。
    • 结合起来,InstanceType<typeof componentWithoutGenerics> 表示 componentWithoutGenerics 的实例类型。
  • 适用于没有泛型的普通组件。
  1. 处理带有泛型的组件引用
typescript 复制代码
ref<ComponentExposed<typeof genericComponent>>();
  • ref<ComponentExposed<typeof genericComponent>>
    • ComponentExposed 是从 vue-component-type-helpers 导入的类型工具。
    • typeof genericComponent 获取组件的类型。
    • ComponentExposed<typeof genericComponent> 提取了 genericComponent 的暴露类型。
  • 适用于带有泛型的组件,因为 InstanceType 无法正确处理泛型组件的类型。

总结:

  1. 普通组件 :可以直接使用 InstanceType 获取实例类型。
  2. 泛型组件 :需要借助 vue-component-type-helpers 提供的 ComponentExposed 类型工具,因为 InstanceType 无法正确处理泛型。

这种方式确保了在使用组件引用时,能够获得正确的类型推断和静态检查,提升了代码的安全性和可维护性。

为了更好地理解没有泛型的普通组件和带有泛型的组件的使用场景,以下是具体的示例讲解:

  • 没有泛型的普通组件

假设我们有一个普通的 Vue 组件 ButtonComponent.vue,它不使用任何泛型:

html 复制代码
<!-- filepath: ButtonComponent.vue -->
<template>
  <button>{{ label }}</button>
</template>

<script setup lang="ts">
defineProps<{
  label: string
}>();
</script>

<script setup> 中,我们可以通过 refInstanceType 来引用该组件的实例:

typescript 复制代码
<script setup lang="ts">
import ButtonComponent from './ButtonComponent.vue';

const buttonRef = ref<InstanceType<typeof ButtonComponent>>();
</script>

这里的 InstanceType<typeof ButtonComponent> 提供了 ButtonComponent 的实例类型,适用于没有泛型的普通组件。


  • 带有泛型的组件

假设我们有一个带有泛型的 Vue 组件 GenericList.vue,它接受一个泛型 T 来定义列表项的类型:

html 复制代码
<!-- filepath: GenericList.vue -->
<template>
  <ul>
    <li v-for="(item, index) in items" :key="index">{{ item }}</li>
  </ul>
</template>

<script setup lang="ts" generic="T">
defineProps<{
  items: T[]
}>();
</script>

<script setup> 中,我们需要使用 vue-component-type-helpers 提供的 ComponentExposed 来正确引用该组件的实例:

typescript 复制代码
<script setup lang="ts">
import GenericList from './GenericList.vue';
import type { ComponentExposed } from 'vue-component-type-helpers';

const listRef = ref<ComponentExposed<typeof GenericList>>();
</script>

这里的 ComponentExposed<typeof GenericList> 提取了 GenericList 的暴露类型,适用于带有泛型的组件,因为 InstanceType 无法正确处理泛型。

通过这些示例,我们可以清楚地看到如何在 Vue 3 的 <script setup> 中使用 TypeScript 来处理组件的引用,尤其是泛型组件的引用。

在 Vue 3.4 及以上版本,推荐使用 useTemplateRef 进行子组件引用,而不是直接用 refuseTemplateRef 能更好地处理类型推断,尤其是泛型组件。

typescript 复制代码
<script setup lang="ts">
import componentWithoutGenerics from '../component-without-generics.vue'; // 没有泛型的组件
import genericComponent from '../generic-component.vue'; // 带泛型的组件
import type { ComponentExposed } from 'vue-component-type-helpers';

// 没有泛型的组件引用
const normalRef = useTemplateRef<InstanceType<typeof componentWithoutGenerics>>('normalRef');

// 带泛型的组件引用
const genericRef = useTemplateRef<ComponentExposed<typeof genericComponent>>('genericRef');
</script>

这样可以确保在 <script setup> 中获得正确的类型推断和 IDE 智能提示,无论是普通组件还是泛型组件。

限制

以下是一个关于 <script setup> 的实例讲解,帮助你理解其用法和限制。

假设我们有一个 Vue 3 组件,使用 <script setup> 来定义逻辑和模板。

html 复制代码
<template>
  <div>
    <h1>{{ message }}</h1>
    <button @click="increment">点击次数: {{ count }}</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

// 定义响应式数据
const message = 'Hello, Vue 3!';
const count = ref(0);

// 定义方法
const increment = () => {
  count.value++;
};
</script>

限制说明:

  1. 不能与 src 属性一起使用

    如果你尝试将 <script setup> 的逻辑提取到外部文件并通过 src 引入,会导致上下文丢失。例如:

    vue 复制代码
    <script setup src="./logic.ts"></script>

    这种写法是不支持的,因为 <script setup> 的代码依赖于单文件组件的上下文。

  2. 不支持 DOM 内根组件模板 <script setup> 不支持直接在 DOM 内使用根组件模板。例如:

    html 复制代码
    <div id="app">
      <MyComponent />
    </div>

    如果 MyComponent 使用了 <script setup>,它必须通过 Vue 的 createApp 挂载,而不能直接在 DOM 内使用。

相关推荐
Json_Lee3 小时前
每次切项目都要改 Node 版本?macOS 自动化读取.nvmrc,解放双手!
前端
定栓3 小时前
vue3入门- script setup详解上
前端·javascript·vue.js
武汉刘德华3 小时前
Flutter配置环境,运行三端- iOS、android、harmony全流程操作实践(最新)
前端
酥饼_i3 小时前
你的自动化脚本又双叒叕崩了?
前端·人工智能·ai编程
Lsx-codeShare3 小时前
前端数据可视化:基于Vue3封装 ECharts 的最佳实践
前端·javascript·echarts·vue3·数据可视化
主宰者3 小时前
WPF外部打开html文件
前端·html·wpf
jason_yang4 小时前
vue3中定义组件的4种姿势
前端·vue.js
袋鱼不重4 小时前
Gitee 与 GitHub 仓库同步:从手动操作到自动化部署
前端·github
哒哒哒就是我4 小时前
React中,函数组件里执行setState后到UI上看到最新内容的呈现,react内部会经历哪些过程?
前端·react.js·前端框架