Vue3 组合式API

文章目录


前言

Vue 3 的组合式 API(Composition API)是一组函数和语法糖,用于更灵活和可组合地组织 Vue 组件的代码逻辑。与 Vue 2 的 Options API 不同,组合式 API 不再以选项的形式存在,而是通过 <script setup> 标签来使用。

组合式 API 提供了以下几个核心函数:

  • setup():组件创建之前调用的函数,用于定义组件的状态、计算属性、方法、事件等。setup() 函数可以返回一个对象,包含模板中需要用到的数据和方法。
  • ref():接收一个初始值并返回一个响应式对象,可以通过 .value 属性获取或修改值。
  • reactive():接收一个普通对象并返回一个响应式的对象,内部所有属性都是响应式的。
  • computed():接收一个计算函数作为参数,返回一个响应式的计算属性。
  • watch():监听数据变化并执行回调函数。
  • onMounted()、onUpdated()、onUnmounted():生命周期钩子函数,分别在组件挂载、更新和卸载时调用。

使用组合式 API 的好处包括:

  • 更好的代码组织性,将相关代码组织在同一个函数内,更方便阅读和维护;
  • 更容易重用代码,可以编写可复用的函数或复杂逻辑的组合函数;
  • 更好的类型推断和 IDE 支持。

组合式 API 可以与 Options API 共存,因此可以按需选择使用。

OptionsAPl 选项式(Vue2)与 CompositionAPl 组合式(Vue3)

Vue2 的 API 设计是 options(配置/选项)风格的。
Vue3 的API 设计是 Composition(组合)风格的。

动图来自:大帅老猿

Options 类型的 API ,数据、方法、计算属性等,是分散在: data、methods 、computed 中的,若想新或者修改一个需求,就需要分别修改:data、methods 、computed ,不便于维护和复用。

Vue3优势:可以用函数的方式,更加优雅的组织代码,让相关功能的代码更加有序的组织在一起,

了解组合式API

新建项目后在根组件中重新生成vue

然后删掉script标签内代码全部删掉

html 复制代码
<template>
  <div>
    {{ name }}
  </div>
</template>
<!-- 加上setup就是组合式API -->
<script setup>
let name = '张三'
</script>
<style scoped>
</style>

其中name 是一个响应式的数据属性,初始值为 '张三'

在模板中使用插值语法 {``{}} 来动态展示 name 属性的值

简单类型 ref 封装

上面的例子比较简单,下面看简单类型加上ref封装变成响应式数据

javascript 复制代码
<template>
  <div>
    {{ name }}
    <br>
    <button @click="change">换名字</button>
  </div>
</template>
<!-- 加上setup就是组合式API -->
<script setup>
let name = '张三'
function change(){
  name = '张三去打水了'
}
</script>
<style scoped>
</style>

运行后代码并没有改变

定义的数据默认不支持响应式(代码一变立刻同步到视图上,配置式API默认支持)

  1. 接下来需要导入东西
javascript 复制代码
import {ref} from 'vue'

组合式API想把数据变成响应式的,需要用ref对数据做一个封装

简单类型需要用ref进行封装,而对象类型不需要

  1. 调用.value属性
javascript 复制代码
<template>
  <div>
    {{ name }}
    <br>
    <button @click="change">换名字</button>
  </div>
</template>
<!-- 加上setup就是组合式API -->
<script setup>
import {ref} from 'vue'

let name = ref ('张三')
function change(){
  name.value = '张三去打水了'
}
</script>
<style scoped>
</style>

接下来点击按钮数据改变

对象类型 user.name

javascript 复制代码
<template>
  <div>
    {{ name }}--{{ user.name }}
    <br>
    <button @click="change">换名字</button>
  </div>
</template>
<!-- 加上setup就是组合式API -->
<script setup>
import {ref} from 'vue'

let name = ref ('张三')
// 对象类型
let user = {
  name: '李四'
}
function change(){
  name.value = '张三去打水了'
  user.name = '李四去挑水'
}
</script>
<style scoped>
</style>

在上面的代码中,user 对象的 name 属性值为 '李四',该属性与 name 响应式变量不同,二者无关。

在模板中,{``{ user.name }} 用于显示 user 对象的 name 属性值,而 {``{ name }} 则用于显示 name 响应式变量的值。

子组件

之前使用子组件是导入、注册、使用,在组合式API内是导入之后直接使用

导入:

javascript 复制代码
import HelloWorld from '@/components/HelloWorld.vue';

使用:

javascript 复制代码
    <HelloWorld></HelloWorld>

效果:

数组类型 reactive封装

javascript 复制代码
<template>
  <div>
    {{ name }}--{{ user.name }}
    <br>
    <p v-for="(item, index) in arr" :key="index">{{ item }}</p>
    <button @click="change">换名字</button>
    <HelloWorld></HelloWorld>
  </div>
</template>
<!-- 加上setup就是组合式API -->
<script setup>
import {ref} from 'vue'

