当前位置: 首页 > news >正文

Vue ⑧-Vue3 | 组合式API

在这里插入图片描述

Vue3 预体验

需求:点击按钮,让数字 + 1

vue2 的 选项式 API:

<script>
export default {data() {return {count: 0}},methods: {increment() {this.count++}}
}
</script>

vue3 的 组合式 API:

<script>
import { ref } from 'vue'
const count = ref(0)
const increment = () => count.value++
</script>
  • 代码量变少了
  • 分散式维护转为集中式维护,更易封装复用

create-vue

create-vue 是 Vue 官方新的脚手架工具,底层切换到了 vite(下一代构建工具),为开发提供极速响应。

  • 前提环境条件:已安装 16.0 或更高版本的 Node.js

  • 创建一个Vue应用:npm init vue@latest,这一指令将会安装并执行 create-vue
    在这里插入图片描述

关键文件:

  1. vite.config.js - 项目的配置文件 基于 vite 的配置
  2. package.json - 项目包文件 核心依赖项变成了 Vue3.x 和 vite
  3. main.js - 入口文件 createApp 函数创建应用实例
  4. app.vue - 根组件 SFC 单文件组件 script - template - style
    变化一:脚本 script 和模板 template 顺序调整
    变化二:模板 template 不再要求唯一根元素
    变化三:脚本 script 添加 setup 标识支持组合式API
  5. index.html - 单页入口 提供 id 为 app 的挂载点

组合式 API

setup 选项的写法和执行时机

<script>
export default {setup() {},beforeCreate() {},
}
</script>

在这里插入图片描述

setup 选项中写代码的特点

<script>
export default {setup() {// 数据const message = 'Hello Vue 3'// 函数const logMessage = () => {console.log(message)}return {message,logMessage}},beforeCreate() {console.log('beforeCreate 函数')}
}
</script><template><div>message: {{ message }}</div><button @click="logMessage">Log Message</button>
</template>

<script setup> 语法糖

<script> 标签中,使用 <script setup> 语法糖,可以省略掉 export defaultreturn 关键字,直接在 script 标签中书写代码。

<script setup>
// 数据
const message = 'Hello Vue 3'
// 函数
const logMessage = () => {console.log(message)
}
</script><template><div>message: {{ message }}</div><button @click="logMessage">Log Message</button>
</template>

reactive()

作用:接受对象类型数据的参数传入并返回一个响应式的对象

<script setup>
// 1. reactive:接受一个对象类型的数据,返回一个响应式的对象,当数据发生变化时,视图会自动更新
import { reactive } from 'vue'
const state = reactive({count: 0
})
const setCount = () => {state.count++
}
</script>

ref()

作用:接收简单类型或者对象类型的数据传入并返回一个响应式的对象

<script setup>
// 1. ref:接受简单类型或者复杂类型,返回一个响应式对象
// 本质:是在原有传入数据的基础上,外层包了一层对象
// 底层:包成复杂类型之后,再借助 reactive 实现的响应式
// 注意:
// 1. ref 不能直接访问数据,需要通过 .value
// 2. 在 template 中 .value 不需要加(帮我们扒了一层)// 推荐:以后声明数据,统一用 ref => 统一编码规范
import { ref } from 'vue'
const count = ref(0)const setCount = () => {count.value++
}
</script>

computed计算属性函数

计算属性基本思想和 Vue2 的完全一致,组合式 API 下的计算属性只是修改了写法

核心步骤:

  • 导入 computed 函数
  • 执行函数 在回调参数中 return 基于响应式数据做计算的值,用变量接收
<script setup>
// const 计算属性 => computed(() => {
//   return 计算返回后的结果
//})import { ref, computed } from 'vue'
// 声明数据
const list = ref([1, 2, 3, 4, 5, 6, 7, 8])const computedList = computed(() => {return list.value.filter(item => item % 2 === 0)
})const addFn = () => {list.value.push(Math.ceil(Math.random() * 100))
}
</script><template><div><div>原始数据:{{ list }}</div><div>计算后的数据:{{ computedList }}</div><button @click="addFn">修改</button></div>
</template>

watch函数

作用: 侦听一个或者多个数据的变化,数据变化时执行回调函数

俩个额外参数:

  1. immediate(立即执行)
  2. deep(深度侦听)

侦听单个数据

  1. 导入 watch 函数

  2. 执行 watch 函数传入要侦听的响应式数据 (ref对象) 和回调函数

<script setup>
import { ref, watch } from 'vue'
const count = ref(0)const changeCount = () => {count.value++
}// 1. 监视单个数据的变化
watch(count, (newValue, oldValue) => {console.log('count', newValue, oldValue)
})</script><template><div>{{ count }}</div><button @click="changeCount">+1</button>
</template>

侦听多个数据

同时侦听多个响应式数据的变化,不管哪个数据变化都需要执行回调

<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const nickname = ref('张三')const changeCount = () => {count.value++
}
const changeNickname = () => {nickname.value = '李四'
}// 2. 监视多个数据的变化
watch([count, nickname], (newArr, oldArr) => {console.log('watch', newArr, oldArr)
})</script><template><div>{{ count }}</div><button @click="changeCount">+1</button><div>{{ nickname }}</div><button @click="changeNickname">修改昵称</button>
</template>

immediate

在侦听器创建时立即触发回调,响应式数据变化之后继续执行回调

<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const nickname = ref('张三')const changeCount = () => {count.value++
}
const changeNickname = () => {nickname.value = '李四'
}// 3. immediate 立即执行
watch([count, nickname],(newArr, oldArr) => {console.log('watch', newArr, oldArr)},{immediate: true}
)</script><template><div>{{ count }}</div><button @click="changeCount">+1</button><div>{{ nickname }}</div><button @click="changeNickname">修改昵称</button>
</template>

deep

默认机制:通过 watch 监听的 ref 对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启 deep 选项

const state = ref({count: 0})
watch(state, () => console.log('数据变化了'))const changeStateByCount = () => {// 直接修改属性 => 不会触发回调state.value.count++
}
<script setup>
import { ref, watch } from 'vue'// 4. deep 深度监视,默认 watch 进行的是 浅层监视
//    const ref1 = ref(简单类型) 可以直接监视
//    const ref2 = ref(复杂类型) 监视不到复杂类型内部数据的变化
const userInfo = ref({name: 'zs',age: 18
})const setUserInfo = () => {// 此时修改了 userInfo.value 对象的地址,默认的 watch 才能监视到// userInfo.value = { name: 'ls', age: 19}userInfo.value.age++userInfo.value.name = 'ls'
}watch(userInfo,(newValue) => {console.log('userInfo', newValue)},{deep: true}
)</script><template><div>{{ userInfo }}</div><button @click="setUserInfo">修改userInfo</button>
</template>

精确侦听对象的某个属性

需求:在不开启deep的前提下,侦听 age 的变化,只有 age 变化时才执行回调

<script setup>
import { ref, watch } from 'vue'const userInfo = ref({name: 'zs',age: 18
})const setUserInfo = () => {userInfo.value.age++userInfo.value.name = 'ls'
}// 5. 对于对象中的属性,进行监视
watch(() => userInfo.value.age,(newValue, oldValue) => {console.log('userInfo.age', newValue, oldValue)}
)</script><template><div>{{ userInfo }}</div><button @click="setUserInfo">修改userInfo</button>
</template>

Vue3 的生命周期 API

选项式API组合式API
beforeCreate/createdsetup
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
<script setup>
import { onMounted } from 'vue'// beforeCreate 和 created 的相关代码
// 一律放在 setup 中执行const getList = () => {setTimeout(() => {console.log('getList')}, 2000)
}
// 一进入页面的请求
getList()// 如果有些代码需要在 mounted 生命周期中执行
onMounted(() => {console.log('mounted生命周期函数')
})
// 写成函数的调用方式,可以调用多次,并不会冲突,而是按照顺序依此执行
onMounted(() => {console.log('mounted生命周期函数2')
})
</script>

