Vue演练场基础知识(六)Props传参+Emits事件

为学习Vue基础知识,我动手操作通关了Vue演练场,该演练场教程的目标是快速体验使用 Vue 是什么感受,设置偏好时我选的是选项式 + 单文件组件。以下是我结合深入指南写的总结笔记,希望对Vue初学者有所帮助。

文章目录

十三. Props传参

Props 声明

子组件可以通过 props 从父组件接受动态数据,同时它需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props,哪些是透传 attribute。

"透传 attribute"指的是传递给一个组件,却没有被该组件声明为 props 或 emits 的 attribute 或者 v-on 事件监听器。最常见的例子就是 class、style 和 id。"透传 attribute"会被自动原封不动透传到收到它的组件的根元素上。

js 复制代码
// 在子组件中
export default {
  props: {
    msg: String
  }
  created() {
  	console.log(this.msg);
  }
}
// 或
export default {
  props: ['msg']
}

对于以对象形式声明的每个属性,key 是 prop 的名称,而值则是该 prop 预期类型的构造函数。比如,如果要求一个 prop 的值是 number 类型,则可使用 Number 构造函数作为其声明的值。

props声明的作用不只是声明props类型,一定程度上作为组件的文档,它还可以:

  1. 其他开发者在使用你的组件时传递了错误的类型,在浏览器控制台中抛出警告。
  2. 显式或隐式地设置默认值
  3. Boolean 类型的 props 有特别的类型转换规则

传递 prop 的细节

Prop 名字格式

推荐的命名形式:

props 声明用camelCase 形式;

props 传递用kebab-case 形式;

组件名用PascalCase 形式。

html 复制代码
<script>
export default {
  props: {
    greetingMessage: String
  }
}
</script>
<template>
  <MyComponent greeting-message="hello" />
</template>

静态 vs. 动态 Props

父组件可以通过 props 向子组件传递固定值,或使用 v-bind 语法传递动态值。

html 复制代码
// 在父组件中
<ChildComp msg="Hello World" :type="myObj.type" />

传递不同的值类型

html 复制代码
<ChildComp 
   :likes="42"
   :comment-ids="[234, 266, 273]"
   :author="{
     name: 'Veronica',
     company: 'Veridian Dynamics'
   }"
   v-model="obj"
   is-flag
/>

v-model="obj"等同于逐个绑定obj中的所有属性。

若已声明is-flag类型为Boolean,则只传is-flag不传值等同于:is-flag="true",不传is-flag等同于:is-flag="false"

单向数据流

所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递,也不允许修改props。

一般来说,我们修改props的需求可以根据需求场景不同用以下两种方法代替:

  1. 场景1: prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。
js 复制代码
export default {
  props: ['initialCounter'],
  data() {
    return {
 	  // this.initialCounter仅用于为counter赋初始值,与counter后续更新无关
      counter: this.initialCounter
    }
  }
}
  1. 场景2:需要对传入的 prop 值做进一步的转换。
js 复制代码
export default {
  props: ['size'],
  computed: {
    // 该 prop 变更时计算属性也会自动更新
    normalizedSize() {
      return this.size.trim().toLowerCase()
    }
  }
}

更改对象 / 数组类型的 props

虽然在子组件里修改父组件传下来的对象 / 数组类型的 props内部的值,但在大多数情况下是一种不好的实践,最好不要这样。

代替的方法是子组件抛出一个事件来通知父组件做出改变。

Prop 校验

你可以向 props 选项提供一个带有 props 校验选项的对象,更细致地声明对传入的 props 的校验要求:

js 复制代码
export default {
  props: {
    // 基础类型检查
    //(给出 `null` 和 `undefined` 值则会跳过任何类型检查)
    propA: Number,
    // 多种可能的类型
    propB: [String, Number],
    // 必传,且为 String 类型
    propC: {
      type: String,
      required: true
    },
    // 必传但可为 null 的字符串
    propD: {
      type: [String, null],
      required: true
    },
    // Number 类型的默认值
    propE: {
      type: Number,
      default: 100
    },
    // 对象类型的默认值
    propF: {
      type: Object,
      // 对象或者数组应当用工厂函数返回。
      // 工厂函数会收到组件所接收的原始 props(即父组件传入propF的值)
      // 作为参数
      default(rawProps) {
        return { message: 'hello' }
      }
    },
    // 自定义类型校验函数
    // 在 3.4+ 中完整的 props 作为第二个参数传入
    propG: {
      validator(value, props) {
        // The value must match one of these strings
        return ['success', 'warning', 'danger'].includes(value)
      }
    },
    // 函数类型的默认值
    propH: {
      type: Function,
      // 不像对象或数组的默认,这不是一个
      // 工厂函数。这会是一个用来作为默认值的函数
      default() {
        return 'Default function'
      }
    }
  }
}

