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

news2024/11/18 5:29:46

一、组合式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 第二个参数为context
  setup(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 = () => {
  // 在函数中修改变量 需要调用.value
  count.value++;
}
// 在生命周期中使用声明的变量
onMounted(() => {
  // 获取变量值需要调用.value
  console.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.value
  console.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的 count
  count++
  console.log(count) // 1
  console.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>
  // 引入相关API
	import { 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>
  // 引入相关API
	import { 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.coloradmin.cn/o/1428728.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

编程实例分享,眼镜店电脑系统软件,配件验光管理顾客信息记录查询系统软件教程

编程实例分享&#xff0c;眼镜店电脑系统软件&#xff0c;配件验光管理顾客信息记录查询系统软件教程 一、前言 以下教程以 佳易王眼镜店顾客档案管理系统软件V16.0为例说明 如上图&#xff0c; 点击顾客档案&#xff0c;在这里可以对顾客档案信息记录保存查询&#xff0c;…

idea项目如何上传gitee

1.先创建仓库&#xff08;nonono&#xff01;&#xff01;&#xff01;idea上传会自动创建仓库&#xff01;&#xff01;&#xff01;&#xff01;&#xff09; 2.从gitee上面clone下来&#xff08;nonono&#xff01;&#xff01;&#xff01;&#xff01;这个.git文件也是自动…

【2024年美赛】C题,一二问的模型AUC为1.0,泛化能力超强

2024年美赛C题第一二问模型 基于L2正则化的Logistic回归模型AUC 1.0 !!! 并且超强泛化能力 基于L2正则化的Logistic回归模型 考虑到这里可以看作是一个二分类问题&#xff0c;且需要对模型和结果进行解释和可视化&#xff0c;因此&#xff0c;我选择了基于L2正则化的Logistic…

小程序支付类型接入京东支付

一、情景描述 当前项目想在微信小程序付款时添加上京东支付支付类型&#xff0c;效果如下 普通的付款方式可以直接付款就能完成支付&#xff0c;但京东支付无法在小程序上直接付款&#xff0c;他需要复制生成的链接&#xff0c;然后打开京东app然后在京东平台上付款。 所以&…

【已更新】2024美赛C题代码教学思路数据处理数学建模分析Momentum in Tennis

问题一完整的代码已给出&#xff0c;预计2号晚上或者3号凌晨全部给出。 代码逻辑如下&#xff1a; C题第一问要求我们开发一个模型&#xff0c;捕捉得分时的比赛流程&#xff0c;并将其应用于一场或多场比赛。你的模型应该确定哪名球员在比赛的特定时间表现得更好&#xff0c;…

洛谷p1644跳马问题

跳马问题 题目背景 在爱与愁的故事第一弹第三章出来前先练练四道基本的回溯/搜索题吧…… 题目描述 中国象棋半张棋盘如图 1 1 1 所示。马自左下角 ( 0 , 0 ) (0,0) (0,0) 向右上角 ( m , n ) (m,n) (m,n) 跳。规定只能往右跳&#xff0c;不准往左跳。比如图 1 1 1 中所…

python解决替换空格问题

对于字符串中&#xff0c;利用指定字符替换字符串中的所有空格&#xff0c;使用合适的方法来避免多次移动字符的操作&#xff0c;考虑使用python的内置方法来简便的解决该问题&#xff0c;可以更加了解到python的便捷。 对于给定一个内部含有空格字符的字符串input_str&#x…

git小白进阶之路

git是最常用的版本控制工具&#xff0c;我对其进行了整理后续补充&#xff0c;这个文档欢迎大家来讨论&#xff0c;当前我的视频梳理&#xff1a; git小白进阶之路_哔哩哔哩_bilibili&#xff0c;非常希望大佬们能够批评指正&#xff0c;并多多交流。 目录 初始配置 配置账号…

JUC并发编程02——线程原理(运行机制,线程调度,未来优化)

1.线程原理 1.运行机制 Java Virtual Machine Stacks&#xff08;Java 虚拟机栈&#xff09;&#xff1a;每个线程启动后&#xff0c;虚拟机就会为其分配一块栈内存 每个栈由多个栈帧&#xff08;Frame&#xff09;组成&#xff0c;对应着每次方法调用时所占用的内存每个线程…

Unity_使用Shader实现玻璃和镜面效果

效果图如下&#xff1a; 玻璃效果图 镜面效果图 Step1 搭建场景→镜子使用Quad代替&#xff0c;放置在需要反射的墙面→创建新的材质和Shader Step2 墙壁外创建Camera&#xff0c;用来渲染物体后方的视图→创建RenderTexture&#xff0c;赋于该相机 Step3 Shader的编写如下…

腾讯云云监控实践:使用云审计 CloudAudit SDK 精准管理腾讯云资源

文章目录 前言一、什么是腾讯云的操作审计 CloudAudit二、CloudAudit 有哪些优势三、CloudAudit 应用场景举例3.1 安全分析3.2 资源变更跟踪3.3 合规性审计 四、使用云审计 SDK 进行云监控4.1 安装环境包 PHP4.2 下载并解压云审计 PHP SDK4.3 创建的腾讯云持久证书&#xff08;…

机器学习入门-----sklearn

机器学习基础了解 概念 机器学习是人工智能的一个实现途径 深度学习是机器学习的一个方法发展而来 定义:从数据中自动分析获得模型,并利用模型对特征数据【数据集:特征值+目标值构成】进行预测 算法 数据集的目标值是类别的话叫做分类问题;目标值是连续的数值的话叫做回…

第二十一回 阎婆大闹郓城县 朱仝义释宋公明-FreeBSD Linux 使用Rsync备份

阎婆状告宋江杀死她女儿阎婆惜&#xff0c;知县有意偏袒宋江&#xff0c;只是一味的拷打唐牛儿&#xff0c;但无奈张三张文远说刀子是宋江的&#xff0c;知县不得已差人拿宋江来审问。第一次没见到人&#xff0c;第二次派朱仝雷横两个人去。 朱仝到地窖里找到了躲藏的宋江&…

【Java程序设计】【C00243】基于Springboot的社区医院管理系统(有论文)

基于Springboot的社区医院管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的社区医院管理服务系统 本系统分为系统功能模块、管理员功能模块、用户功能模块以及医生功能模块。 系统功能模块&#xff1a;社…

C++数据结构与算法——哈希表

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

学习Android的第一天

目录 什么是 Android&#xff1f; Android 官网 Android 应用程序 Android 开发环境搭建 Android 平台架构 Android 应用程序组件 附件组件 Android 第一个程序 HelloWorld 什么是 Android&#xff1f; Android&#xff08;发音为[ˈnˌdrɔɪd]&#xff0c;非官方中文…

C#读取和保存INI文件配置

在C#应用程序中&#xff0c;读取和保存配置文件是常见的任务&#xff0c;而INI文件是一种轻量级的配置文件格式。在以下代码中&#xff0c;我们将探讨如何使用C#创建一个窗体应用程序&#xff0c;并通过读取和保存INI文件配置来实现一些基本的功能。 创建IniHelper类 首先&…

C语言搭配EasyX实现贪吃蛇小游戏

封面展示 内部展示 完整代码 #define _CRT_SECURE_NO_WARNINGS #include<easyx.h> #include<stdio.h> #include<mmsystem.h> #pragma comment (lib,"winmm.lib") #define width 40//宽有40个格子 #define height 30//长有40个格子 #define size 2…

1.27马尔科夫链,抽样蒙特卡洛模拟(逆转化方法,接受拒绝矩阵),马尔科夫链蒙特卡洛MCMC,隐马尔科夫(HMM(V算法剪枝优化),NLP)

马尔科夫链 蒙特卡洛法模拟 抽样&#xff0c;逆转换方法 就是说由系统自带的随机函数RANDOM&#xff0c;通过下面这个方法&#xff0c;可以变为对应的随机模拟函数 就是说要实现蒙特卡洛模拟&#xff0c;是要先有一个概率表达式&#xff0c;然后基于这个概率表达式&#xff0…

Leetcode2855. 使数组成为递增数组的最少右移次数

Every day a Leetcode 题目来源&#xff1a;2855. 使数组成为递增数组的最少右移次数 解法1&#xff1a;暴力 由于右移 n 次就变回原数组了&#xff0c;所以答案至多为 n−1。 枚举右移次数&#xff08;1~n-1&#xff09;&#xff0c;每次右移一个元素后判断数组是否有序&…