组合式API - 父子通信

组合式API下的父传子

基本思想

  1. 父组件中给子组件绑定属性
  2. 子组件内部通过 props 选项接收

父组件

<script setup>
// 引入子组件
import sonComVue from './son-com.vue'<template>{/* 绑定属性 message */}<sonComVue message="this is app message">
</template>

子组件

<script setup>
// 通过 defineProps "编译器宏" 接受子组件传递的数据
const props = defineProps({message: String
})
</script><template>{{ message }}
</template>

组合式API下的子传父

基本思想

  1. 父组件中给子组件标签通过@绑定事件
  2. 子组件内部通过 emit 方法触发事件

父组件

<script setup>
// 引入子组件
import sonComVue from './son-com.vue'const getMessage = (message) => {console.log(message)
}
</script><template>{/* 绑定事件 changeMessage */}<sonComVue @get-message="getMessage">
</template>

子组件

<script setup>
// 通过 defineEmits 编译器宏生成 emit 方法
const emit = defineEmits(['get-message'])const sendMessage = () => {// 触发自定义事件,并传递参数emit('get-message', 'this is son message')
}
</script><template><button @click="sendMessage">发送消息</button>
</template>

模板引用

通过ref标识获取真实的dom对象或者组件实例对象

<script setup>
import { ref } from 'vue'// 模板引用(可以获取到 dom,也可以获取组件)
// 1. 调用 ref函数,生成一个 ref对象
// 2. 通过 ref标识,进行绑定
// 3. 通过 ref对象.value 即可访问到绑定的元素(必须等到元素渲染完才可以获取到)
const inp = ref(null)
</script><template><div><!-- 通过 ref标识,绑定元素 --><input ref="inp" type="text"></div>
</template>

defineExpose()

默认情况下在 <script setup> 语法糖下组件内部的属性和方法是不开放给父组件访问的。

可以通过 defineExpose 编译宏指定哪些属性和方法允许访问

<script setup>
const cnt = 99
const sayHi = () => {alert('Hi')
}defineExpose({sayHi,cnt
})
</script>

