Vue3如何融合TS

Vue3 对 TS 的适配是全方位的,从组件定义、Props 声明到响应式数据、生命周期等都有专门的 TS 语法,下面我会按开发中最常用的场景逐一讲解:

一、基础:组件的 TS 写法(setup 语法糖)

Vue3 推荐使用 <script setup lang="ts"> 作为 TS 组件的核心写法,这是对 TS 支持最友好的方式,也是官方推荐的最佳实践。

javascript 复制代码
<template>
  <div>{{ msg }}</div>
  <button @click="handleClick">点击</button>
</template>

<script setup lang="ts">
// 1. 响应式数据的 TS 类型标注
import { ref, reactive } from 'vue'

// ref 基础类型(TS 可自动推导,也可显式标注)
const msg = ref<string>('Hello Vue3 + TS') // 显式标注为字符串类型
const count = ref(0) // 自动推导为 number 类型

// reactive 复杂类型(推荐用 interface/type 定义)
interface User {
  name: string
  age: number
  isAdmin?: boolean // 可选属性
}
const user = reactive<User>({
  name: '张三',
  age: 20
})

// 2. 函数的 TS 类型标注
const handleClick = (): void => { // 无返回值标注为 void
  count.value++
  console.log(count.value)
}
</script>

二、核心:Props 的 TS 强类型声明

Vue3 提供了 defineProps 专门适配 TS 的写法,有两种方式(推荐第二种):

方式 1:运行时声明(兼容 Vue2 风格,类型推导)
复制代码
<script setup lang="ts">
// TS 会自动从 props 定义中推导类型
const props = defineProps({
  title: {
    type: String,
    required: true
  },
  size: {
    type: Number,
    default: 16
  },
  isShow: Boolean
})

// 使用 props 时会有完整的类型提示
console.log(props.title) // 提示为 string 类型
</script>
方式 2:纯 TS 类型声明(推荐,更贴合 TS 习惯)
复制代码
<script setup lang="ts">
// 用 TS 接口定义 Props 类型,配合 withDefaults 设置默认值
interface Props {
  title: string // 必传
  size?: number // 可选
  isShow?: boolean // 可选
}

// 方式 1:仅声明类型
// const props = defineProps<Props>()

// 方式 2:声明类型 + 设置默认值(推荐)
const props = withDefaults(defineProps<Props>(), {
  size: 16,
  isShow: true
})
</script>

三、Emits 的 TS 类型声明

defineEmits 同样支持 TS 类型标注,明确事件的名称和参数类型:

复制代码
<script setup lang="ts">
// 方式 1:纯 TS 类型声明(推荐)
const emit = defineEmits<{
  // 事件名: (参数1类型, 参数2类型) => void
  change: (value: string) => void
  confirm: (id: number, name: string) => void
}>()

// 方式 2:运行时声明(类型推导)
// const emit = defineEmits(['change', 'confirm'])

// 触发事件时会校验参数类型
const handleConfirm = () => {
  emit('confirm', 1, '张三') // 正确
  // emit('confirm', '1', '张三') // TS 报错:id 应为 number 类型
}
</script>

四、Ref 获取 DOM 元素的 TS 标注

获取 DOM 元素时,需要给 ref 标注具体的元素类型:

复制代码
<template>
  <input ref="inputRef" type="text" />
</template>

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

// 标注为 HTMLInputElement 类型,初始值为 null
const inputRef = ref<HTMLInputElement | null>(null)

onMounted(() => {
  // 使用时需要判空(避免 null 报错)
  if (inputRef.value) {
    inputRef.value.focus() // TS 会提示 input 元素的所有方法/属性
  }
})
</script>

五、组合式函数的 TS 适配

自定义组合式函数时,通过 TS 标注返回值类型,让调用方获得完整提示:

TypeScript 复制代码
// hooks/useUser.ts
import { ref, computed } from 'vue'

interface UserInfo {
  id: number
  name: string
}

// 标注函数返回值类型
export function useUser(): {
  user: Ref<UserInfo | null>
  userName: ComputedRef<string>
  updateName: (name: string) => void
} {
  const user = ref<UserInfo | null>(null)
  
  const userName = computed(() => {
    return user.value?.name || '未知用户'
  })

  const updateName = (name: string): void => {
    if (user.value) {
      user.value.name = name
    }
  }

  return { user, userName, updateName }
}

在组件中使用:

复制代码
<script setup lang="ts">
import { useUser } from '@/hooks/useUser'

// 解构时会获得完整的类型提示
const { user, userName, updateName } = useUser()
</script>

六、全局属性 / 方法的 TS 扩展

如果给 Vue 实例添加了全局属性(如 app.config.globalProperties.$api),需要扩展 TS 类型以获得提示:

TypeScript 复制代码
// src/types/global.d.ts
import type { ComponentCustomProperties } from 'vue'
import type { Api } from '@/api'

// 扩展 ComponentCustomProperties 接口
declare module 'vue' {
  interface ComponentCustomProperties {
    $api: Api // 声明全局 $api 的类型
    $formatTime: (time: number) => string // 声明全局方法类型
  }
}

