Vue中选项式和组合式API的学习
-
- [组合式 API (Composition API)](#组合式 API (Composition API))
- [选项式 API (Options API)](#选项式 API (Options API))
- 两种风格对比
- [在其他 Vue 组件中导入和使用 `export default` 定义的组件 有几种方式](#在其他 Vue 组件中导入和使用
export default定义的组件 有几种方式)
Vue 的组件可以按两种不同的风格书写 :组合式 API和选项式 API 。
组合式 API (Composition API)
通过组合式 API,可以使用导入的 API 函数来描述组件逻辑。在单文件组件中,组合式 API 通常会与
javascript
<script setup>
import { ref, onMounted } from 'vue'
// 响应式状态
const count = ref(0)
// 用来修改状态、触发更新的函数
function increment() {
count.value++
}
// 生命周期钩子
onMounted(() => {
console.log(`The initial count is ${count.value}.`)
})
</script>
<template>
<button @click="increment">Count is: {{ count }}</button>
</template>
选项式 API (Options API)
使用选项式 API,可以用包含多个选项的对象来描述组件的逻辑,例如 data、methods 和 mounted。选项所定义的属性都会暴露在函数内部的 this 上,它会指向当前的组件实例。
javascript
<script>
export default {
name: '', //`name` 属性标识组件名称
components: { //使用 `components` 选项注册子组件
}
// data() 返回的属性将会成为响应式的状态
// 并且暴露在 `this` 上
data() {
return {
// 所有响应式数据在这里定义
count: 0
};
},
// methods 是一些用来更改状态与触发更新的函数
// 它们可以在模板中作为事件处理器绑定
methods: {
// 所有组件方法在这里定义
},
// 生命周期钩子会在组件生命周期的各个不同阶段被调用
created() { /* 实例创建后 */ },
mounted() { /* DOM挂载后 */ },
updated() { /* 数据更新后 */ },
destroyed() { /* 实例销毁前 */ },
// 组件注册
components: {
// 子组件注册
}
}
<script>
export default 的作用
export default 是 ES6 模块系统的语法,用于导出模块的默认内容。在 Vue 中,它用于导出一个 Vue 组件配置对象
javascript
export default {
// 组件配置选项
}
当在另一个文件中使用 import 时,导入的就是这个默认导出的对象。
data
- 作用:定义组件的响应式数据
- 必须是函数:返回一个包含数据的对象
- 为什么是函数:确保每个组件实例都有独立的数据副本,避免数据共享问题
javascript
data() {
return {
newUser: { name: '', email: '' }, // 创建新用户时的表单数据
users: [], // 存储从API获取的用户列表
editingUser: null // 当前正在编辑的用户
};
}
created
- 生命周期钩子:在组件实例创建完成后立即调用
- 执行时机:数据观测已建立,但DOM还未挂载
- 常见用途:初始化数据、调用API
javascript
created() {
this.fetchUsers(); // 组件创建时自动获取用户列表
}
methods
- 作用:定义组件的方法
- 特点:这些方法可以直接在模板中调用
javascript
methods: {
// 异步获取用户列表
async fetchUsers() {
try {
const response = await axios.get('https://api.example.com/users');
this.users = response.data; // 更新响应式数据
} catch (error) {
console.error('获取用户列表失败:', error);
}
}
}
两种风格对比
组合式 API 的核心思想是直接在函数作用域内定义响应式状态变量,并将从多个函数中得到的状态组合起来处理复杂问题。这种形式更加自由,也需要对 Vue 的响应式系统有更深的理解才能高效使用。相应的,它的灵活性也使得组织和重用逻辑的模式变得更加强大。
选项式 API 是在组合式 API 的基础上实现的。选项式 API 以"组件实例"的概念为中心 (即上述例子中的 this),基于面向对象概念实现。同时,它将响应性相关的细节抽象出来,并强制按照选项来组织代码。
以下是官方给出的建议:
- 在学习的过程中,推荐采用更易于理解的风格。大部分的核心概念在这两种风格之间都是通用的。熟悉了一种风格以后,也能够很快地理解另一种风格。
- 在生产项目中:
- 当不需要使用构建工具,或者打算主要在低复杂度的场景中使用 Vue,例如渐进增强的应用场景,推荐采用选项式 API。
- 当你打算用 Vue 构建完整的单页应用,推荐采用组合式 API + 单文件组件。
在其他 Vue 组件中导入和使用 export default 定义的组件 有几种方式
基本导入和使用
导入组件
vue
<template>
<div>
<!-- 使用导入的组件 -->
<ArticleList />
</div>
</template>
// 选项式
<script>
// 导入组件
import ArticleList from '@/components/ArticleList.vue'
export default {
name: 'ParentComponent',
components: {
// 注册导入的组件
ArticleList
}
}
</script>
调用子组件的方法
方法一:使用 ref 引用
vue
<template>
<div>
<button @click="refreshArticles">刷新文章</button>
<button @click="createNewArticle">创建文章</button>
<!-- 给组件添加 ref 属性 -->
<ArticleList ref="articleListRef" />
</div>
</template>
<script>
import ArticleList from '@/components/ArticleList.vue'
export default {
name: 'ParentComponent',
components: {
ArticleList
},
methods: {
refreshArticles() {
// 调用子组件的 fetchArticles 方法
this.$refs.articleListRef.fetchArticles()
},
createNewArticle() {
// 调用子组件的 handleCreateArticle 方法
this.$refs.articleListRef.handleCreateArticle()
}
}
}
</script>
方法二:通过 props 传递回调函数
父组件:
vue
<template>
<div>
<ArticleList
:onRefresh="handleRefresh"
:onCreate="handleCreate"
/>
</div>
</template>
<script>
import ArticleList from '@/components/ArticleList.vue'
export default {
components: {
ArticleList
},
methods: {
handleRefresh() {
console.log('父组件处理刷新逻辑')
},
handleCreate(articleData) {
console.log('父组件处理创建逻辑', articleData)
}
}
}
</script>
子组件 (ArticleList.vue):
vue
<script>
export default {
name: 'ArticleList',
props: {
onRefresh: Function,
onCreate: Function
},
methods: {
fetchArticles() {
// 原有的获取文章逻辑
fetch('http://localhost:8000/posts')
.then(r => r.json())
.then(data => {
this.articles = data
// 如果有传递回调函数,则调用
if (this.onRefresh) {
this.onRefresh(data)
}
})
},
handleCreateArticle() {
// 原有的创建逻辑...
// 如果有传递回调函数,则调用
if (this.onCreate) {
this.onCreate(this.newArticle)
}
}
}
}
</script>
完整的父子组件通信示例
父组件
vue
<template>
<div class="parent">
<h1>博客管理</h1>
<div class="controls">
<button @click="triggerRefresh" :disabled="isLoading">
{{ isLoading ? '加载中...' : '刷新文章' }}
</button>
<button @click="showCreateForm = true">新建文章</button>
</div>
<!-- 使用子组件 -->
<ArticleList
ref="articleList"
:externalCreateData="createFormData"
@articles-loaded="onArticlesLoaded"
@article-created="onArticleCreated"
/>
<!-- 父组件的创建表单 -->
<div v-if="showCreateForm" class="modal">
<h3>创建新文章</h3>
<input v-model="createFormData.title" placeholder="标题">
<textarea v-model="createFormData.content" placeholder="内容"></textarea>
<button @click="submitCreateForm">提交</button>
<button @click="cancelCreate">取消</button>
</div>
</div>
</template>
<script>
import ArticleList from '@/components/ArticleList.vue'
export default {
name: 'BlogManagement',
components: {
ArticleList
},
data() {
return {
isLoading: false,
showCreateForm: false,
createFormData: {
title: '',
content: ''
}
}
},
methods: {
// 触发子组件的刷新方法
triggerRefresh() {
this.isLoading = true
this.$refs.articleList.fetchArticles()
},
// 触发子组件的创建方法
submitCreateForm() {
this.$refs.articleList.handleCreateArticle(this.createFormData)
this.showCreateForm = false
},
cancelCreate() {
this.showCreateForm = false
this.createFormData = { title: '', content: '' }
},
// 监听子组件的事件
onArticlesLoaded(articles) {
this.isLoading = false
console.log('文章加载完成:', articles)
},
onArticleCreated(newArticle) {
console.log('新文章创建:', newArticle)
this.createFormData = { title: '', content: '' }
}
}
}
</script>
子组件 (ArticleList.vue)
vue
<template>
<div class="article-list">
<div v-if="articles.length === 0" class="empty">暂无文章</div>
<ArticleCard
v-for="article in articles"
:key="article.id"
:article="article"
/>
</div>
</template>
<script>
import ArticleCard from './ArticleCard.vue'
export default {
name: 'ArticleList',
components: {
ArticleCard
},
props: {
// 从父组件接收创建数据
externalCreateData: {
type: Object,
default: () => ({ title: '', content: '' })
}
},
data() {
return {
articles: [],
newArticle: {
title: '',
content: '',
author: '左越'
}
}
},
watch: {
// 监听外部创建数据的变化
externalCreateData: {
handler(newData) {
if (newData.title || newData.content) {
this.newArticle = { ...newData, author: '左越' }
this.handleCreateArticle()
}
},
deep: true
}
},
created() {
this.fetchArticles()
},
methods: {
async fetchArticles() {
try {
const response = await fetch('http://localhost:8000/posts')
const data = await response.json()
this.articles = data
// 触发事件通知父组件
this.$emit('articles-loaded', data)
} catch (err) {
console.log('error', err)
this.$emit('articles-loaded', [])
}
},
async handleCreateArticle(externalData = null) {
const articleData = externalData || this.newArticle
if (!articleData.title.trim() || !articleData.content.trim()) {
alert('请填写所有字段')
return
}
try {
const response = await fetch('http://localhost:8000/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(articleData)
})
const newArticle = await response.json()
this.articles.unshift(newArticle)
// 触发事件通知父组件
this.$emit('article-created', newArticle)
// 重置表单
this.resetForm()
} catch (err) {
console.error('error: ', err)
}
},
resetForm() {
this.newArticle = {
title: '',
content: '',
author: '左越'
}
}
}
}
</script>
关键点总结:
- 导入组件 :使用
import ComponentName from 'path/to/component' - 注册组件 :在
components选项中注册 - 使用 ref :通过
this.$refs.refName.methodName()调用子组件方法 - 事件通信 :子组件使用
this.$emit('event-name', data),父组件使用@event-name="handler" - Props 传递:父组件通过 props 向子组件传递数据和方法
选择哪种方式取决于具体需求:
- 简单的调用使用
ref - 复杂的通信使用
props和events - 状态管理考虑使用 Vuex(对于大型应用)
这种结构让 Vue 能够自动处理数据与视图的同步,大大简化了前端开发的复杂度。
愿你我都能在各自的领域里不断成长,勇敢追求梦想,同时也保持对世界的好奇与善意!