Vue3学习记录(一)--- 组合式API之基础概念和变量声明

一、组合式API基础

1、简介

​ 组合式 API (Composition API) 是Vue3和Vue2的v2.7之后版本中的全新特性,是一系列API的的集合(响应式API、生命周期钩子、依赖注入等等),其风格是基于函数的组合,以一种更直观、更灵活的方式来书写Vue单文件组件。但是组合式 API并不属于函数式编程,因为函数式编程主要强调的是数据不可变,而以 Vue 中数据可变的、细粒度的响应性系统为基础的。

​ 接下来我们讲述的相关知识点,都是基于Vue3的组合式API来进行的。

2、优势
更好的复用性

​ 组合式API最大的优势在于可以通过组合函数的方式,将组件进行任意颗粒度的拆分和组合,这样就大大提高了代码的可维护性和复用性。

更灵活的代码组织性

​ 同时,组合式API可以将处理某一逻辑的所有相关代码,放置在相邻区域内,解决了选项式API中将处理统一逻辑的代码分散在不同的选项中,在查看时需要上下来回进行滚动切换的弊端,方便后期将相关逻辑抽出,封装为单独组件。官方示例图片如下,相同颜色的代码块,表示处理某一逻辑的相关代码:

在这里插入图片描述

更好的TS支持

​ 组合式API主要利用基本的变量和函数,这些东西本身就是类型友好的,因此组合式API对于TypeScript的支持性更好。

更小的打包体积

​ 使用组合式API(<script setup>)的单文件组件中的组件模板(<template>)在打包时,会被编译成一个内联函数,与JS代码位于同一作用域内,可以直接访问定义的变量和函数,无需像选项式API一样依赖this这个上下文对象来访问属性。由于变量名可以被压缩,但是对象的属性名不能被压缩,所以组合式API对于代码压缩更友好,打包后的体积也就更小。

3、使用方法
方法一:使用setup() 钩子函数(不常用)

​ 这种方法并不常用,通常只用于以下两种情况:① 在非单文件组件中使用组合式API时,例如:在html文件中引入Vue。② 在基于选项式API的单文件组件中集成使用组合式API时,例如:在Vue2中使用。

​ 通过使用setup()函数来定义响应式数据,然后将该函数返回的对象暴露给模板和组件实例,然后就可以在选项式API中通过this来获取相关属性,如果在模板中使用变量,则无需使用this。但是在setup()函数中,是无法访问组件实例的,也就是this对象。

<script>
// 引入响应式API
import { ref } from 'vue'export default {// 使用setup()钩子函数setup() {// 声明响应式变量const count = ref(0)// 返回值会暴露给模板和其他的选项式 API return {count}},mounted() {// 通过this来访问声明的响应式变量console.log(this.count) // 0}
}
</script><template><!-- 在模板中使用声明的变量 --><button @click="count++">{{ count }}</button>
</template>

setup()函数具有两个参数:第一个参数为props,用于获取组件实例中接受的props参数,会根据props参数的变化,响应式的变化;第二个参数为context,表示一个Setup上下文对象,该对象暴露了一些常用的API对象:

export default {// 组件接收父组件传递过来的props数据props: {title: String},// setup的第一个参数为props 第二个参数为contextsetup(props, context) {// 以props.xxx的形式方式访问数据 不要使用数据解构 这样会失去响应性console.log(props.title)// 透传 Attributes(非响应式的对象,等价于 $attrs)console.log(context.attrs)// 插槽(非响应式的对象,等价于 $slots)console.log(context.slots)// 触发事件(函数,等价于 $emit)console.log(context.emit)// 暴露公共属性(函数)console.log(context.expose)}
}

​ 更多详细用法请查阅:组合式 API:setup()。

方法二:使用<script setup>(常用)

​ 该方法是我们在Vue3中的主流用法,通过在单文件组件的<script>标签中增加setup,标识该标签内部使用组合式API,大大简化了setup()函数的繁琐语法。而且在<script setup>中的顶层(非局部)中导入的模块、声明的变量和函数,可以在当前单文件组件中直接使用。