总结

Vue3 对 TS 的核心适配语法可以总结为以下 3 个关键点:

  1. 核心基座 :使用 <script setup lang="ts"> 作为 TS 组件的基础,这是所有适配语法的前提。
  2. 核心 APIdefineProps<Props>()withDefaults()defineEmits<Emits>() 是组件层面适配 TS 的核心,能精准约束 Props/Emits 类型。
  3. 类型标注 :响应式数据(ref/reactive)、DOM 引用、组合式函数等场景,通过 TS 的 interface/type + 显式标注,实现完整的类型校验和代码提示。

这些语法的核心目标是让 Vue 组件的每一个部分(数据、Props、事件、方法)都能被 TS 精准识别,既提升开发效率,也减少运行时错误。

vue3中父子同行

你想了解的是 Vue3 中父子组件在同一行显示的布局实现方法,这本质上是 CSS 布局的问题,Vue3 本身不直接控制布局,但可以结合 Vue 的组件写法来实现这个效果。

下面我会提供几种最常用、兼容性最好的实现方案,你可以根据实际需求选择:

一、核心实现思路(Flex 布局,推荐)

Flex 布局是实现 "父子同行" 最简洁、灵活的方式,也是现代前端布局的首选方案。

1. 基础示例(父组件控制布局)
复制代码
<!-- 父组件 Parent.vue -->
<template>
  <!-- 父容器设置 display: flex 即可让子元素同行 -->
  <div class="parent-container">
    <!-- 子组件1 -->
    <Child1 />
    <!-- 子组件2 -->
    <Child2 />
    <!-- 普通元素也适用 -->
    <div class="normal-element">普通元素</div>
  </div>
</template>

<script setup lang="ts">
import

父子通信

你想了解的是 Vue3 中父子组件之间的通信方式,这是 Vue 开发中最基础也最核心的知识点之一,Vue3 结合 TypeScript 提供了更规范、类型更安全的通信方案。

下面我会按「父传子」「子传父」「父子双向通信」「跨级 / 通用通信(补充)」四个维度,结合 TS 语法详细讲解,所有示例都基于 Vue3 推荐的 <script setup lang="ts"> 语法。


一、父传子(Props)

这是最基础的通信方式,父组件通过 Props 向子组件传递数据,Vue3 + TS 下能通过类型校验保证数据合法性。

1. 子组件(Child.vue):声明 Props 并接收
复制代码
<template>
  <div>
    <h3>子组件接收的父组件数据:</h3>
    <p>标题:{{ title }}</p>
    <p>数量:{{ count }}</p>
    <p>是否显示:{{ isShow }}</p>
  </div>
</template>

<script setup lang="ts">
// 1. 定义 Props 类型(TS 专属)
interface Props {
  title: string; // 必传字符串
  count?: number; // 可选数字
  isShow?: boolean; // 可选布尔值
}

// 2. 声明 Props + 设置默认值(withDefaults 是 Vue3 适配 TS 的语法)
const props = withDefaults(defineProps<Props>(), {
  count: 0, // count 默认值
  isShow: true // isShow 默认值
});
</script>
2. 父组件(Parent.vue):传递 Props
复制代码
<template>
  <div class="parent">
    <h2>父组件</h2>
    <!-- 向子组件传递 Props -->
    <Child 
      title="父组件传递的标题" 
      :count="parentCount" 
      :isShow="parentIsShow" 
    />
  </div>
</template>

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

// 父组件的响应式数据
const parentCount = ref<number>(10);
const parentIsShow = ref<boolean>(true);
</script>

二、子传父(Emits)

子组件通过触发自定义事件,将数据传递给父组件,Vue3 + TS 可通过类型约束事件名和参数类型。

1. 子组件(Child.vue):触发事件
复制代码
<template>
  <div>
    <h3>子组件</h3>
    <button @click="handleSendData">向父组件传值</button>
    <button @click="handleChangeShow">切换显示状态</button>
  </div>
</template>

<script setup lang="ts">
// 1. 定义事件类型(TS 专属)
const emit = defineEmits<{
  // 事件名: (参数类型) => void
  send-data: (msg: string, id: number) => void;
  change-show: (status: boolean) => void;
}>();

// 2. 触发事件,传递数据
const handleSendData = () => {
  emit('send-data', '子组件传递的消息', 1001);
};

const handleChangeShow = () => {
  emit('change-show', false);
};
</script>
2. 父组件(Parent.vue):监听事件
复制代码
<template>
  <div class="parent">
    <h2>父组件</h2>
    <!-- 监听子组件的自定义事件 -->
    <Child 
      @send-data="handleReceiveData" 
      @change-show="handleChangeShow" 
    />
    <p>子组件传递的消息:{{ childMsg }}</p>
  </div>
</template>

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

const childMsg = ref<string>('');

// 接收子组件传递的数据
const handleReceiveData = (msg: string, id: number) => {
  console.log('子组件传递的ID:', id);
  childMsg.value = msg;
};

