跳到主要内容

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 事件监听器。最常见的例子就是 classstyle 和 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

参考