源本科技 | 码上会

Vue3 props 父传子

2026/04/23
42
0

引言

Props 是 Vue 中父组件向子组件传递数据的核心机制,用于实现父子组件之间的单向数据流,保证应用状态的可预测性。

  • Props 是组件的自定义属性,通过标签属性的形式为子组件传值

  • Props 是只读的,子组件禁止直接修改,严格遵循单向数据流规范

我们在原有输入框组件的基础上,新增标签文本、占位提示、最大输入长度等 Props,让组件更灵活复用。

优化子组件代码

修改文件 src/components/MyText/src/index.vue,集成 Props 定义、模板使用,兼容原有功能且符合最新语法:

<template>
  <!-- 使用 Props 渲染标签文本 -->
  <label class="my-label" for="text-input">{{ label }}:</label>
  <!-- 绑定 Props 与原生属性,保留双向绑定能力 -->
  <input
    id="text-input"
    type="text"
    class="my-input-text"
    :value="modelValue"
    @input="$emit('update:modelValue', ($event.target as HTMLInputElement)?.value ?? '')"
    :placeholder="placeholder"
    :maxlength="maxLength"
  />
</template>

<script setup lang="ts">
// defineProps 是编译时宏,无需手动导入
// 1. 对象写法:支持类型校验、必填、默认值
const props = defineProps({
  // 标签文本:字符串类型,必填项
  label: {
    type: String,
    required: true,
  },
  // 占位提示:字符串类型,非必填,带默认值
  placeholder: {
    type: String,
    default: '请输入内容',
  },
  // 最大输入长度:数字类型,非必填
  maxLength: {
    type: Number,
    default: 20,
  },
  // 双向绑定值:继承原有功能
  modelValue: {
    type: String,
    default: '',
  },
})

// 定义事件,配合 v-model 使用
const emit = defineEmits(['update:modelValue'])
</script>

<style scoped>
/* 中英文与数字之间添加空格 */
.my-label {
  margin-right: 8px;
  font-size: 14px;
  color: #333;
}

.my-input-text {
  width: 80%;
  padding: 10px;
  border-radius: 3px;
  border: 1px solid #ccc;
  outline: none;
}

.my-input-text:focus {
  border-color: #409eff;
}
</style>

统一入口文件

沿用之前的 src/components/MyText/index.ts,保持组件导出规范不变:

// 导入组件核心文件
import MyText from './src/index.vue'

// 具名导出(用于局部注册)
export { MyText }

// 默认导出(支持 app.use 全局注册)
export default {
  install(app: any) {
    app.component('MyText', MyText)
  }
}

父组件传递 Props 数据

在业务组件中使用 MyText,通过简写语法传递 Props,支持静态值和动态值两种方式:

<template>
  <div class="demo-container">
    <h3>Props 传值演示</h3>
    
    <!-- 1. 静态传值:直接传递字符串 -->
    <MyText label="用户名" placeholder="请输入账号" />

    <!-- 2. 动态传值:绑定响应式数据 -->
    <MyText :label="userLabel" :max-length="10" v-model="value" />
  </div>
</template>

<script setup lang="ts">
// 导入组件
import { MyText } from './components/MyText'
import { ref } from 'vue'

// 动态 Props 数据
const userLabel = ref('密码')
const value = ref('')
</script>

<style scoped>
.demo-container {
  padding: 20px;
}
</style>

Props 解析

定义 Props

defineProps 是 Vue 3 <script setup> 专属的编译时宏,无需导入、无需调用,专门用于声明组件 Props,支持两种配置方式:

  • 简易写法(仅指定类型)

const props = defineProps(['label', 'placeholder'])
  • 完整写法(推荐,支持校验、必填、默认值)

const props = defineProps({
  label: {
    type: String,       // 数据类型
    required: true,     // 是否必填
    default: '标题',     // 默认值
  }
})

支持的 Props 类型

type 支持所有 JS 基础类型与引用类型:

  • 基础类型:StringNumberBooleanSymbol

  • 引用类型:ObjectArrayFunction

Props 两种传值方式

  • 静态传值:直接传递固定字符串

<MyText label="用户名" />
  • 动态传值:使用 :v-bind 简写)绑定变量 / 表达式

<MyText :label="dynamicLabel" :max-length="10" />

TypeScript 类型 Props

在 TS 项目中,推荐使用泛型写法定义 Props,类型校验更严格、代码更简洁:

<script setup lang="ts">
// TS 泛型 + withDefaults 定义默认值(Vue 3 最新语法)
interface Props {
  label: string
  placeholder?: string
  maxLength?: number
  modelValue?: string
}

// 声明 Props 并配置默认值
const props = withDefaults(defineProps<Props>(), {
  placeholder: '请输入内容',
  maxLength: 20,
  modelValue: '',
})
</script>

Props 只读与单向数据流

  • Props 是只读的 子组件不能直接修改 Props 的值,Vue 会抛出警告,避免数据混乱:

// ❌ 错误写法
props.label = '新标题'
  1. 单向数据流
    数据只能从 父组件 → 子组件 单向传递,保证数据流向唯一、易于调试。

  2. 修改方案
    子组件需要修改数据时,通过 emit 触发事件,由父组件修改原值。

说明

  1. Prop 命名规范
    子组件使用小驼峰maxLength),模板中使用短横线分隔max-length),Vue 会自动转换。

  2. 必填 Prop 校验
    如果设置 required: true,父组件未传值时,控制台会抛出警告,提升开发健壮性。

  3. 属性透传
    未定义的原生属性(如 classid)会自动绑定到组件根元素,无需额外声明。

总结

  1. Props 是父子组件传值的核心,遵循单向数据流、只读不可改原则

  2. defineProps 是 Vue 3 声明 Props 的标准方式,支持校验与默认值

  3. TS 泛型写法是企业级开发的最佳实践,类型安全更可靠

  4. 静态传值直接书写,动态传值必须使用 : 简写语法