// 处理子组件的状态变更
const handleChangeShow = (status: boolean) => {
  console.log('子组件切换显示状态:', status);
};
</script>

三、父子双向通信(v-model)

Vue3 支持自定义 v-model,实现父子组件数据双向绑定,相比 Vue2 更灵活。

1. 子组件(Child.vue):定义 v-model
复制代码
<template>
  <div>
    <h3>子组件</h3>
    <!-- 绑定内部值,触发 update 事件 -->
    <input 
      type="text" 
      :value="modelValue" 
      @input="emit('update:modelValue', $event.target.value)" 
    />
    <!-- 自定义名称的 v-model -->
    <input 
      type="number" 
      :value="count" 
      @input="emit('update:count', Number($event.target.value))" 
    />
  </div>
</template>

<script setup lang="ts">
// 定义 v-model 对应的 Props
interface Props {
  modelValue: string; // 默认 v-model 绑定的属性
  count: number; // 自定义 v-model:count 绑定的属性
}

const props = defineProps<Props>();
// 定义 update 事件(v-model 固定触发 update:xxx 事件)
const emit = defineEmits<{
  'update:modelValue': (value: string) => void;
  'update:count': (value: number) => void;
}>();
</script>
2. 父组件(Parent.vue):使用 v-model
复制代码
<template>
  <div class="parent">
    <h2>父组件</h2>
    <p>默认 v-model 值:{{ inputValue }}</p>
    <p>自定义 v-model:count 值:{{ countValue }}</p>
    <!-- 使用 v-model 双向绑定 -->
    <Child 
      v-model="inputValue" 
      v-model:count="countValue" 
    />
  </div>
</template>

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

// 父组件的响应式数据
const inputValue = ref<string>('初始值');
const countValue = ref<number>(0);
</script>

四、补充:父子通用通信方式(ref /provide/inject)

1. 父组件通过 ref 调用子组件方法 / 访问数据
  • 子组件(Child.vue):暴露方法 / 数据

    复制代码
    <script setup lang="ts">
    import { ref, defineExpose } from 'vue';
    
    const childCount = ref<number>(0);
    const addCount = () => {
      childCount.value++;
    };
    
    // 显式暴露(setup 语法糖下默认私有,需 defineExpose 暴露)
    defineExpose({
      childCount,
      addCount
    });
    </script>
  • 父组件(Parent.vue):通过 ref 访

    复制代码
    <template>
      <Child ref="childRef" />
      <button @click="handleCallChildMethod">调用子组件方法</button>
    </template>
    
    <script setup lang="ts">
    import { ref } from 'vue';
    import Child from './Child.vue';
    
    // 标注子组件 ref 类型(TS 专属)
    const childRef = ref<InstanceType<typeof Child> | null>(null);
    
    const handleCallChildMethod = () => {
      if (childRef.value) {
        childRef.value.addCount(); // 调用子组件方法
        console.log('子组件的 count:', childRef.value.childCount); // 访问子组件数据
      }
    };
    </script>
2. provide/inject(适合深层嵌套,父子也可用)
  • 父组件:提供数据

    复制代码
    <script setup lang="ts">
    import { ref, provide } from 'vue';
    import Child from './Child.vue';
    
    const theme = ref<string>('dark');
    const changeTheme = () => {
      theme.value = theme.value === 'dark' ? 'light' : 'dark';
    };
    
    // 提供数据(TS 可标注类型)
    provide('theme', theme);
    provide('changeTheme', changeTheme);
    </script>
  • 子组件:注入数据

    复制代码
    <script setup lang="ts">
    import { inject } from 'vue';
    
    // 注入数据并标注类型(TS 专属,设置默认值避免 null)
    const theme = inject<string>('theme', 'light');
    const changeTheme = inject<() => void>('changeTheme', () => {});
    </script>
相关推荐
切糕师学AI2 小时前
Vue 中如何修改地址栏参数并重新加载?
前端·javascript·vue.js
2501_920931708 小时前
React Native鸿蒙跨平台采用ScrollView的horizontal属性实现横向滚动实现特色游戏轮播和分类导航
javascript·react native·react.js·游戏·ecmascript·harmonyos
0思必得010 小时前
[Web自动化] Selenium处理动态网页
前端·爬虫·python·selenium·自动化
东东51610 小时前
智能社区管理系统的设计与实现ssm+vue
前端·javascript·vue.js·毕业设计·毕设
catino10 小时前
图片、文件的预览
前端·javascript
2501_9209317012 小时前
React Native鸿蒙跨平台实现推箱子游戏,完成玩家移动与箱子推动,当所有箱子都被推到目标位置时,玩家获胜
javascript·react native·react.js·游戏·ecmascript·harmonyos
layman052812 小时前
webpack5 css-loader:从基础到原理
前端·css·webpack
半桔12 小时前
【前端小站】CSS 样式美学:从基础语法到界面精筑的实战宝典
前端·css·html
AI老李12 小时前
PostCSS完全指南:功能/配置/插件/SourceMap/AST/插件开发/自定义语法
前端·javascript·postcss