Vue3 进阶:从选项式 API 到组件通信
学完 Vue3 的基础语法后,真正影响开发效率的往往不是某一个指令,而是能不能把组件、状态、表单和生命周期串起来使用。本文围绕 Vue3 中最常见的进阶知识点展开:选项式 API、单文件组件、应用挂载、表单处理、组件复用、props 传值、子组件事件通信以及生命周期钩子。
如果你已经能写出简单页面,但还不确定 Vue 组件内部的数据如何组织、父子组件如何协作、生命周期代码应该放在哪里,这篇文章可以作为一份清晰的梳理。
1. 选项式 API:用对象组织组件逻辑
Vue3 同时支持组合式 API 和选项式 API。对于刚开始进阶的同学来说,选项式 API 更直观:组件的状态、方法、计算属性和生命周期钩子都被放在一个对象中。
vue
<script>
export default {
// data() 返回的属性会成为响应式状态
// 并且可以通过 this 访问
data() {
return {
count: 0,
author: {
books: ['Vue Guide']
}
}
},
// methods 用来声明事件处理函数或普通方法
methods: {
increment() {
this.count++
}
},
// computed 用来声明依赖响应式数据的计算结果
computed: {
publishedBooksMessage() {
return this.author.books.length > 0 ? 'Yes' : 'No'
}
},
// mounted 会在组件挂载完成后执行
mounted() {
console.log(`The initial count is ${this.count}.`)
}
}
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
<p>Has published books: {{ publishedBooksMessage }}</p>
</template>
这段代码包含了选项式 API 的几个核心部分:
data:声明组件内部的响应式数据。methods:声明组件方法,常用于事件处理。computed:声明计算属性,适合处理由状态推导出的结果。mounted:生命周期钩子,适合在组件完成初始渲染后执行逻辑。
当组件逻辑还不复杂时,选项式 API 的结构非常清楚;随着业务增长,也可以逐步过渡到组合式 API。
2. 单文件组件:把结构、逻辑和样式放在一起
Vue 项目中最常见的组件形式是 .vue 单文件组件。它通常由三个区域组成:script、template 和 style。
vue
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
<template>
<button @click="count++">Count is: {{ count }}</button>
</template>
<style scoped>
button {
font-weight: bold;
}
</style>
其中:
<script>负责组件逻辑。<template>负责页面结构。<style scoped>负责当前组件样式,scoped可以让样式尽量只作用于当前组件。
单文件组件的好处是边界清晰,适合复用和维护。一个按钮、一个列表、一张卡片,甚至一个完整页面,都可以被拆成组件。
3. 根组件与应用挂载
Vue 应用需要通过 createApp 创建应用实例,再挂载到页面中的某个 DOM 节点上。
vue
import { createApp } from 'vue'
const app = createApp({
data() {
return {
count: 0
}
}
})
app.mount('#app')
这里的 #app 对应 HTML 中的根节点:
html
<div id="app"></div>
可以把根组件理解为整个 Vue 应用的入口。实际项目中,我们通常会把根组件写成 App.vue,再在 main.js 或 main.ts 中完成挂载。
4. 表单处理:用 v-model 简化双向绑定
表单是前端项目中非常高频的场景。Vue 提供了 v-model,可以让输入框的值和组件状态保持同步。
文本输入
vue
<p>Message is: {{ message }}</p>
<input v-model="message" placeholder="edit me" />
当用户输入内容时,message 会自动更新;当 message 被代码修改时,输入框也会同步变化。
多行文本
vue
<span>Multiline message is:</span>
<p style="white-space: pre-line;">{{ message }}</p>
<textarea v-model="message" placeholder="add multiple lines"></textarea>
多行文本常用于评论、备注、说明等场景。展示时可以配合 white-space: pre-line; 保留换行效果。
复选框
vue
<input type="checkbox" id="checkbox" v-model="checked" />
<label for="checkbox">{{ checked }}</label>
单个复选框通常绑定布尔值,适合表示"是否启用""是否同意"等状态。
单选按钮
vue
<div>Picked: {{ picked }}</div>
<input type="radio" id="one" value="One" v-model="picked" />
<label for="one">One</label>
<input type="radio" id="two" value="Two" v-model="picked" />
<label for="two">Two</label>
多个单选按钮绑定同一个变量,变量值会等于当前被选中的 value。
选择器
单选选择器:
vue
<div>Selected: {{ selected }}</div>
<select v-model="selected">
<option disabled value="">Please select one</option>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
多选选择器:
vue
<div>Selected: {{ selected }}</div>
<select v-model="selected" multiple>
<option>A</option>
<option>B</option>
<option>C</option>
</select>
如果选项来自接口或配置,可以使用 v-for 动态渲染:
vue
<div>Selected: {{ selected }}</div>
<select v-model="selected">
<option
v-for="option in options"
:key="option.value"
:value="option.value"
>
{{ option.text }}
</option>
</select>
这里建议给 v-for 添加 key,这样 Vue 可以更稳定地追踪列表节点。
5. 组件基础:定义组件与使用组件
组件可以理解为可复用的 UI 单元。一个 .vue 文件通常就是一个组件。
例如,我们定义一个 ButtonCounter.vue:
vue
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
<template>
<button @click="count++">You clicked me {{ count }} times.</button>
</template>
然后在父组件中引入并使用它:
vue
<script>
import ButtonCounter from './ButtonCounter.vue'
export default {
components: {
ButtonCounter
}
}
</script>
<template>
<h1>Here is a child component!</h1>
<ButtonCounter />
</template>
组件拆分的关键不是"越细越好",而是看它是否具备复用价值、是否能让当前页面更容易维护。
6. props:父组件向子组件传值
父组件向子组件传递数据,最常用的方式是 props。
子组件 BlogPost.vue:
vue
<script>
export default {
props: ['title']
}
</script>
<template>
<h4>{{ title }}</h4>
</template>
父组件:
vue
<script>
import BlogPost from './BlogPost.vue'
export default {
components: {
BlogPost
},
data() {
return {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
}
}
</script>
<template>
<BlogPost
v-for="post in posts"
:key="post.id"
:title="post.title"
/>
</template>
这里的 :title="post.title" 表示把父组件中的 post.title 动态传给子组件的 title 属性。
7. 子组件通知父组件:优先使用 emit
有些初学者会在子组件里通过 this.$parent 直接调用父组件方法。虽然这种方式能运行,但不推荐长期使用,因为它会让父子组件强耦合,后续维护成本较高。
更推荐的方式是:子组件通过 $emit 抛出事件,父组件监听事件并处理业务逻辑。
子组件:
vue
<template>
<button @click="handleClick">点击</button>
</template>
<script>
export default {
methods: {
handleClick() {
this.$emit('confirm')
}
}
}
</script>
父组件:
vue
<template>
<Child @confirm="fatherMethod" />
</template>
<script>
import Child from './Child.vue'
export default {
components: {
Child
},
methods: {
fatherMethod() {
console.log('测试')
}
}
}
</script>
这种写法更符合 Vue 的单向数据流:父组件通过 props 给子组件传值,子组件通过事件通知父组件。
8. 生命周期:在合适的阶段执行代码
生命周期钩子用于在组件不同阶段执行代码。以 mounted 为例,它会在组件完成初始渲染并创建 DOM 节点后执行。
vue
export default {
mounted() {
console.log('the component is now mounted.')
}
}
常见使用场景包括:
- 组件挂载后请求接口数据。
- 初始化依赖 DOM 的第三方库。
- 读取页面元素尺寸。
- 注册事件监听,并在组件卸载时清理。
如果需要查看完整生命周期,可以参考 Vue 官方文档:
https://cn.vuejs.org/api/options-lifecycle.html
9. 小结
本文梳理了 Vue3 进阶阶段最常见的几个知识点:
- 选项式 API 用于组织组件状态、方法、计算属性和生命周期。
- 单文件组件让结构、逻辑和样式拥有清晰边界。
createApp和mount是 Vue 应用启动的入口。v-model可以简化表单双向绑定。- 组件拆分能提升复用性和可维护性。
props用于父传子,emit用于子通知父。- 生命周期钩子适合处理组件不同阶段的副作用逻辑。
掌握这些内容后,再去学习组合式 API、Pinia、Vue Router、组件封装和工程化实践,会顺畅很多。Vue3 的学习重点不是记住每个 API,而是理解组件之间如何协作,以及状态如何驱动视图更新。