转战VUE3学习

安装好vue3后,开始新的项目吧!

npm init vue@latest

1.组合式API

在以前vue2的项目里,使用的选项式API将各种不同逻辑的代码分散到像data、methods等不同的对象里。如果想看某一方面的逻辑,鼠标滚轮都要磨出火星了!

而升级到vue3之后组合式API就解决了这一难题。
在这里插入图片描述

1.1拉开序幕的setup

setup是所有组合式API表演的舞台。组件中所用到的数据、方法,均要配置在setup中。

//setup其实是一个函数
export default {
	name : 'APP',
	setup : function(){
	}
}
//函数可简写
export default {
	name : 'APP',
	setup(){
	}
}
//添加数据和方法
export default {
	name : 'APP',
	setup(){
		//数据
		let name = '张三'
		let age = 18
		//方法
		function sayHello(){
			alert("我是${name},我已经${age}岁了")
		}
	}
}

setup有返回值,有两种返回值:
(1)若返回对象,则对象中的属性、方法在模板中可以直接使用。(重点)
(2)若返回渲染函数,则可以自定义渲染的内容。(了解)

<template>
	<h1>姓名:{{ name }}</h1>
	<h1>年龄:{{ age }}</h1>
	<button @click="sayHello">说话</button>
</template>
<script>
export default {
	name : 'APP',
	setup(){
		//数据
		let name = '张三'
		let age = 18
		//方法
		function sayHello(){
			alert("我是${name},我已经${age}岁了")
		}
		//setup返回对象    return {name:name,age:age}   
		return {name,age,sayHello} //setup返回对象的简写形式(常用)
		//返回渲染函数
		//return () => h('h1','您好')
	}
}
</script>

1.2 兼容vue2

在vue3中其实也可以继续用vue2的写法。
在vue2的配置中可以读取vue3的配置,但是vue3的配置中不能读取vue2的配置。

<template>
	<h1>姓名:{{ name }}</h1>
	<h1>年龄:{{ age }}</h1>
	<h1>性别:{{ sex }}</h1>
	<button @click="sayHello">说话</button>
	<button @click="sayWelcome">欢迎</button>
</template>
<script>
export default {
	name : 'APP',
	data(){
		return {
			sex:'男'
		}
	},
	methods : {
		sayWelcome(){
			alert('欢迎')
		}
	},
	setup(){
		//数据
		let name = '张三'
		let age = 18
		//方法
		function sayHello(){
			alert("我是${name},我已经${age}岁了")
		}
		return {name,age,sayHello} 
	}
}
</script>

但是不建议vue3与vue2的配置混用!

1.3 ref函数

在之前setup中写的数据,并不是响应式的。当修改数据时,在模板中的数据没有相应的变化。因此,需要通过一个函数ref来讲数据变成响应式的。

<template>
	<h1>姓名:{{ name }}</h1>
	<h1>年龄:{{ age }}</h1>
	<button @click="changeInfo">修改个人信息</button>
</template>
<script>
export default {
	name : 'APP',
	setup(){
		//数据
		let name = '张三'
		let age = 18
		//方法
		function changeInfo(){
			name = '李四'
			age = 48
			console.log(name,age) //数据修改了,但是没响应到模板中
		}
		return {name,age,changeInfo} 
	}
}
</script>

现在引入ref函数,将初始化的字面量用ref函数包裹起来。

<script>
import {ref} from 'vue'
export default {
	name : 'APP',
	setup(){
		//数据
		let name = ref('张三')
		let age = ref(18)
		//方法
		function changeInfo(){
			// name = '李四'
			// age = 48
			console.log(name,age) //数据修改了,但是没响应到模板中
		}
		return {name,age,changeInfo} 
	}
}
</script>

用ref函数包裹的数据实际上是RefImpl的引用实现对象。
在这里插入图片描述
修改数据的方式需要使用value来修改。但是在模板中不用.value访问数据,因为vue3自动解析RefImpl对象的值了。

<template>
	<h1>姓名:{{ name }}</h1>
	<h1>年龄:{{ age }}</h1>
	<button @click="changeInfo">修改个人信息</button>
</template>
<script>
import {ref} from 'vue'
export default {
	name : 'APP',
	setup(){
		//数据
		let name = ref('张三')
		let age = ref(18)
		//方法
		function changeInfo(){
			name.value = '李四'
			age.value = 48
		}
		return {name,age,changeInfo} 
	}
}
</script>

当用ref函数包裹对象时,也需要value才能访问内部的值。

<template>
	<h1>姓名:{{ name }}</h1>
	<h1>年龄:{{ age }}</h1>
	<button @click="changeInfo">修改个人信息</button>
</template>
<script>
import {ref} from 'vue'
export default {
	name : 'APP',
	setup(){
		//数据
		let name = ref('张三')
		let age = ref(18) 
		let job = ref({
			type : '前端工程师',
			salary : '30k' 
		})
		//方法
		function changeInfo(){
			job.value.type = '李四'
			job.value.salary = '60k'
		}
		return {name,age,changeInfo} 
	}
}
</script>

但是,按道理RefImpl内部的值应该也是RefImpl的对象,但是实际上我们发现内部用的是Proxy对象。其实ref包裹对象类型数据时,借用了reactive函数。
在这里插入图片描述

总结:

  • 作用:定义一个响应式的数据
  • 语法:const xxx = ref(initValue)
  • 创建一个包含响应式数据的引用对象(ref对象)
  • js中操作数据:xxx.value 模板中读取数据:不需要.value,直接
    <div>{{xxx}}<div>
  • 接收的数据可以是基本类型,也可以是对象类型。
  • 基本类型的数据的响应式是靠Object.defineProperty()的get与set完成的。

1.4 reactive函数

在vue中,reactive函数(响应)的作用定义一个对象类型的响应式数据。(基本类型不用它,要用ref函数)

<script>
import {ref, reactive} from 'vue'
export default {
	name : 'APP',
	setup(){
		//数据
		let number = reactive(666)
		//方法
		function changeInfo(){
			console.log(number)
		}
		return {name,age,changeInfo} 
	}
}
</script>

若将reactive直接用于基本数据类型就会报错。
在这里插入图片描述
因此

<template>
	<h1>姓名:{{ name }}</h1>
	<h1>年龄:{{ age }}</h1>
	<button @click="changeInfo">修改个人信息</button>
</template>
<script>
import {ref} from 'vue'
export default {
	name : 'APP',
	setup(){
		//数据
		let name = ref('张三')
		let age = ref(18) 
		let job = reactive({
			type : '前端工程师',
			salary : '30k' 
		})
		//方法
		function changeInfo(){
			console.log(job)
		}
		return {name,age,changeInfo} 
	}
}
</script>

可以看到,reactive将对象类型的数据封装为Proxy对象。
在这里插入图片描述
使用reactive的好处:
支持修改深层属性
支持修改数组

<script>
import {ref} from 'vue'
export default {
	name : 'APP',
	setup(){
		//数据
		let name = ref('张三')
		let age = ref(18) 
		let job = reactive({
			type : '前端工程师',
			salary : '30k' ,
			a:{
				b:{
					c:666
				}
			}
		})
		let hobby = reactive(['抽烟','喝酒','烫头'])
		//方法
		function changeInfo(){
			job.a.b.c = 999 //reactive支持修改深层属性
			hobby[0] = '学习'  //reactive支持修改数组			
		}
		return {name,age,changeInfo} 
	}
}
</script>

reactive与ref的对比

(1)ref访问属性需要使用value,reactive访问属性不需要使用value
(2)ref定义基本数据类型,reactive定义对象类型
(3)ref的响应式原理是通过Object.defineProperty()实现的,reactive通过Proxy对象实现的。

//数据
let name = ref('张三')
let age = ref(18) 
//方法
function changeInfo(){
	name.value = '李四'		
	age.value = 44
}
//数据
let person= reactive({name:'张三',age:18})
//方法
function changeInfo(){
	person.name = '李四'		
	person.age = 44
}

1.5 setup的注意点

(1)setup函数的执行比beforeCreate函数要早
(2)setup函数里的this是undefined,这意味着setup里不能用this
(3)setup只能收到两个参数,分别是props用来接受父组件传给该组件的属性,以及context用来保存上下文信息。

<script>
import {ref} from 'vue'
export default {
	name : 'APP',
	beforeCreate(){
		console.log('---beforeCreate---')	
	},
	setup(){
		console.log('---setup---',this)	
		return {} 
	}
}
</script>

在这里插入图片描述
props传输

<script>
export default {
	name : 'APP',
	props: ['msg','school'], //必须声明props才能接受属性
	beforeCreate(){
		console.log('---beforeCreate---')	
	},
	setup(props){ //通过props可以访问父组件传过来的值
		console.log(props)	
		console.log(props.msg)
		console.log(props.school)
		return {} 
	}
}
</script>

上下文context

<script>
export default {
	name : 'APP',
	props: ['msg','school'],
	beforeCreate(){
		console.log('---beforeCreate---')	
	},
	setup(props,context){
		console.log(context)	
		return {} 
	}
}
</script>

context里面是一个expose对象,它包含attrsemitslots对象。

其中,attrs对象接收遗漏的props中未声明的属性。(相当于vue2中的$attrs)

emit对象接收分发的自定义事件。(相当于vue2中的$emit)

slots对象接收传入包裹的slot中未声明的DOM节点。(相当于vue2中 的$slots)

在这里插入图片描述

//App.vue
<template>
	<Demo @hello="handleHello"></Demo>
</template>
<script>
import {Demo} from './compnents/Demo'
export default {
	name : 'App',
	setup(){
		//方法
		function handleHello(value){
			alert('触发了Hello事件,我收到的参数是${value}')
		}
		return {handleHello} 
	}
}
</script>
//Demo.vue
<template>
	<button @click="test">测试触发一下Demo组件的hello事件</button>
