Vue3(一):创建vue3工程、setup、vue3响应式原理、computed和watch

news2025/4/19 14:10:08

Vue3:第一章

  • 一、创建Vue3.0工程
    • 1.使用vue-cli创建
    • 2.使用vite创建
  • 二、Vue3中的响应式
    • 1.拉开序幕的setup
    • 2.ref函数
    • 3.reactive函数
    • 4.vue3中响应式的原理
      • (1)vue2中响应式原理
      • (2)Vue3中的Proxy
    • 5.reactive和ref的对比
    • 6.setup的两个注意点
      • (1)执行时机
      • (2)setup的参数
  • 三、计算属性和监视
    • 1.computed函数
    • 2.watch函数
      • (1)第一个参数怎么写
      • (2)第一个参数到底写不写.value
    • 3.watchEffect函数

一、创建Vue3.0工程

1.使用vue-cli创建

官方文档:https://cli.vuejs.org/zh/guide/creating-a-project.html#vue-create

// 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
// 安装或者升级你的@vue/cli
npm install -g @vue/cli
// 创建
vue create vue_test
// 启动
cd vue_test
npm run serve

2.使用vite创建

官方文档:https://v3.cn.vuejs.org/guide/installation.html#vite
vite官网:https://vitejs.cn

// 创建工程
npm init vite-app <project-name>
// 进入工程目录
cd <project-name>
// 安装依赖
npm install
// 运行
npm run dev

二、Vue3中的响应式

1.拉开序幕的setup

之前我们在vue2中都是多个配置项组合在一起(选项式API),在vue3中,换了一种方式,那就是Composition API(组合式API),而setup则是组合式API表演的舞台,组件中所有的数据和方法都写在setup函数中。

这个setup函数其实有两种返回值:1、返回一个对象 2、返回一个渲染函数,用的最多的就是返回一个对象,把用到的所有属性、方法都返回出去,方便在模板中使用(当然,现在有了setup语法糖,后面再说)

export default {
	name: 'App',
	setup() {
		const name = 'zzy'
		const age = 18
		return {
			name,
			age,
		}
		//返回一个渲染函数
		//return () => h(h1, 'zzy')
 	}
}

注意:不要把setup和vue2中的选项式API混用,因为setup中访问不到vue2中的属性方法(但2可访问3),如果混用的话,有重名setup优先。

还有,setup前不要加async,因为返回值会变成promise,而不是我们return的那些属性和方法

2.ref函数

作用:定义响应式的数据。

import { ref } from 'vue'
const count = ref(0)

上面代码创建了一个包含响应式数据的引用对象(RefImp对象)
在js中操作数据:count.value
在模板中读取数据:<div> {{count}} </div>(这里会自动读取value属性)

其实ref可以接受的数据不只是基本数据类型,复杂数据类型也可以,只不过比较麻烦,操作时都要.value

const person = ref({name: 'zzy', age: 18})
person.value.name = 'dantin'

基本数据类型和负责数据类型实现响应式的方式不同,前者依然是Object.defineProperty()的get与set,后者则在内部求助了reactive函数

3.reactive函数

作用:定义一个对象类型的响应式数据(基本类型不要用reactive,要用ref)

import { reactive } from 'vue'
const person = reactive({name: 'zzy', age: 18})
person.name = 'ht'

接收一个对象或数组,返回一个代理对象(Proxy对象),且这里的监视式深层次监视

4.vue3中响应式的原理

对于refreactive是不一样的,ref简单类型是通过:Object.defineProperty()getset,当然啊,ref定义的复杂类型是通过reactiveProxy

reactive是通过Proxy来实现响应式的(上文提到了),并通过Reflect来操作源数据

不管怎么样,总结来说,vue3中新增的就是对于复杂数据类型通过Proxy实现响应式,也就是两个点:

  1. 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性的增删、属性值的读写等。
  2. 通过Reflect(反射): 对源对象的属性进行上述操作。

(1)vue2中响应式原理

我们知道vue2中是通过Object.defineProperty()对对象类型数据进行劫持并添加getter和setter来监视数据的,对于数组是重写了更新数组的方法。复习vue2监视数据的原理

vue2监视数据存在两个问题:
1、直接新增属性、删除属性, 界面不会更新。2、直接通过下标修改数组, 界面不会自动更新。

(2)Vue3中的Proxy

在vue3中,**解决了vue2中的两个问题。下面是模拟实现vue3中响应式的原理:

const person = {
  name: 'zzy',
  age: 18
}

const p = new Proxy(person, {
  get(target, propName) {
    console.log(target)
    console.log(`有人读person中的${propName}`)
    return target[propName]
  },
  set(target, propName, value) {
    console.log(`有人改person中的${propName},修改后的值为:${value}`)
    target[propName] = value
  },
  deleteProperty(target, propName) {
    console.log(`有人删除了person中的${propName}`)
    return delete target[propName]
  }
})

在这里插入图片描述

再严谨一些,用Reflect来进行增删改查,这也是框架的做法,其实本质上没什么太大的区别,只是Reflect可以避免一些报错吧,不然还要用try-catch去捕获?这里不用太纠结,其实就是为了让框架更加的友好(少一些报错)

const person = {
  name: 'zzy',
  age: 18
}

const p = new Proxy(person, {
  //查
  get(target, propName) {
    console.log(target)
    console.log(`有人读person中的${propName}`)
    // return target[propName]
    return Reflect.get(target, propName)
  },
  //增&改
  set(target, propName, value) {
    console.log(`有人改person中的${propName},修改后的值为:${value}`)
    // target[propName] = value
    Reflect.set(target, propName, value)
  },
  //删
  deleteProperty(target, propName) {
    console.log(`有人删除了person中的${propName}`)
    // return delete target[propName]
    return Reflect.deleteProperty(target, propName)
  }
})

5.reactive和ref的对比

1、定义数据类别不同

ref用于定义基本数据类型(也可以定义复杂类型)
reactive用于定义对象或数组类型

2、原理不同

ref是通过:`Object.defineProperty()`get与set,当然啊,复杂类型是通过reactive的Proxy
reactive是通过Proxy来实现响应式的(上文提到了),并通过Reflect来操作源数据

3、使用方式不同

ref定义的数据,操作时需要.value,模板读取不需要.value
reactive定义的数据,操作和读取都不需要.value

6.setup的两个注意点

(1)执行时机

setup函数的执行时机是beforeCreate之前,也就是所有生命周期的最前面,此时thisundefined,也就是说在setup中是绝对不能通过this访问组件实例的。

(2)setup的参数

setup接收两个参数:(props,context)
props是一个对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
context是一个对象,包含三个属性,分别是attrs、slots、emit
第一个attrs相当于this.$attrs,值为对象,包含组件外部传递过来,但没有在props配置中声明的属性
第二个slots相当于this.$slots,包含收到的插槽的内容。
第三个emit相当于this.$emit,用来触发组件的自定义事件。

props: ['name', 'age']
setup(props, context) {
  console.log(props) // Proxy{name:'zzy',age:18}:组件外部传递过来,且组件内部声明接收了的属性。
  console.log(context.attrs)//相当于this.$attrs
  console.log(context.slots)相当于this.$slots
  console.log(context.emit)//相当于this.$emit
}

三、计算属性和监视

1.computed函数

和vue2中功能是一样的,只不过变成了一个函数,而且要手动引入。默认写法是传一个回调

import {computed} from 'vue'

setup(){
    ...
	//计算属性——简写(只读readonly)
    let fullName = computed(()=>{
        return person.firstName + '-' + person.lastName
    })
}

当我们去改计算属性的值的时候,控制台可能会有警告:

在这里插入图片描述
这是因为计算属性默认是只读的,当它所依赖的值改变的时候,它自己会变。如果要想改计算属性,需要用下面的完整写法,传一个对象,里面有gettersettersetter中参数是修改后的新值

import {computed} from 'vue'