<!-- 在script标签中增加setup -->
<script setup>
// 引入要使用的API
import { ref, onMounted } from 'vue'
// 声明变量
let count = ref(0);
// 声明函数
const addCount = () => {// 在函数中修改变量 需要调用.valuecount.value++;
}
// 在生命周期中使用声明的变量
onMounted(() => {// 获取变量值需要调用.valueconsole.log('mounted---',count.value)
})
</script><template><!-- 在模板中使用声明的变量 无需使用.value --><button @click="addCount">{{ count }}</button>
</template>

二、变量声明

1、ref(推荐)

​ 在组合式API中,推荐使用ref()函数来声明响应式变量,该函数可以声明任何类型的值,包括StringNumber等简单类型,也包括深层嵌套的对象、数组以及JS内置的Map、Set等内置数据结构。简单类型的变量可以ref()可以直接声明,复杂类型的变量则会在内部直接调用 reactive()函数来实现。

​ 想要使用 ref()函数,需要先从vue对象中将该函数引入到当前组件中:

// 引入ref()函数API
import { ref } from 'vue'

ref()函数接收一个参数,这个参数为声明的响应式变量的初始值,然后函数会返回一个带有.value属性的ref对象。我们需要使用let/const/var声明一个变量,来接收ref()函数返回的响应式对象,至此,一个响应式变量就创建成功了。

// 通过ref()函数声明响应式变量
const count = ref(0)

​ 通过ref()函数声明的变量,如果是在<script setup>的顶层(非局部)中声明的,那可以在整个单文件组件中访问。在组件的<script setup>标签中修改或访问时,需要通过变量名.value的形式访问,但是在组件的<template>模板中使用变量,包括{{ }}模板语法、事件监听器的表达式等等情况,可以直接使用变量名,此时响应式变量会自动解包,获取其value属性。

<script setup>// 省略前面的引入和声明// 直接访问count 返回一个含有value属性的对象console.log(count) // { value: 0 }// 访问count.valueconsole.log(count.value) // 0// 修改变量值count.value++// 再次访问变量console.log(count.value) // 1
</script><template><!-- 在模板中使用声明的变量 无需使用.value --><button @click="count++">{{ count }}</button>
</template>

​ 在组件的<template>模板中只有顶级的ref变量才会被自动解包,如果是非顶级的ref变量,则不会解包,例如在一个对象中将属性值声明为ref变量:

<script setup>// 省略前面的引入和声明// 顶级的ref变量 let obj3 = ref({ count: 0 });// 非顶级的ref变量 let obj4 = { count: ref(0) };
</script><!--  自动解包 显示:0 -->
<p>{{ obj3.count }}</p>
<!-- 此时 ref 是文本插值的最终计算值 会自动解包 因此显示:0 -->
<p>{{ obj4.count }}</p>
<!-- 自动解包 显示:1 -->
<p>{{ obj3.count + 1 }}</p>
<!-- 此时 ref 不是文本插值的最终计算值 不自动解包 显示:[object Object]1  -->
<p>{{ obj4.count + 1 }}</p>
2、reactive

reactive()函数是组合式API声明响应式变量的另外一种方式,只能用来声明复杂类型的数据变量(ObjectArrayMapSet等),不能声明简单类型的数据变量。想要使用 reactive()函数,需要先从vue对象中将该函数引入到当前组件中:

// 引入reactive()函数API
import { reactive } from 'vue'

reactive()函数接收一个参数,表示声明的响应式变量的初始对象值,然后函数会返回一个原始数据对象的 Proxy对象,该对象是响应式的。最终再使用let/const/var声明一个变量,来接收reactive()函数返回的响应式对象,至此,一个响应式变量就创建成功了:

// 通过reactive()函数声明响应式变量
const countObj = reactive({ count: 0 })

