Vue中选项式和组合式API的学习
- 组合式 API (Composition API)
- 选项式 API (Options API)
- export default 的作用
- data
- created
- methods
- 两种风格对比
- 在其他 Vue 组件中导入和使用 `export default` 定义的组件 有几种方式
- 基本导入和使用
- 调用子组件的方法
- 完整的父子组件通信示例
- 关键点总结:
Vue 的组件可以按两种不同的风格书写 :组合式 API和选项式 API 。
组合式 API (Composition API)
通过组合式 API,可以使用导入的 API 函数来描述组件逻辑。在单文件组件中,组合式 API 通常会与
<script setup>import{ref,onMounted}from'vue'// 响应式状态constcount=ref(0)// 用来修改状态、触发更新的函数functionincrement(){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上,它会指向当前的组件实例。
<script>exportdefault{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 组件配置对象
exportdefault{// 组件配置选项}当在另一个文件中使用import时,导入的就是这个默认导出的对象。
data
- 作用:定义组件的响应式数据
- 必须是函数:返回一个包含数据的对象
- 为什么是函数:确保每个组件实例都有独立的数据副本,避免数据共享问题
data(){return{newUser:{name:'',email:''},// 创建新用户时的表单数据users:[],// 存储从API获取的用户列表editingUser:null// 当前正在编辑的用户};}created
- 生命周期钩子:在组件实例创建完成后立即调用
- 执行时机:数据观测已建立,但DOM还未挂载
- 常见用途:初始化数据、调用API
created(){this.fetchUsers();// 组件创建时自动获取用户列表}methods
- 作用:定义组件的方法
- 特点:这些方法可以直接在模板中调用
methods:{// 异步获取用户列表asyncfetchUsers(){try{constresponse=awaitaxios.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定义的组件 有几种方式
基本导入和使用
导入组件
<template> <div> <!-- 使用导入的组件 --> <ArticleList /> </div> </template> // 选项式 <script> // 导入组件 import ArticleList from '@/components/ArticleList.vue' export default { name: 'ParentComponent', components: { // 注册导入的组件 ArticleList } } </script>调用子组件的方法
方法一:使用 ref 引用
<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 传递回调函数
父组件:
<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):
<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>完整的父子组件通信示例
父组件
<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)
<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 能够自动处理数据与视图的同步,大大简化了前端开发的复杂度。
愿你我都能在各自的领域里不断成长,勇敢追求梦想,同时也保持对世界的好奇与善意!