</template>
<script>
import {ref} from 'vue'
export default {
	name : 'App',
	props:['msg'],
	emits:['hello'], //vue3中应该声明将触发的hello事件,否则会有警告
	setup(props,context){
		function test(){
			context.emit('hello',666) //不能用this,必须用context触发外部事件
		}
		return {test} 
	}
}
</script>

2. computed计算属性

在vue2中,计算属性是由其他属性派生出来的属性。在vue3中,计算属性写在setup中,并且需要再引入

<!-- vue2写法 -->
<template>
	姓: <input type="text" v-model="person.firstName">
	<br>
	名: <input type="text" v-model="person.lastName">
	<br>
	<span>全名: {{ fullName}}</span>
</template>
<script>
import {reactive} from 'vue'
export default {
	name : 'App',
	//计算属性
	computed: {
		fullName(){
			return this.person.firstName + '-' + this.person.lastName
		}
	},
	setup(props,context){
		let person = reactive({
			firstName : '张',
			lastName : '三'
		})
		return {person} 
	}
}
</script>

vue3写法

<!-- vue3写法 -->
<template>
	姓: <input type="text" v-model="person.firstName">
	<br>
	名: <input type="text" v-model="person.lastName">
	<br>
	<span>全名: {{ fullName}}</span>
</template>
<script>
import {reactive,computed} from 'vue'
export default {
	name : 'App',
	setup(props,context){
		let person = reactive({
			firstName : '张',
			lastName : '三'
		})
		//计算属性,由于fullName是person派生出的属性,建议放在person对象里新增
		person.fullName = computed(()=>{
			return person.firstName + "-" + person.lastName
		}
		)
		return {person} 
	}
}
</script>

上述代码中,computed是简写形式,只能读,不能修改。因此需要改成下面的完整形式

<!-- vue3写法 -->
<template>
	姓: <input type="text" v-model="person.firstName">
	<br>
	名: <input type="text" v-model="person.lastName">
	<br>
	全名: <input type="text" v-model="person.fullName">
</template>
<script>
import {reactive,computed} from 'vue'
export default {
	name : 'App',
	setup(props,context){
		let person = reactive({
			firstName : '张',
			lastName : '三'
		})
		//计算属性,完整形式,同时考虑读写
		person.fullName = computed({
			get(){
				return person.firstName + "-" + person.lastName
			},
			set(value){
				const nameAttr = value.split('-')
				person.firstName = nameAttr[0]
				person.lastName= nameAttr[1]
			}
		})
		return {person} 
	}
}
</script>

3. watch监视函数

在vue3中watch监视函数的用法如下:

<template>
	<span>{{sum}}</span>
	<button @click="sum++">点我加1</button>	
</template>
<script>
import {ref,watch} from 'vue'
export default {
	name : 'App',
	setup(props,context){
		let sum = ref(0)
		let person = reactive({
			name: '张三',
			age: 18,
			job:{
				salary:20
			}
		})
		//情况一:监听ref所定义的一个响应式数据
		watch(sum,(newValue,oldValue)=>{
			console.log('sum变了',newValue,oldValue)
		})
		//情况二:监视ref所定义的多个响应式数据,第一个参数配置成数组
		watch([sum,msg],(newValue,oldValue)=>{
			console.log('sum或msg变了',newValue,oldValue)
		})
		//情况三:希望可以立马执行watch的监视,而不用等变化后,
		//解决方式:第三个参数可以填配置信息,
		watch(sum,(newValue,oldValue)=>{
			console.log('sum变了',newValue,oldValue)
		},{immediate:true,deep:true})
		//情况四:监听reactive所定义的一个响应式数据
		//1.注意vue3这里有个bug,无法监控到oldValue的值
		//2.注意vue3中reactive绑定了深度监视,配置deep无法关闭深层监视
		watch(person,(newValue,oldValue)=>{
			console.log('person变了',newValue,oldValue)
		},{deep:false})
		//情况五:监听reactive对象中的某个属性,需要写匿名函数
		watch(()=>person.name,(newValue,oldValue)=>{
			console.log('person变了',newValue,oldValue)
		})
		//情况六:监听reactive对象中的某些属性,需要封装在数组中
		watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
			console.log('person的name和age变了',newValue,oldValue)
		})
		//特殊七:若监视的reactive对象中的某个对象,则必须开启deep深层监视的配置。
		watch(()=>person.job,(newValue,oldValue)=>{
			console.log('person的job变了',newValue,oldValue)
		},{deep:false})
		return {sum} 
	}
}
</script>

watchEffect函数

watchEffect函数可以智能判断属性的变化,不用指明监视哪个属性,监视的回调中用到哪个属性,就监视哪个属性。
类似于computed函数,不过watchEffect更注重过程,所以不用写返回值。

<script>
import {ref,watch} from 'vue'
export default {
	name : 'App',
	setup(props,context){
		let sum = ref(0)
		let person = reactive({
			name: '张三',
			age: 18,
			job:{
				salary:20
			}
		})
		watchEffect(()=>{
			const x1 = sum.value //用到哪个属性,就监测哪个
			console.log('watchEffect执行的回调执行了')
		})
		return {sum} 
	}
}
</script>

4. vue3里的声明周期

//待续…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值