import HelloWorld from '@/components/HelloWorld.vue';
let name = ref ('张三')
// 对象类型
let user = {
  name: '李四'
}
//数组类型
let arr = ['a','b','c']

function change(){
  name.value = '张三去打水了'
  user.name = '李四去挑水'
  arr.splice(0,1)
}
</script>
<style scoped>
</style>

运行后会把第一个数据删掉

但是再删就删不掉了

接下来也给数组封装一下reactive

javascript 复制代码
<template>
  <div>
    {{ name }}--{{ user.name }}
    <br>
    <p v-for="(item, index) in arr" :key="index">{{ item }}</p>
    <button @click="change">换名字</button>
    <HelloWorld></HelloWorld>
  </div>
</template>
<!-- 加上setup就是组合式API -->
<script setup>
import {ref,reactive} from 'vue'

import HelloWorld from '@/components/HelloWorld.vue';
let name = ref ('张三')
// 对象类型
let user = {
  name: '李四'
}
//数组类型
let arr = reactive(['a','b','c'])

function change(){
  name.value = '张三去打水了'
  user.name = '李四去挑水'
  arr.splice(0,1)
}
</script>
<style scoped>
</style>

ref 对比 reactive

宏观角度看:
ref 用来定义:基本类型数据、对象类型数据
reactive 用来定义:对象类型数据

区别:
ref 创建的变量必须使用 .value(可以使用 volar 插件自动添加.value)
reactive 重新分配一个新对象,会失去响应式(可以使用 0bject.assign 去整体替换)

使用原则:

  1. 若需要一个基本类型的响应式数据,必须使用 ref
  2. 若需要一个响应式对象,层级不深,ref、reactive 都可以
  3. 若需要一个响应式对象,且层级较深,推荐使用reactive

.ref创建→可以定义基本类型、对象类型的响应式数据

定义基本类型的响应式数据

想让哪个数据是响应式的,就把那个数据用ref包一下

作用:定义响应式变量。

语法:

javascript 复制代码
let xxx = ref(初始值)

返回值:一个 RefImpl 的实例对象,简称 ref对象 或 ref , ref对象的 value属性是响应式的。

注意点:

JS中操作数据需要:xxx.value ,但模板中不需要.value,直接使用即可。

对于 let name = ref('张三')来说,name 不是响应式的,name.value是响应式的

  1. 引入ref
javascript 复制代码
import {ref} from vue
  1. 给需要响应式的数据用ref包一下
javascript 复制代码
let name = ref('张三')
let age = ref(18)

对象类型的响应式数据

ref通过将值包装成对象,并利用reactive提供的响应式转换,使得任何类型的值(包括对象类型)都能成为响应式数据。这就是所谓的"ref的底层还是reactive"

reactive→只能定义:对象类型的响应式数据



组合式 API 基础练习

新建两个子组件:Add、List,生成基本结构后删除script标签内默认生成的配置项

根组件中:

使用了 <List> 组件,该组件的代码未展示,可以理解为列表组件;

引入了 reactive 函数,创建了一个响应式的数组 arr,用于存储待办列表中的数据

javascript 复制代码
<template>
  <div>
    <h1>待办列表</h1>
    <List></List>
  </div>
</template>

<script setup>
import List from '@/components/List.vue'
import { reactive } from 'vue';

// 定义数组
let arr = reactive(['看电影','玩游戏'])
</script>
<style scoped>
</style>

List子组件中

在列表组件 <script setup> 中,使用了 defineProps 函数来定义组件的属性,可以用于定义组件的 props。在这里定义了一个 list 属性,类型为数组类型 Array。

在模板中,使用了 v-for 循环遍历 list 数组中的数据,然后使用 {``{ item }} 渲染每一项的数据。这里加上了 :key="index",以便 Vue 可以快速识别每个列表项,并渲染正确的数据。

javascript 复制代码
<template>
  <div>
    <ol>
        <li v-for="(item, index) in list" :key="index">{{ item }}</li>
    </ol>
  </div>
</template>

<script setup>
// 组合式写法,自定义属性
let myProps = defineProps({
    list :{type: Array}
})
</script>
<style scoped>
</style>

接下来在父组件中传递一个属性数据

javascript 复制代码
    <List :list="arr"></List>

接下来在父组件中传入子组件App

  1. 导入
javascript 复制代码
import Add  from '@/components/Add.vue';
  1. 使用
javascript 复制代码
    <Add @item="recive"></Add>
  1. 定义方法
javascript 复制代码
function recive(v) {
  arr.push(v)
}

接下来就剩子组件Add了

javascript 复制代码
<template>
  <div>
    <input type="text" v-model="item">
    <button @click="send">添加</button>
  </div>
</template>

<script setup>
// 定义事件  数据名item
import {ref} from 'vue'
let item = ref('')

// 定义事件  send
// 组合式API自定义事件
let emits = defineEmits(['item'])
function send(){
    if ( item.value!='' ) {
        // 不为空 发送数据
        emits('item',item.value)
        // 清空输入框,避免重复添加
        item.value = ''
    }

}
</script>
<style scoped>
/* 输入框样式 */
input {
    padding: 8px 16px;
    font-size: 16px;
    outline:none;
    border: 1px solid black;
    border-radius: 4px;
    margin-right: 8px;
  }

  /* 按钮样式 */
  button {
    padding: 8px 16px;
    font-size: 16px;
    background-color: #409EFF;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
</style>

基础练习优化

以下是对代码的注释和步骤的解释:

html 复制代码
<template>
  <div>
    <h1>待办列表</h1>
    <Add @item="recive"></Add>
    <List @remove="del" :list="arr"></List>
  </div>
</template>

上面的代码是 App.vue 文件的模板部分,主要是引入 Add.vue 和 List.vue 组件,并绑定对应的自定义事件。

javascript 复制代码
<script setup>
import List from '@/components/List.vue'
import Add from '@/components/Add.vue'
import { reactive } from 'vue'

// 定义数组
let arr = reactive(['玩游戏','看电影'])

function recive(v){
  arr.push(v)
}
function del(i){
  arr.splice(i,1)
}
</script>

上面的代码是 App.vue 文件的逻辑部分。引入 List.vue 和 Add.vue 组件,并用 reactive 定义了一个响应式数组 arr,用于存储待办列表。

recive 函数用于接收从 Add.vue 组件传递过来的数据,并将其添加到 arr 数组中。

del 函数用于接收从 List.vue 组件传递过来的数据,并将对应的列表项从 arr 数组中删除。

html 复制代码
<template>
    <div>
      <input type="text" v-model="item">
      <button @click="send">添加</button>
    </div>
  </template>

上面的代码是 Add.vue 文件的模板部分,主要包含一个输入框和一个按钮,用于向父组件派发数据。

javascript 复制代码
<script setup>
// 定义事件  数据名item
import {ref} from 'vue'
let item = ref('')

// 定义事件  send
// 组合式API自定义事件
let emits = defineEmits(['item'])
function send(){
  if ( item.value!='' ) {
        // 不为空 发送数据
        emits('item',item.value)
        // 清空输入框,避免重复添加
        item.value=''
  }
}
</script>

上面的代码是 Add.vue 文件的逻辑部分,用 defineEmits 定义了一个自定义事件 item,用 ref 定义了一个响应式变量 item,用于获取输入框内的值。当点击添加按钮时,根据输入框内的值,触发自定义事件并将输入框内的值作为参数传递给父组件,最后清空输入框。

html 复制代码
<template>
    <div>
      <ol>
          <li @click="send(index)" v-for="(item, index) in list" :key="index">{{ item }}</li>
      </ol>
    </div>
  </template>

上面的代码是 List.vue 文件的模板部分,主要用于展示待办列表。

javascript 复制代码
<script setup>
  //组合式写法 自定义属性
  let myProps = defineProps({
      list:{type: Array}
  })
  let emits = defineEmits(['remove'])
  function send(i){
   emits('remove',i)
  }
  </script>

上面的代码是 List.vue 文件的逻辑部分,用 defineProps 定义了一个 list 属性,用于接收父组件传递过来的数据。用 defineEmits 定义了一个自定义事件 remove,当每个列表项被点击时触发该事件,并向父组件派发一个消息。

css 复制代码
<style scoped>
  li{
      color: #223;
      margin: 10px 0;
      font-size: 25px;
  }
  li:hover{
      cursor: pointer;
      color: #31c27c;
  }
  
  li:hover::after{
      content: "-点击已办";
      color: red;
  }
</style>

上面的代码是 List.vue 文件的样式部分,主要用于美化待办列表的样式。

相关推荐
无限聚核13 分钟前
抖音 certificate.prepare 之后怎么验券?和美团 msconsume 差在哪,无限聚核
前端·微信小程序
2603_9658966222 分钟前
Grid 网格布局|CSS 二维页面排版体系
前端·css
有味道的男人38 分钟前
搜好货平台关键词商品搜索接口:分页容错与商品数据轻量化提取方案
服务器·前端·javascript
lovingsoft1 小时前
Chrome Lighthouse 详解
前端·chrome
Qlly1 小时前
全栈工程师怎么接需求?从一句「加个功能」聊到能开发、能验收
前端·后端
马可家的菠萝2 小时前
你的微信“文件传输助手”,是不是已经成了第二个网盘?
前端·后端·架构
略略略啦2 小时前
Vibe Coding 基于Spec DD和TDD的实践指南
前端
shmily麻瓜小菜鸡2 小时前
前端实现图片与PDF文字识别敏感信息打码方案
前端·pdf·状态模式
打工仔折腾 AI2 小时前
LangChain 工具调用实战:从 Tool 定义到 Agent 执行链路拆解
java·前端·人工智能·后端·python·langchain·ai agent 实战
汉堡大王95272 小时前
单次输出拉到100万Token:拆完 Gemini 4 Argon 的续写协议,才看懂谷歌这7个月在憋什么
前端·人工智能·后端