组件注册
全局注册
- 全局注册的组件可以在此应用的任意组件的模板中使用
import { createApp } from 'vue' import MyComponent from './App.vue' const app = createApp({}) app.component('MyComponent', MyComponent)
局部注册
-
使用
<script setup>
的单文件组件<script setup> import ComponentA from './ComponentA.vue' </script> <template> <ComponentA /> </template>
-
没有使用
<script setup>
import ComponentA from './ComponentA.js' export default { components: { ComponentA }, setup() { // ... } }
Props–父传子参数
props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递
Props类型
- 字符串数组,[‘foo’, ‘bar’]
- 对象,{ title: String, likes: Number }
定义
-
使用
<script setup>
的单文件组件<script setup> const props = defineProps(['foo']) console.log(props.foo) </script>
-
不使用
<script setup>
export default { props: ['foo'], setup(props) { // setup() 接收 props 作为第一个参数 console.log(props.foo) } }
-
使用TypeScript时:
<script setup lang="ts"> defineProps<{ title?: string likes?: number }>() </script>
传递 prop 的细节
-
Prop 名字格式
- 子组件
defineProps({ greetingMessage: String }) // ================================= <span>{{ greetingMessage }}</span>
- 父组件
<MyComponent greeting-message="hello" />
- 子组件
-
静态 vs. 动态 Prop
- 静态
<BlogPost title="My journey with Vue" />
- 动态 ,使用
v-bind
或缩写:
<!-- 根据一个变量的值动态传入 --> <BlogPost :title="post.title" /> <!-- 根据一个更复杂表达式的值动态传入 --> <BlogPost :title="post.title + ' by ' + post.author.name" />
- 静态
-
传递不同的值类型
- Number
<!-- 虽然 `42` 是个常量,我们还是需要使用 v-bind --> <!-- 因为这是一个 JavaScript 表达式而不是一个字符串 --> <BlogPost :likes="42" /> <!-- 根据一个变量的值动态传入 --> <BlogPost :likes="post.likes" />
- Boolean
<!-- 仅写上 prop 但不传值,会隐式转换为 `true` --> <BlogPost is-published /> <!-- 虽然 `false` 是静态的值,我们还是需要使用 v-bind --> <!-- 因为这是一个 JavaScript 表达式而不是一个字符串 --> <BlogPost :is-published="false" /> <!-- 根据一个变量的值动态传入 --> <BlogPost :is-published="post.isPublished" />
- Array
<!-- 虽然这个数组是个常量,我们还是需要使用 v-bind --> <!-- 因为这是一个 JavaScript 表达式而不是一个字符串 --> <BlogPost :comment-ids="[234, 266, 273]" /> <!-- 根据一个变量的值动态传入 --> <BlogPost :comment-ids="post.commentIds" />
- Object
<!-- 虽然这个对象字面量是个常量,我们还是需要使用 v-bind --> <!-- 因为这是一个 JavaScript 表达式而不是一个字符串 --> <BlogPost :author="{ name: 'Veronica', company: 'Veridian Dynamics' }" /> <!-- 根据一个变量的值动态传入 --> <BlogPost :author="post.author" />
- Number
-
使用一个对象绑定多个 prop
const post = { id: 1, title: 'My Journey with Vue' } <!-- --> <BlogPost v-bind="post" /> <!-- 等价与 --> <BlogPost :id="post.id" :title="post.title" />
Prop 校验
defineProps() 宏中的参数不可以访问 <script setup>
中定义的其他变量,因为在编译时整个表达式都会被移到外部的函数中。
defineProps({
// 基础类型检查
// (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
propA: Number,
// 多种可能的类型
propB: [String, Number],
// 必传,且为 String 类型
propC: {
type: String,
required: true
},
// Number 类型的默认值
propD: {
type: Number,
default: 100
},
// 对象类型的默认值
propE: {
type: Object,
// 对象或数组的默认值
// 必须从一个工厂函数返回。
// 该函数接收组件所接收到的原始 prop 作为参数。
default(rawProps) {
return { message: 'hello' }
}
},
// 自定义类型校验函数
propF: {
validator(value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].includes(value)
}
},
// 函数类型的默认值
propG: {
type: Function,
// 不像对象或数组的默认,这不是一个工厂函数。这会是一个用来作为默认值的函数
default() {
return 'Default function'
}
}
})
-
所有 prop 默认都是可选的,除非声明了
required: true
。 -
除
Boolean
外的未传递的可选 prop 将会有一个默认值undefined
。 -
Boolean
类型的未传递 prop 将被转换为false
。这可以通过为它设置default
来更改——例如:设置为default: undefined
将与非布尔类型的 prop 的行为保持一致。 -
如果声明了
default
值,那么在 prop 的值被解析为undefined
时,无论 prop 是未被传递还是显式指明的undefined
,都会改为default
值。
Boolean 类型转换
-
子组件的部分内容
defineProps({ disabled: Boolean })
-
父组件的部分内容
<!-- 等同于传入 :disabled="true" --> <MyComponent disabled /> <!-- 等同于传入 :disabled="false" --> <MyComponent />
组件事件–子向父传递事件
声明触发的事件
-
<script setup>
<script setup> const emit = defineEmits({ submit(payload) { // 通过返回值为 `true` 还是为 `false` 来判断 // 验证是否通过 } }) </script> <script setup> const emit = defineEmits(['inFocus', 'submit']) function buttonClick() { emit('submit') } </script>
-
<script setup lang="ts">
<script setup lang="ts"> const emit = defineEmits<{ (e: 'change', id: number): void (e: 'update', value: string): void }>() </script>
-
非
<script setup>
-
非解构
export default { emits: ['inFocus', 'submit'], setup(props, ctx) { ctx.emit('submit') } }
-
解构
export default { emits: ['inFocus', 'submit'], setup(props, { emit }) { emit('submit') } }
-
基本用法
-
子组件
<!-- MyComponent --> <button @click="$emit('someEvent')">click me</button>
-
父组件
<MyComponent @some-event="callback" /> <!-- 监听器也支持 .once 修饰符 --> <MyComponent @some-event.once="callback" />
事件参数
-
子组件
<button @click="$emit('increaseBy', 1)"> Increase by 1 </button>
-
父组件
<!--内连函数 --> <MyButton @increase-by="(n) => count += n" />
<!--方法函数 --> <MyButton @increase-by="increaseCount" /> ... ... function increaseCount(n) { count.value += n }
事件校验
- 事件校验
<script setup> const emit = defineEmits({ // 没有校验 click: null, // 校验 submit 事件 submit: ({ email, password }) => { if (email && password) { return true } else { console.warn('Invalid submit event payload!') return false } } }) function submitForm(email, password) { emit('submit', { email, password }) } </script>
配合 v-model
使用
- 1
-
子组件
<script setup> defineProps(['modelValue']) defineEmits(['update:modelValue']) </script> <template> <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" /> </template>
-
父组件
<script setup> import { ref } from 'vue' import CustomInput from './CustomInput.vue' const message = ref('hello') </script> <template> 子组件input:<CustomInput v-model="message" /> <br> 父组件input:<input v-model="message" /> <br> 父组件显示:{{ message }} </template>
-
显示
-