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 ,数据、方法、计算属性等,是分散在: datamethodscomputed 中的,若想新或者修改一个需求,就需要分别修改:datamethodscomputed ,不便于维护和复用。

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. 若需要一个响应式对象,层级不深,refreactive 都可以
  3. 若需要一个响应式对象,且层级较深,推荐使用reactive

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

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

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

作用:定义响应式变量。

语法:

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

返回值:一个 RefImpl 的实例对象,简称 ref对象refref对象的 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.vueList.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.vueAdd.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 文件的样式部分,主要用于美化待办列表的样式。

相关推荐
_Kayo_1 小时前
CSS BFC
前端·css
二哈喇子!3 小时前
Vue 组件化开发
前端·javascript·vue.js
chxii4 小时前
2.9 插槽
前端·javascript·vue.js
姑苏洛言5 小时前
扫码点餐小程序产品需求分析与功能梳理
前端·javascript·后端
Freedom风间5 小时前
前端必学-完美组件封装原则
前端·javascript·设计模式
江城开朗的豌豆5 小时前
React表单控制秘籍:受控组件这样玩就对了!
前端·javascript·react.js
一枚前端小能手5 小时前
📋 代码片段管理大师 - 5个让你的代码复用率翻倍的管理技巧
前端·javascript
国家不保护废物5 小时前
Web Worker 多线程魔法:告别卡顿,轻松实现图片压缩!😎
前端·javascript·面试
接着奏乐接着舞。5 小时前
如何在Vue中使用拓扑图功能
前端·javascript·vue.js