​ 通过reactive()函数声明的变量,如果是在<script setup>的顶层(非局部)中声明的,那可以在整个单文件组件中访问。在组件的<script setup>标签中和在<template>模板中使用变量时,直接使用变量名即可:

<script setup>// 省略前面的引入和声明// 直接调用变量名 会返回一个代理对象console.log(countObj); // Proxy(Object) {count: 0}// 调用代理对象的属性console.log(countObj.count); // 0// 代理对象的属性值可以直接修改countObj.count++;console.log(countObj.count); // 1
</script><template><!-- 在模板中使用声明的变量 无需使用.value --><button @click="state.count++">{{ state.count }}</button>
</template>

reactive() 返回的是原始对象的Proxy代理对象,它和原始对象是不全等的,只有Proxy对象是响应式的,更改原始对象不会触发更新。而且为保证访问代理的一致性,对同一个原始对象调用 reactive() 会总是返回同一个的Proxy对象,而对一个已存在的Proxy对象调用 reactive() 会返回其本身。

// 创建一个原始对象
let obj = { count: 0 };
// 使用reactive()创建一个响应式对象
let countObj = reactive(obj);
// 代理对象和原始对象并不相等
console.log(obj === countObj); // false
// 在同一个原始对象上重复调用reactive()会返回相同的代理
console.log(countObj === reactive(obj)); // true
// 在代理对象上再次调用reactive()会返回其本身
console.log(countObj === reactive(countObj)); // true

​ 如果reactive()函数包裹的变量初始值为一个多层嵌套对象的数据,reactive()会深层的转换代理对象,无论嵌套多少层,都会将每一层对象转换为代理对象,最终返回的Proxy对象内部全部都是响应式的。

// 声明一个嵌套对象
let obj2 = { test: { count: 0 } };
// 使用reactive()创建一个响应式对象
let countObj2 = reactive(obj2);

​ 对于一个通过reactive()创建的响应式对象,如果我们使用对象解构,将其简单类型的属性解构为一个变量时,或者将简单类型的属性传递给函数作为参数时,变量和参数都会失去响应性的效果:

	// 当解构时,count 已经与 countObj.count 断开响应式连接let { count } = countObj// 修改解构出的count的变量值  不会影响countObj的 countcount++console.log(count) // 1console.log(countObj.count) // 0// 该函数接收到的是一个普通的数字 无法响应式的追踪到countObj.count的变化testFunction(countObj.count) {// ...}// 如果想要保持响应式的特性 则需要将整个响应式对象传入其中testFunction2(countObj) {// ...}

​ 如果通过reactive()创建的响应式数组或原生集合类型(如 Map) 中,使用ref()声明的变量作为元素,其不会被自动解包,需要通过.value的形式访问:

// 响应式数组中使用ref()声明的变量作为元素
const books = reactive([ref('Vue 3 Guide')])
// 这里需要 .value
console.log(books[0].value)
// 响应式集合中使用ref()声明的变量作为元素
const map = reactive(new Map([['count', ref(0)]]))
// 这里需要 .value
console.log(map.get('count').value)
3、shallowRef

shallowRef()函数是ref()函数的浅层作用形式,函数接收一个参数,这个参数为声明的响应式变量的初始值,但是函数不会返回一个Proxy代理对象,返回的是原始数据本身。此时只有对变量.value的访问是响应式的。如果想要转换为响应式的原始数据是复杂类型的数据变量,则更改其内部的属性值并不会有响应式,只有变更整个value的值才会触发响应式。

​ 如果原始数据是具有多层嵌套的复杂数据类型,也只有对.value的访问是响应式的。因此shallowRef() 常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成,避免对大型数据的响应性开销。

