组件注册
全局注册 使用 .component() 方法
import {createApp} from 'vue';
const app = createApp({})
import ComponentA from './ComponentA';
app.component('ComponentA',ComponentA)
局部注册 在使用 <script></script> 单文件组件中,导入组件即可直接在模板中使用,无需注册
<script setup>
import ComponentA from './ComponentA.vue'
</script>
<template>
<ComponentA />
</template>
组件名格式
使用 PascalCase 作为组件名的注册格式,Vue 支持将模板中使用 kebab-case 的标签解析为使用 PascalCase 注册的组件。这意味着一个以MyComponent为名注册的组件,在模板中可以通过 <
MyComponent>
或 <
my-component>
引用
Props
在使用<script setup> 的单文件组件中,props 可以使用 defineProps() 宏来声明:
<script setup>
const props = defineProps(['foo']) //数组类型
// 对象类型
const props2 = defineProps({
title: String,
likes: Number,
example:{
default(){
return '默认值'
},
type:String,
require:false
}
})
console.log(props.foo)
</script>
使用一个对象绑定多个prop
如果你想要将一个对象的所有属性都当作 props 传入,你可以使用没有参数的 v-bind ,即只使用 v-bind而非 :prop-name
const post = {
id:1,
title:'这是一个标题'
}
<Component v-bind="post" />
实际上等价于
<Component :id="post.id" :title="post.title" />
单向数据流
所有的props 都遵循单向绑定原则,props 因父组件更新而变化,将新的状态向下流往子组件,不会逆向传递。
1.prop 被用于传入初始值;而子组件想在之后将其作为一个局部数据属性。在这种情况下,最好是新定义一个局部数据属性,从 props 上获取初始值即可:
const props = defineProps(['initialCounter'])
// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.initialCounter)
2.需要对传入的 prop 值做进一步的转换。在这种情况中,最好是基于该 prop 值定义一个计算属性:
const props = defineProps(['size'])
// 该 prop 变更时计算属性也会自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())
组件事件
在组件模板中,可直接使用 $emit 方法触发自定义事件
<!-- MyComponent -->
<button @click="$emit('someEvent')">click me</button>
<MyComponent @some-event="callback" />
在<script setup>中 使用 defineEmits
<script setup>
const emit = defineEmits(['inFocus'])
function buttonClick(){
emit('inFocus')
}
</script>
事件校验
要为事件添加校验,那么改事件可被复制一个函数,接受的参数就是抛出事件时传入emit的内容,返回一个布尔值来表明事件是否合法
<script setup>
const emit = defineEmits({
//没有校验
click:null,
// 校验submit事件
submit:({email,password})=>{
if(email && password) {
return true
}else{
console.warn('传参不对')
return false
}
}
})
function submitForm(email,password){
emit('submit',{email,password})
}
</script>
组件v-model
v-model 可以在组件上使用以实现 双向绑定。
从vue3.4 开始, 推荐实现方式是使用 defineModel() 宏:
// Parent.vue
<Child v-model="count"/>
// Child.vue
<script setup>
const model = defineModel()
function update(){
model.value++
}
// defineModel 还可设置默认值、必填
const model2 = defineModel({require:true,default:0})
</script>
<template>
<p>父组件绑定的值是 {{model}}</p>
<input v-model="model" />
</template>
defineModel() 返回的值是一个 ref 。它可以像其他ref 一样被访问以及修改,不过它能起到父组件和当前变量之间的双向绑定作用.
v-model 的参数
组件上的v-model 也可以接受一个参数,在子组件中,可通过字符串作为第一个参数传递给 defineModel() 来支持相应的参数
<MyComponent v-model:title="bookTitle" v-model:last-name="last">
// MyComponent.vue
<script setup>
const title = defineModel('title');
const lastName = defineModel('lastName')
// 额外传递prop选项
const title = defineModel('title',{require:true})
</script>
<template>
<input type="text" v-model="title" />
</template>
插槽Slots
<button type="submit">
<slot>
Submit <!-- 默认内容 -->
</slot>
</button>
<SubmitButton /> // “Submit”将会被作为默认内容渲染:
<SubmitButton>Save</SubmitButton> // “Save” 会被显示 被显式提供的内容会取代默认内容
具名插槽
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
<BaseLayout>
<template v-slot:header>
// header 插槽内容放这里
</template>
<template #main> // # 简写
// main插槽内容放这里
</template>
<template v-slot:[dynamicSlotName]>
// 动态插槽名
</template>
<template #[dynamicSlotName]>
// 动态插槽名
</template>
</BaseLayout>
依赖注入
provide 和 inject,解决父级组件向所有后代组件传递数据,不需要层层传递。
Provide(提供)
<script setup>
import {ref,provide,readonly} from 'vue';
// provide(注入名,值)
const count = ref(0);
provide('message',count)
// 传入数据不被注入方更改
provide('message',readonly(count))
</script>
应用层provide
import { createApp } from 'vue'
const app = createApp({})
app.provide(/* 注入名 */ 'message', /* 值 */ 'hello!')
Inject(注入)
<script setup>
import {inject} from 'vue'
const message = inject('message','默认值')
</script>
在一些场景中,默认值可能需要通过调用一个函数或初始化一个类来取得。为了避免在用不到默认值的情况下进行不必要的计算或产生副作用,我们可以使用工厂函数来创建默认值:第三个参数表示默认值应该被当作一个工厂函数。
const value = inject('key', () => new ExpensiveClass(), true)
和响应式数据配合使用
需要在注入方组件中更改数据时,需传递一个更改数据的方法函数
<!-- 在供给方组件内 -->
<script setup>
import { provide, ref } from 'vue'
const location = ref('North Pole')
function updateLocation() {
location.value = 'South Pole'
}
provide('location', {
location,
updateLocation
})
</script>
// 在注入方组件
<script setup>
import {inject} from 'vue';
const {location,updateLocation} = inject('location)
</script>
<template>
<button @click="updateLocation">{{location}}</button>
</template>