Vue 组件
组件注册
一个 Vue 组件在使用前需要先被“注册”,才能在渲染模板时找到其对应的实现。
组件注册有两种方式:
- 全局注册
- 局部注册
全局注册
使用 Vue 应用实例的 .component()
方法:
mport { createApp } from 'vue'
const app = createApp({})
app.component(
// 注册的名字
'MyComponent',
// 组件的实现
{ /* ... */ }
)
使用 SFC:
import MyComponent from './App.vue' app.component('MyComponent', MyComponent)
链式调用:
app
.component('ComponentA', ComponentA)
.component('ComponentB', ComponentB)
.component('ComponentC', ComponentC)
全局组件的使用:
<!-- 在当前应用的任意组件中都可用 -->
<ComponentA/>
<ComponentB/>
<ComponentC/>
局部注册
在使用 <scirpt setup>
SFC 中:
<script setup>
import ComponentA from './ComponentA.vue'
</script>
<template>
<ComponentA />
</template>
在没有使用 <scirpt setup>
中:
import ComponentA from './ComponentA.js'
export default {
// 显式注册
components: {
ComponentA
},
setup() {
// ...
}
}
组件名格式
SFC 和内联字符串模板中推荐:
MyComponent
<MyComponent />
DOM 模板内:
<my-component>
Props
一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props,哪些是透传 attribute。
使用 <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)
}
}
props 传递:定义props:
<span>{{ greetingMessage }}</span>
defineProps({ greetingMessage: String })
props 传递:传值:
<MyComponent greeting-message="hello" />
单向数据流
所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。
const props = defineProps(['foo'])
// ❌ 警告!prop 是只读的!
props.foo = 'bar'
导致你想要更改一个 prop 的需求通常来源于以下两种场景:
1、prop 被用于传入初始值:
const props = defineProps(['initialCounter'])
// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.initialCounter)
2、需要对传入的 prop 值做进一步的转换:
const props = defineProps(['size'])
// 该 prop 变更时计算属性也会自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())
注:如果要改变 props 的值,多数场景下,子组件应该抛出一个事件来通知父组件做出改变。
事件
触发事件:
<!-- MyComponent -->
<button @click="$emit('someEvent')">Click Me</button>
监听事件:
<MyComponent @some-event="callback" />
<!-- 监听一次 -->
<MyComponent @some-event.once="callback" />
提供额外参数:
<button @click="$emit('increaseBy', 1)">
Increase by 1
</button>
父组件:
<MyButton @increase-by="(n) => count += n" />
<!-- 抽离方法 -->
<MyButton @increase-by="increaseCount" />
声明触发的事件:
- 在
<template>
中使用的$emit
方法不能在组件的<script setup>
部分中使用 - 可以
defineEmit()
(宏)会返回一个相同作用的函数供我们使用:
<script setup>
const emit = defineEmits(['inFocus', 'submit'])
function buttonClick() {
emit('submit')
}
</script>
组件 v-model
UserName.vue:
<script setup>
const firstName = defineModel('firstName')
const lastName = defineModel('lastName')
</script>
<template>
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
</template>
App.vue:
<script setup>
import { ref } from 'vue'
import UserName from './UserName.vue'
const first = ref('John')
const last = ref('Doe')
</script>
<template>
<h1>{{ first }} {{ last }}</h1>
<UserName
v-model:first-name="first"
v-model:last-name="last"
/>
</template>
透传 Attributes
“透传 attribute”指的是传递给一个组件,却没有被该组件声明为 props
或 emits
的 attribute 或者 v-on
事件监听器。最常见的例子就是 class
、style
和 id
。
插槽
<FancyButton>
Click me! <!-- 插槽内容 -->
</FancyButton>
<button class="fancy-btn">
<slot></slot> <!-- 插槽出口 -->
</button>
依赖注入
- Prop 逐级透传问题
- Provide(提供)
- 应用层 Provide
- Inject(注入)
- 和响应式数据配合使用
- 使用 Symbol 作为注入名
异步组件
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./components/MyComponent.vue')
)
资源
- 代码:code/vue-start