// 引入 shallowRef() API
import { ref, shallowRef } from 'vue'// 声明一个js对象
const obj = { num: 1 };
// 利用ref声明一个响应式变量
const state = ref(obj)
// 利用shallowRef声明一个浅层响应式变量
const state2 = shallowRef(obj)
// 输出两个变量
console.log(state.value); // 一个Proxy代理对象:Proxy(Object) {num: 1} 
console.log(state2.value); // 一个普通对象:{num: 1}
// 判断响应式变量和原对象是否相等
console.log(state.value === obj); // false
// 判断浅层响应式变量和原对象是否相等
console.log(state2.value === obj); // true
// 判断浅层响应式变量和响应式变量是否相等 
console.log(state2.value === state.value); // false// 直接修改内部属性值不会触发响应式
state.value.count = 2;// 直接修改value属性才会触发响应式
state.value = { count: 3 };

shallowRef()函数返回的是原始数据本身,如果我们在组件被挂载之前修改浅层响应式变量的内部属性值,依旧会被响应式监听到。例如:在<script setup>中声明浅层变量后,直接修改其内部属性值,或者在onBeforeMount()生命周期钩子函数中修改其内内部属性值。

<script setup>// 引入相关APIimport { onBeforeMount, onMounted, shallowRef } from 'vue'// 声明一个js对象const obj = { num: 1 };// 利用shallowRef声明一个浅层响应式变量const state2 = shallowRef(obj);// 在组件挂载之前 修改变量的内部属性值 页面显示会更新onBeforeMount(() => {state2.value.num = 1111;});// 在组件挂载之后 修改变量的内部属性值 页面显示不会更新onMounted(() => {console.log(state2.value); // {num: 1111}state2.value.num = 2222;console.log(state2.value); // {num: 2222}})
</script><!-- 最终渲染显示为:1111 -->
<p>{{ state2.num }}</p>
4、shallowReactive

shallowReactive()函数时reactive()函数的浅层作用形式,通过该函数的声明的响应式对象变量,只有根对象中的属性才是响应式的,如果变量包含嵌套的对象结构,则嵌套对象的属性值不会被转换成响应式,只有整个替换属性值时才会触发响应式。

// 引入 shallowReactive API
import { shallowReactive } from 'vue'// 声明一个原始对象
const obj = { num: 1, next: { count: 1 } };
// 声明一个浅层响应式对象
const state = shallowReactive(obj);
// 输出该对象
console.log(state); // Proxy(Object) {num: 1, next: {…}}// 直接修改根属性 可以触发响应式
state.num = 2222;
// 直接修改嵌套属性 不会触发响应式
state.next.count = 2222;
// 重新赋值嵌套属性 可以触发响应式
state.next = { count: 3333 };

​ 如果我们在组件被挂载之前修改其嵌套对象的属性值,依旧会被响应式监听到。例如:在<script setup>中声明浅层对象变量后,直接修改其嵌套对象的属性值,或者在onBeforeMount()生命周期钩子函数中修改其嵌套对象的属性值。

<script setup>// 引入相关APIimport { onBeforeMount, onMounted, shallowRef } from 'vue'// 声明一个原始对象const obj = { num: 1, next: { count: 1 } };// 声明一个浅层响应式对象const state = shallowReactive(obj);// 在组件挂载之前 修改变量的内部属性值 页面显示会更新onBeforeMount(() => {state.next.count = 2222;});// 在组件挂载之后 修改变量的内部属性值 页面显示不会更新onMounted(() => {console.log(state.next.count); // {num: 2222}state.next.count = 3333;console.log(state.next.count); // {num: 3333}})
</script><!-- 最终渲染显示为:2222 -->
<p>{{ state.next.count}}</p>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/449891.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python+django+vue高校学生社团管理系统euw84

社团管理系统是一个B/S模式系统&#xff0c;采用django框架&#xff0c;MySQL数据库设计开发&#xff0c;充分保证系统的稳定性。在系统的测试环节&#xff0c;主要通过功能测试的方式&#xff0c;验证系统的功能设计是否符合要求&#xff0c;能否满足使用需求。本社团管理系统…

论文阅读-在分布式数据库环境中对哈希算法进行负载均衡基准测试