setup(){
    ...
    //计算属性——完整(包括读和写)
    let fullName = computed({
        get(){
            return person.firstName + '-' + person.lastName
        },
        set(value){
            const nameArr = value.split('-')
            person.firstName = nameArr[0]
            person.lastName = nameArr[1]
        }
    })
}

2.watch函数

其实watch在vue3中和vue2中功能是一样的,就是写法不一样。
先准备一些数据吧:

import { ref, reactive } from 'vue'
const sum = ref(0)
const msg = ref('hello')
const person = reactive({
	name: 'dantin'
	age: 18
	job: {
		type: 'code'
		salary: '1000k'
	}
})

(1)第一个参数怎么写

情况一:监视ref定义的响应式数据

watch(sum,(newValue,oldValue)=>{
	console.log('sum变化了',newValue,oldValue)
},{immediate:true})

情况二:监视多个ref定义的响应式数据

watch([sum,msg],(newValue,oldValue)=>{
	console.log('sum或msg变化了',newValue,oldValue)//new和old也是监听值构成的数组
}) 

情况三:监视reactive定义的响应式数据
这里有两个坑,第一个是reactive定义的数据,监视时回调中无法获得oldValue!oldValue和new一样
第二个坑是,监视reactive定义的数据,默认开启的deep:true,且deep不能改成false

watch(person,(newValue,oldValue)=>{
	console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不再奏效

情况四:监视reactive定义的响应式数据中的某个属性
这里要注意,第一个参数必须写成箭头函数,如果直接写person.job,那么就相当于写了个死的值,这样是监视不到的。还有就是如果job是一个对象,那么默认deepfalse的,如果要深度监视需要手动开启deep:true(deep配置有效)

watch(()=>person.job,(newValue,oldValue)=>{
	console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true}) 

情况五:监视reactive定义的响应式数据中的某些属性
如果这种情况的话和上面类似,不同的是第一个参数写成数组,且每个元素是箭头函数,返回的new和old值也是相对应的值构成的数组。

watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
	console.log('person的job变化了',newValue,oldValue)
},{immediate:true,deep:true})

(2)第一个参数到底写不写.value

如果我们把person换成是一个ref定义的呢?那么监视的时候写不写.value

import { ref, reactive } from 'vue'
const sum = ref(0)
const msg = ref('hello')
const person = ref({
	name: 'dantin'
	age: 18
	job: {
		type: 'code'
		salary: '1000k'
	}
})

答案是:要写的,因为ref对于复杂数据类型,内部是借助reactiveProxy实现响应式的,所以这么写的话就相当于是写了一个reactive定义的响应式数据,在监视时也就具有了对应的坑(见上文情况三)