provide & inject

顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信

  1. 顶层组件通过 provide 函数提供数据

  2. 底层组件通过 inject 函数获取数据

跨层传递普通数据

顶层组件

provide('key', 'this is top message')

底层组件

const message = inject('key')

跨层传递响应式数据

顶层组件

const count = ref(100)
provide('count-key', count)

底层组件

const count = inject('count-key')

跨层传递方法

顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据

顶层组件

const sayHi = () => {alert('Hi')
}
provide('sayHi-Key', sayHi)

底层组件

const sayHi = inject('sayHi-Key')
sayHi()

defineOptions

背景说明:

  • <script setup> 之前,如果要定义 props, emits 可以轻而易举地添加一个与 setup 平级的属性。
setup() {},
props: {},
emits: {}
  • 但是用了 <script setup> 后,就没法这么干了 setup 属性已经没有了,自然无法添加与其平级的属性。

为了解决这一问题,引入了 defineProps 与 defineEmits 这两个宏。但这只解决了 props 与 emits 这两个属性。

如果我们要定义组件的 name 或其他自定义的属性,还是得回到最原始的用法——再添加一个普通的 <script> 标签。

这样就会存在两个 <script> 标签。让人无法接受。

<script>
export default {name: "componentName"
}
</script><script setup>
// 这里是不能直接定义 name 的,所以需要两个 <script> 标签
<script>

使用 defineOptions 解决这个问题

<script setup>
// 用于定义组件的选项。它允许你在组件内部以更简洁的方式设置组件的配置项
defineOptions({name: 'componentName',props: {message: String},emits: ['custom-event']
})
</script><template><div>{{ message }}</div>
</template>

defineModel

在Vue3中,自定义组件上使用 v-model,相当于传递一个 modelValue属性,同时触发 update:modelValue 事件

<Child v-model="isVisible">
<!-- 相当于 -->
<Child :modelValue="isVisible" @update:modelValue="isVisible = $event">

而在 Child 组件中,我们需要先定义 props,再定义 emits 。其中有许多重复的代码。如果需要修改此值,还需要手动调用 emit 函数。

父组件

<script setup>
import MyInput from './my-input.vue'
import { ref } from 'vue'
const txt = ref('123456')
</script><template>
<div><MyInput v-model="txt"></MyInput>{{ txt }}
</div>
</template>

子组件

<script setup>
import { defineProps, defineEmits } from 'vue'const props = defineProps({modelValue: String
})
const emits = defineEmits(['update:modelValue'])</script><template><inputtype="text":value="modelValue"@input="e => emit('update:modelValue', e.target.value)">
</template>

使用 defineModel 简化

<script setup>
import { defineModel } from 'vue'
const modelValue = defineModel()
</script><template>
<div><inputtype="text":value="modelValue"@input="e => modelValue = e.target.value">
</div>
</template>

plate>

{{ txt }}
```

子组件

<script setup>
import { defineProps, defineEmits } from 'vue'const props = defineProps({modelValue: String
})
const emits = defineEmits(['update:modelValue'])</script><template><inputtype="text":value="modelValue"@input="e => emit('update:modelValue', e.target.value)">
</template>

使用 defineModel 简化

<script setup>
import { defineModel } from 'vue'
const modelValue = defineModel()
</script><template>
<div><inputtype="text":value="modelValue"@input="e => modelValue = e.target.value">
</div>
</template>

相关文章:

  • 产业园如何精准招商?
  • 【Pandas】pandas DataFrame notna
  • DAY 46 超大力王爱学Python
  • RFID测温芯片在新能源电池管理中的创新应用
  • 电路图识图基础知识-行程开关自动往返运行控制电路详解(二十三)
  • 人工智能新纪元:技术融合驱动产业深变的五大路径
  • 水利水电安全员考试真题中,有哪些易错易混淆的知识点需要重点关注?
  • window 显示驱动开发-创建视频处理设备
  • 【Java】【力扣】121.买卖股票的最佳时机
  • 【leetcode】125.验证回文串
  • Spring Boot 3 集成 MyBatis 连接 MySQL 数据库
  • Unity 服务器交互开发指南
  • 【第一章:人工智能基础】02.数据处理及可视化-(3)可视化工具与技术
  • java实现RabbitMQ消息发送和接收功能(包含测试)
  • 代码随想录训练营二十六天| 654.最大二叉树 617.合并二叉树 700.二叉搜索树的搜索 98.验证二叉搜索树
  • ttyd:安全地通过网络共享您的 Linux 终端
  • 上传一个新菜谱-第一部分
  • 深入解析Docker网桥模式:从docker0到容器网络的完整通信链路
  • 人机交互设计知识点总结
  • 控制器轨迹生成
  • 手机网站开发学习/聚名网域名注册
  • 合同无效的12种情形/seo建站教程
  • 企业网站策划怎么样/google搜索网址
  • 南京网站关键词/谷歌优化推广
  • 网站开发规划书/学生个人网页制作
  • 旅游网模板html代码/seo网站排名优化价格