论文名称&#xff1a;Benchmarking Hashing Algorithms for Load Balancing in a Distributed Database Environment 摘要 现代高负载应用使用多个数据库实例存储数据。这样的架构需要数据一致性&#xff0c;并且确保数据在节点之间均匀分布很重要。负载均衡被用来实现这些目…

springboot并mybatis入门启动

pom.xml,需要留意jdk的版本&#xff08;11&#xff09;和springboot版本要匹配&#xff08;2.7.4&#xff09;&#xff0c;然后还要注意mybatis启动l类的版本&#xff08;2.2.2&#xff09; <?xml version"1.0" encoding"UTF-8"?> <project xm…

用免费软件分流抢票Bypass辅助购买火车票的亲测可用方法

本文介绍基于分流抢票&#xff08;Bypass&#xff09;软件&#xff0c;协助购买预售、已开售或候补等各类状态的火车票的方法。 最近几天一直没有顾得上看过年回家的火车票&#xff0c;导致错过了回家车票的开售时间&#xff0c;所以当时就直接去12306的官方网站候补了&#xf…

vite打包原理

vite 工程化开发&#xff1a;打包工具 启动速度很快 核心原理还是webpack 把webpack封装了&#xff0c;把webpack对象封装了 和vue2整体结构几乎一致 webpack两种模式&#xff1a;开发&生产 代码打包编译&#xff0c;本地起一个web服务器实时预览编译后的结果 build 命令模…

爱快安装和双宽带叠加

文章目录 一、爱快安装二、爱快使用1. 配置WAN12. 配置WAN23. 配置LAN14. 配置DHCP5. DNS设置6. 多线负载7. 基础设置 三、通过交换机拨号1. 交换机设置2. 配置WAN1 一、爱快安装 &#xff08;1&#xff09;下载爱快路由ISO版本 爱快路由系统下载 &#xff08;2&#xff09;IS…

1894_透明性以及可显性

1894_透明性以及可显性 全部学习汇总&#xff1a; g_unix: UNIX系统学习笔记 (gitee.com) 这是一个透明性以及可显性的基本描述。 这个是对透明性以及可显性的功能作用的一个基本描述。 做一个简单的小结&#xff0c;从一个软件工程师的角度来看看透明性以及可显性的概念和作用…

基于SpringBoot+Vue的高校在线答疑管理系统

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 二、开发技术与环…

发掘小区商机:适合小区开的店有哪些?

随着城市生活节奏的加快&#xff0c;小区商业逐渐成为了创业的新热点。对于许多初次创业者或低成本创业者来说&#xff0c;小区开店不仅投资小、风险低&#xff0c;而且更容易融入社区&#xff0c;获得稳定的客源。那么&#xff0c;在众多的选择中&#xff0c;哪些店铺适合在小…

移动端基础-响应式开发:Bootstrap前端开发框架

Bootstrap使用 目前只考虑样式库CSS布局效果 步骤&#xff1a; 1.创建文件 2.创建HTML骨架结构 3.引入相应样式文件 4.书写内容 创建文件 到官网下载好bootstrap.js 创建文件夹&#xff1a; 将样式引入 直接查找css样式&#xff1a; 注意&#xff1a;不同的样式是通过类…

NETX90-多协议通讯芯片

随着作为信息物理系统核心技术的工业物联网的发展&#xff0c;Hilscher 基于 netX 51/52成功开发了新一代网络控制器netX90&#xff0c;其安全性是产品的核心价值。可实现更高性能的集成&#xff0c;并提高功率效率等级&#xff0c;凭借其较小的外形尺寸能够满足规格尺寸更小的…

vue yarn certificate has expired

背景&#xff1a;我在用ant design pro框架进行初始化时&#xff0c;安装脚手架时&#xff0c;安装yarn时显示报错 原因分析&#xff1a;查了很久的资料&#xff0c;这种情况应该是开了服务器代理访问导致ssl安全证书失效了 解决办法&#xff1a; 在终端输入&#xff1a;yarn…