watch(person.value,(newValue,oldValue)=>{
	console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不奏效

除此之外还有一种办法,那就是深度监视person(它是一个RefImpl对象),这样就能监视到其中的value及更深层的变化。这是因为如果直接监视person不读取.value,那么监视的是RefImpl对象,只有其中value的地址变的时候才能监视到,value里面的东西变化是监视不到的,所以要开deep

watch(person,(newValue,oldValue)=>{
	console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:true}) 

那为什么简单数据类型不需要.value呢?其实和上面的情况四是一样的,如果简单数据类型直接.value,那么监视的就是一个写死的值。不.value的话,监视的是一个响应式的RefImpl对象,当里面value变化的时候是可以监视到的(这块儿感觉懵懵的,回头看源码吧)

watch(sum,(newValue,oldValue)=>{
	console.log('sum变化了',newValue,oldValue)
})

如果非要.value,请使用箭头函数动态读取,每次sum变化都会执行回调读取最新的值

watch(() => sum.value,(newValue,oldValue)=>{
	console.log('sum变化了',newValue,oldValue)
})

3.watchEffect函数

  1. watch的套路是:既要指明监视的属性,也要指明监视的回调。

  2. watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。

watchEffect有点像computed,但computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。

//watchEffect的回调一上来会先执行一次
//watchEffect所指定的回调中用到的数据只要发生变化,则直接重新执行回调。
watchEffect(()=>{
    const x1 = sum.value
    const x2 = person.age
    console.log('watchEffect配置的回调执行了')
})

其实感觉这玩意儿就是在模仿React中的useEffect

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

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

相关文章

性能测试如何做?超详细性能测试-测试策略总结,新人进阶之路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 性能测试出现的初…

YOLOv5【训练train.py逐行源码及参数调参解析】超详细解读!!!建议收藏✨✨!

之前的文章介绍了YOLOv5的网络结构&#x1f680;与目录结构源码&#x1f680;以及detect.py&#x1f680;的详细解读&#xff0c;今天带来的是YOLOv5的 train.py 代码参数逐行解读以及注释&#xff0c;废话不多说&#xff0c;让我们一起学习YOLOv5的 train.py 源码吧&#xff0…

功能测试和自动化测试的差距在哪里?

一直以来&#xff0c;软件的测试主要是以手工测试为主&#xff0c;但是随着现代软件的复杂程度的加深&#xff0c;人们对使用手工方式来完成软件测试感到的越来越力不从心&#xff0c;同时因为在软件测试中存在着大量的重复性工作&#xff0c;而这种工作是比较适合机器而不是人…

rsync

配置rsync源服务器&#xff1a; #建立/etc/rsyncd.conf 配置文件 vim /etc/rsyncd.conf #添加以下配置项 uid root gid root use chroot yes #禁锢在源目录 address 192.168.80.10 …

​Kali-linux无线网络嗅探工具Kismet​

如果要进行无线网络渗透测试&#xff0c;则必须先扫描所有有效的无线接入点。刚好在Kali Linux中&#xff0c;提供了一款嗅探无线网络工具Kismet。使用该工具可以测量周围的无线信号&#xff0c;并查看所有可用的无线接入点。本节将介绍使用Kismet工具嗅探无线网络。 &#xf…

MySQL_6 自连接和外连接

目录 一、自连接 1.概述 : 2.语法 : 3.演示 : 二、外连接 1.为什么需要外连接&#xff1f; 2.外连接的定义 : 3.外连接的演示 : 1 左外连接 2 右外连接 3 对部门表问题的解决 一、自连接 1.概述 : 自连接是指在同一张表上的连接查询&#xff08;将同一张看做两张表)&a…

R语言实践——rWCVP:按照物种的原生分布区清洗坐标点

rWCVP&#xff1a;按照物种的原生分布区清洗坐标点 加载库工作流&#xff08;单个物种&#xff09;1. 下载发现记录数据&#xff08;rgbif&#xff09;2. 发现记录的预备3. 获取原生区范围4. 清除非原生分布记录 加载库 library(rWCVP) library(rgbif) library(tidyverse) lib…

产品Backlog和需求管理

产品Backlog 产品backlog是一个按照价值排序的需求清单。为了达成产品目标&#xff0c;所有的需求都需要放到产品backlog中进行管理和规划。由产品负责人负责管理和维护。Leangoo为每一个里程碑建立了一个产品Backlog看板, 通过这个产品backlog看板来进行需求管理和规划。 里…

搭建服务器的主流中间件有哪些?如何在外网访问内网的服务?

计算机业内人士对于搭建服务器的中间件并不陌生&#xff0c;apache、tomcat、IIS、nginx 都是比较常用的搭建服务器的中间件&#xff0c;它们之间还是有一些区别差异的。今天就说说这些中间件之间有哪些区别&#xff0c;以及如何利用快解析实现内网主机应用让外网访问。 首先说…

大数据入门(六)- UCloud创建云服务器

一.注册UCloud账户 使用UCloud的服务&#xff0c;首先需要注册账户 账户注册非常简单&#xff0c;在UCloud网站首页&#xff08;https://www.ucloud.cn/&#xff09;右上角点击快速注册 二.创建VPC 1.注册成功后&#xff0c;选择全部产品 2.选择私有网络UVPC 3.区域选择华北…

uni-app开发小程序使用uni.chooseMedia选择图片,安卓手机无法选择图片

uni-app开发小程序时&#xff0c;使用uni.chooseMedia选择图片&#xff0c;苹果手机是正常的&#xff0c;安卓手机无法打开手机选择图片 问题复现解决方法&#xff01;&#xff01;我的反思与总结 问题复现 一、在小程序中&#xff0c;选择图片并上传&#xff0c;是一个很常见…

[Python报错] ImportError: cannot import name ‘timer’ from ‘timer’

[Python报错] ImportError: cannot import name ‘timer’ from ‘timer’ 前言 我最近遇到这个报错&#xff0c;卡了我好长时间去解决&#xff0c;我在互联网上找了很多资料&#xff0c;遗憾的是&#xff0c;我没找到有其他人遇到的类似的问题。所以在这里做一下记录。事实上…

两年外包,从4K涨到了15K....

我18年毕业于一个普通二本学校&#xff0c;电子信息工程学院&#xff0c;是一个很不出名的小本科。大学期间专业知识也没有去认真的学习&#xff0c;所以毕业的时候就随便找了一份工作&#xff0c;在一个外包公司做功能测试。 记得那时候薪资大概是4k左右&#xff0c;因为是以…

神经网络实验---人工神经网络(2)

本实验目的主要是掌握梯度下降法的优化算法&#xff1b;能够使用tf.keras构建Sequential模型&#xff0c;完成多分类任务。 1. 实验目的 ①掌握梯度下降法的优化算法&#xff1b; ②能够使用tf.keras构建Sequential模型&#xff0c;完成多分类任务。 2. 实验内容 ①下载MNIS…

架构设计之复用性概谈

作为开发人员&#xff0c;你对复用这个概念一定不陌生。在开发过程中&#xff0c;我们把系统中通用的代码逻辑抽取出来&#xff0c;变成公共方法或公共类&#xff0c;然后在多个地方调用&#xff0c;这就是最简单的技术上的复用。 但一开始&#xff0c;我们不会过多地考虑复用&…

迭代后首波实测!360智脑一键联网,代码超强,AI诈骗一眼看穿

360大模型&#xff08;又&#xff09;交卷了&#xff01;作为国内首个能联网的大模型&#xff0c;360智脑给了我们怎样的惊喜&#xff1f;话不多说&#xff0c;新鲜出炉的第一手实测来了。 不得不说&#xff0c;自从GPT大模型混战开赛之后&#xff0c;教主周鸿祎可谓是存在感十…

易观分析:消费金融机构以APP为触点,创新消费场景,激发消费活力

易观&#xff1a;随着“用好消费金融、释放消费潜力”等金融支持扩内需的持续深入&#xff0c;消费金融APP活跃用户规模将保持稳健增长的态势&#xff0c;进而在拉动内需、促进消费升级、服务实体经济中扮演更重要的角色。 一、疫情防控较快平稳转段&#xff0c;激发消费市场活…

总结button,input type=“button“,input type=“text“中:[在value添加值] 和 [标签内添加值]的区别

1.如果是需要一个 普通文本框 <input></input>和<input type"text"></input>外观相同 都是 2.对比button,input type"button",input type"text"中&#xff1a;在value添加值 和 标签内 添加值 html中&#xff1a; &l…

第十三章 常用类(Math 类、Arrays 类、System类、Biglnteger 和BigDecimal 类、日期类)

一、Math 类&#xff08;P481&#xff09; Math类包含&#xff0c;用于执行基本数学运算的方法&#xff0c;如初等指数、对数、平方根和三角函数 &#xff08;1&#xff09;abs&#xff1a;绝对值 &#xff08;2&#xff09;pow&#xff1a;求幂 double pow Math.pow(2, 4); /…

520 | ChatGPT会是一个完美情人吗?

A GENERATIVE AI EXPERIMENT 机器人能感受到爱吗&#xff1f;这个困扰了科学家们多年的未解之谜&#xff0c;如今随着ChatGPT的爆火再次回到人们的视线中。虽然我们尚未准备好为机器赋予情感&#xff0c;但机器已经可以借助生成式AI来帮助人类表达自己的情感。 自然情感表达 …