一些补充细节:

  1. 所有 prop 默认都是可选的,除非声明了 required: true。
  2. 除 Boolean 外的未传递的可选 prop 将会有一个默认值 undefined。
  3. Boolean 类型的未传递 prop 将被转换为 false。这可以通过为它设置 default 来更改------例如:设置为 default: undefined 将与非布尔类型的 prop 的行为保持一致。
  4. 如果声明了 default 值,那么在 prop 的值被解析为 undefined 时,无论 prop 是未被传递还是显式指明的 undefined,都会改为 default 值。
    注意 prop 的校验是在组件实例被创建之前,所以实例的属性 (比如 data、computed 等) 将在 default 或 validator 函数中不可用。

运行时类型检查​

校验选项中的 type 可以是下列这些原生构造函数:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol
  • Error
    也可以自定义类或构造函数,Vue 将会通过 instanceof 来检查类型是否匹配:
js 复制代码
class Student {
	firstName: string;
	lastName: string;
}
class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
}

可为 null 的类型

注意如果type仅为null而非使用数组语法,它将允许任何类型。

Boolean 类型转换

为了更贴近原生 boolean attributes 的行为,声明为 Boolean 类型的 props 有特别的类型转换规则。

复制代码
<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />

<!-- 等同于传入 :disabled="false" -->
<MyComponent />

当一个 prop 被声明为允许多种类型时,Boolean 的转换规则也将被应用。然而,当同时允许 String 和 Boolean 时,有一种边缘情况------只有当 Boolean 出现在 String 之前时,Boolean 转换规则才适用:

js 复制代码
export default {
  props: {
    disabled1: [Boolean, Number], // <MyCompo disabled />将被解析为 disabled=true
    disabled2: [Boolean, String], // disabled=true
    disabled3: [Number, Boolean], // disabled=true
    disabled4: [String, Boolean], // disabled=""(空字符串)
  }
}

十四. Emits事件

触发与监听事件

父组件除了向子组件传递props、attribute,还可以传递事件,子组件收到事件后可以在需要的时候触发,并向方法中传递参数,实现子组件向父组件传值。

html 复制代码
// ChildComp.vue
<script>
export default {
	data() {
		msg: '321'
	},
	emits: ['setMsg'], // 注册事件
	created() {
		this.$emit('setMsg', this.msg); // 调用事件1
	}
}
</script>
<template>
	<div @click="$emit('setMsg', msg)">向父组件传值<div> <!-- 调用事件2 -->
</template>
html 复制代码
<script>
export default {
	data() {
		return {msg: '123'};
	},
	components: {ChildComp},
	methods: {
		func1(parameter) {...}
	}		
}
</script>
<template>
	<ChildComp @set-msg="newMsg => msg = newMsg" /> <!-- 传入事件1 -->
	<ChildComp @set-msg.once="newMsg => msg = newMsg" /> <!-- 传入事件2 -->
	<ChildComp @set-msg.once="func1" /> <!-- 传入事件3 -->
	{{msg}}
</template>

事件参数

js 复制代码
// 第2~n个参数会成为传入setMsg函数的第1~(n-1)个参数
this.$emit('setMsg', 参数1, 参数2, 参数3);

声明触发的事件

js 复制代码
export default {
  emits: ['inFocus', 'submit']
}

emits还支持对象语法,并能在执行事件前对传入的参数进行校验。

js 复制代码
export default {
  emits: {
    submit(payload: { email: string, password: string }) {
      // 通过返回值为 `true` 还是为 `false` 来判断
      // 验证是否通过
    }
  }
}

推荐完整地声明所有要触发的事件,作为文档记录组件的用法,又能让 Vue 更好地将事件和透传 attribute 作出区分:如果一个原生事件的名字 (例如 click) 被定义在 emits 选项中,则监听器只会监听组件触发的 click 事件而不会再响应原生的 click 事件。

事件校验

使用对象语法就能对emit事件进行校验,但不能校验DOM原生事件。

js 复制代码
export default {
  emits: {
    // 没有校验
    click: null,

    // 校验 submit 事件
    submit: ({ email, password }) => {
      if (email && password) {
        return true
      } else {
        console.warn('Invalid submit event payload!')
        return false
      }
    }
  },
  methods: {
    submitForm(email, password) {
      this.$emit('submit', { email, password })
    }
  }
}
相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax