一、组件注册
组件注册分全局注册和局部注册 一个可以在全局都可以用的 一个是只能在局部用的
全局注册:使用app.component() 方法 我们可以在main.js的文件中 用app实例去注册组件 这样我们就可以在全局单文件里面使用这个组件
缺点:全局注册在大型项目中使项目的依赖关系变得不那么明确。在父组件中使用子组件时,不太容易定位子组件的实现。和使用过多的全局变量一样,这可能会影响应用长期的可维护性
import MyComponent from './App.vue'
app.component('MyComponent', MyComponent)
也可以链式调用
app
.component('ComponentA', ComponentA)
.component('ComponentB', ComponentB)
.component('ComponentC', ComponentC)
局部注册:局部注册的组件需要在使用它的父组件中显式引入 并且只能在该父组件中使用 并且这个方式 对tree-shaking更加友好
在使用 <script setup> 的单文件组件中,导入的组件可以直接在模板中使用,无需注册:
<script setup>
import ComponentA from './ComponentA.vue'
</script>
<template>
<ComponentA />
</template>
组件名格式:
-
PascalCase 是合法的 JavaScript 标识符。这使得在 JavaScript 中导入和注册组件都很容易,同时 IDE 也能提供较好的自动补全。
-
<PascalCase />
在模板中更明显地表明了这是一个 Vue 组件,而不是原生 HTML 元素。同时也能够将 Vue 组件和自定义元素 (web components) 区分开来。
二、Props
2.1 props声明
在使用 <script setup>
的单文件组件中,props 可以使用 defineProps()
宏来声明:
<script setup>
const props = defineProps(['foo'])
console.log(props.foo)
</script>
在没有使用 <script setup>
的组件中,prop 可以使用 props 选项来声明:
export default {
props: ['foo'],
setup(props) {
// setup() 接收 props 作为第一个参数
console.log(props.foo)
}
}
除了使用字符串数组来声明 prop 外,还可以使用对象的形式:
// 使用 <script setup>
defineProps({
title: String,
likes: Number
})
// 非 <script setup>
export default {
props: {
title: String,
likes: Number
}
}
2.2 传递prop的细节
名字格式:声明的时候最好是用驼峰式 camelCase 形式 在模板上使用是用 kebab-case 形式
静态和动态prop 动态多了一个 v-bind 或者缩写 :
传递的值类型:number boolean array object
可以使用一个对象绑定一个prop 这里只能用一个v-bind 如:
const post = {
id: 1,
title: 'My Journey with Vue'
}
<BlogPost v-bind="post" />
<BlogPost :id="post.id" :title="post.title" />
2.3 单向数据流
所所谓的单向数据流 就是不能回流去修改数据 所有的props都遵循着单向绑定原则 props因为父组件的更新而变化。比如 每次父组件更新后,所有的子组件中的 props 都会被更新到最新值,这意味着你不应该在子组件中去更改一个 prop。
如果想要想通过子组件去修改props的话 只能通过子组件抛出一个事件去修改(后面会讲到)
2.4 prop校验
要声明对props的校验 你可以向defineProps() 宏提供一个带有props校验选项的对象 例如:
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的默认都是可选的 除非声明了requried:true
除 Boolean
外的未传递的可选 prop 将会有一个默认值 undefined
三、事件
3.1 触发与监听事件
在组件的模板中 可以直接使用 $emit 方法去触发自定义事件 然后用v-on(缩写为@)来监听事件,事件名可以用camelCase 形式命名的事件 但在父组件中可以使用 kebab-case 形式来监听 模板中我们也推荐使用 kebab-case 形式来编写监听器。
<!-- MyComponent -->
<button @click="$emit('someEvent')">click me</button>
//父组件
<MyComponent @some-event="callback" />
//或
<MyComponent @some-event.once="callback" />
3.2 事件参数
我们可以给$emit 提供一个额外的参数 :$emit('increaseBy', 1) 父组件在方法接收
3.3 声明触发事件
可以使用defineEmits()宏来声明它要触发的事件,$emit
方法不能在组件的 <script setup>
部分中使用 但是这个宏会返回一个相同作用的函数来使用 defineEmits()
宏不能在子函数中使用 它必须直接放置在 <script setup>
的顶级作用域下。
<script setup>
const emit = defineEmits(['inFocus', 'submit'])
function buttonClick() {
emit('submit')
emit('inFocus')
}
</script>
3.4 事件校验
要给事件添加校验 那么事件可以被赋值为一个函数 接收的参数就是报出事件时传入emit的内容 返回一个布尔值来表明事件是否合法。
<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
4.1 用法
当我们需要在父组件和子组件实现一个数据的双向绑定 则需要v-model 具体用法:
<!-- CustomInput.vue -->
<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])
</script>
<template>
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
/>
</template>
//使用
<CustomInput v-model="searchText" />
4.2 v-model的参数
默认情况下 没有命名的时候 v-model在组件上都是使用modelValue 作为prop 并以update:modelVlaue 作为对应事件。我们可以通过给v-model指定一个参数来更改这些名字:
<MyComponent v-model:title="bookTitle" />
//在这个例子中,子组件应声明一个 title prop,
//并通过触发 update:title 事件更新父组件值:
<!-- MyComponent.vue -->
<script setup>
defineProps(['title'])
defineEmits(['update:title'])
</script>
<template>
<input
type="text"
:value="title"
@input="$emit('update:title', $event.target.value)"
/>
</template>
4.3 v-model 修饰符
在我们想自定义组件的v-model支持自定义的修饰符 比如 自定义个一个 capitalize, 它会自动将v-model绑定输入的字符串值第一个字母转为大写:
<MyComponent v-model.capitalize="myText" />
我们可以通过一个 modelModifiers prop在组件内访问到 有了这个prop 我们就可以编写一个处理函数来改变抛出的值 下面是实现方法:
<script setup>
const props = defineProps({
modelValue: String,
modelModifiers: { default: () => ({}) }
})
const emit = defineEmits(['update:modelValue'])
function emitValue(e) {
let value = e.target.value
if (props.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
emit('update:modelValue', value)
}
</script>
<template>
<input type="text" :value="modelValue" @input="emitValue" />
</template>
对于又有参数又有修饰符的 v-model 绑定 生成的prop 是 arg + "Modifiers" 比如:
<MyComponent v-model:title.capitalize="myText">
声明
const props = defineProps(['title', 'titleModifiers'])
defineEmits(['update:title'])
console.log(props.titleModifiers) // { capitalize: true }