【Vue3】快速入门Vue3 -上篇
原创
©著作权归作者所有:来自51CTO博客作者苏凉zzyo的原创作品,请联系作者获取转载授权,否则将追究法律责任
文章目录
001.setup的配置
setup为vue3的配置项,可以再其中写vue2的数据,方法等。该函数的返回值为一个对象,返回的对象可以使用于模板解析
也可以在vue3中使用vue2中的配置项,但vue2可以访问vue3配置的数据,而vue3中不能访问vue2的数据。
且在数据名相同的情况下,模板解析以vue3的为先
<script>export default {
name: 'App',
setup(){
let name = 'su'
let age = 21;
let num = 1;
function add(){
alert(`${name},${age},${num}`)
}
return {
name,
age,
num,
add
}
}
}</script>
002.ref函数与reactive函数
- ref定义一个响应式的数据,语法
const name = ref('su')
, 这样su就为一个响应式的数据了,而name则变成了一个refimpl(应用实现的实例对象 - 简称:引用对象) 通过name.value
可以修改name的值。且为响应式的数据。 - 在模板中只需要调用{{name}}即可,vue3会自动在模板中给我们调用.value属性。
- ref函数接收的数据可以为:基本类型/对象。基本类型仍然通过
object.defineProperty
的get和set来实现。对象类型则通过vue3中的reactive函数来实现数据响应式。 - reactive 函数: 定义一个对象类型的响应式数据。
语法const 代理对象 = reactive(源对象)/数组
返回的是一个代理对象(proxy)
reactive定义的响应式数据是深层次的。内部基于es6的Proxy实现,通过代理对象操作源对象内部数据进行操作。
003.vue3响应式原理
回顾vue2响应式实现原理:
<script>let person = {
name :'su',
age:21
}
let p = {};
// 模拟vue2中的响应式 - 只有数据被读取或修改时为响应式,添加和删除时不为响应式。
Object.defineProperty(p,'name',{
get(){
console.log('数据被读取!!');
return person.name;
},
set(value){
console.log('数据被修改!!');
person.name = value;
}
})</script>
vue3实现响应式原理:
<script>let person = {
name :'su',
age:21
}
//模拟vue3中的响应式
let p = new Proxy(person,{
// 读取p的属性时调用
// target - 代理的源对象, propName - 读取/修改的属性
get(target,propName){
console.log('数据被读取!');
return Reflect.get(target,propName)
},
// 添加 / 修改 p的属性时 调用
set(target,propName,value){
console.log('数据被修改');
Reflect.set(target,propName,value)
},
// 删除p的属性时调用
deleteProperty(target,propName){
console.log('数据被删除!');
return Reflect.deleteProperty(target,propName);
},
})</script>
- 通过proxy(代理)拦截对象中任意属性的变化 - 属性的增删改查等
- 通过Reflect(反射)对被代理对象的属性进行操作
reactive对比ref
- 从定义角度:ref用来定义基本数据类型。reactive用于定义对象/数组类型数据
- 实现响应式原理:ref通过object.defineProperty的get和set来实现响应式(数据劫持)。reactive通过使用Proxy来实现,并通过Reflect操作源对象内部的数据。
- 从使用角度来说: ref操作数据需要用.value。读取时则直接使用。reactive在操作或读取数据时都不需要.value
004.setup的两个注意点
- setup的执行时期:在beforcreate之前执行,this为undefined
- setup的参数:
- 第一个参数 - props:值为对象,包含组件外部传递进来且组件内部声明接收的数据。
- 第二个参数 - context:上下文对象。attrs:值为对象,包含组件外部传递且在组件内部没有声明接收的数据,相当于vc身上的
this.$attrs
。slots:收到的插槽内容。相当于this.$slots
。emit:分发自定义事件的函数,用于触发自定义事件并传递数据,相当于this.$emit
005.vue3中的计算属性
与reactive函数一样,vue3中的计算属性函数也需要引入。 import {reactive,computed} from 'vue'
<script>setup(){
let data = reactive({
firstName:'三',
lastName:'张'
})
// vue3 的计算属性 - 接收一个回调函数 返回值 - 简写
// let fullName = computed(()=>{
// return `${data.lastName} - ${data.firstName}`
// })
// vue3 的计算属性 - 接收一个回调函数 返回值 - 完整写法
let fullName = computed({
get(){
return `${data.lastName}-${data.firstName}`
},
set(value){
let arrName = value.split('-');
data.lastName = arrName[0];
data.firstName = arrName[1]
}
})
return {
data,
fullName
}
}</script>
- 简写形式:在computed中传递一个回调函数,返回一个值作为声明变量的值。此时的计算属性只能读,不能改
- 完整形式:与vue2中的计算属性写法一致。有get和set。此时的计算属性可读可改
006.监视ref定义的数据
- 与reactive函数一样,watch同样在vue3中也是一个暴露出来的函数。在setup中直接调用即可;
watch可以接收三个参数:
- 第一个参数:要监听的数据
- 第二个参数:回调函数,接收两个参数,一个为监听的数据的新值,一个为旧值。
- 第三个函数:配置是否进行立即监听/深度监听。
<script>setup(){
let sum = ref(0);
let msg = ref('你好')
function add(){
sum.value = sum.value +1;
}
// vue3中的监视 - 第一个参数:监视的数据 第二个参数:回调函数 第三个参数:其余配置项-如immediate,deep等配置
// 监视一个数据时
// watch(sum,(newValue,oldValue)=>{
// console.log(`新值:${newValue},旧值:${oldValue}`);
// },{immediate:true})
// 监视两个数据时
watch([sum,msg],(newValue,oldValue)=>{
console.log(`新值:${newValue},旧值:${oldValue}`);
},{immediate:true})
return {
sum,
msg,
add
}
}</script>
007.监视reactive定义的数据
- 在vue3中监听reactive定义的数据时,当其中的数据发生改变,vue3不监听到旧的数据(oldValue无法正确读取)
- 强制开启了深度监视(配置deep:false无效)
- 监听reactive中的其中某个值时
需要将值写为一个回调函数,返回值为监听的属性值。监视多个值同理写在一个数组里 - 特殊情况
监听reactive中的属性值仍为一个对象时,需要开启深度监视,且oldValue仍无法正确读取。
<script>setup(){
let sum = ref(0);
let msg = ref('你好')
let person = reactive({
name:'zhangsan',
age:21,
job:{
j1:{
salary:12
}
}
})
// vue3监听reactive定义的数据 -vue3中当数据改变时无法监听到旧的数据。 -强制深度监视
watch(person,(newValue,oldValue)=>{
console.log('新值:',newValue,'旧值:',oldValue);
})
// 监视reactive中的某个属性值 - 第一个参数写成一个回调函数,返回值为需要监视的数据,多个值写在一个数组里
watch(()=>person.age,(newValue,oldValue)=>{
console.log('新值:',newValue,'旧值:',oldValue);
})
// 监视reactive中的一个属性,且改属性仍为一个对象 - 需要开启深度监视 ,但oldValue仍无法正确读取
watch(()=>person.job,(newValue,oldValue)=>{
console.log('新值:',newValue,'旧值:',oldValue);
},{deep:true})
return {
sum,
msg,
person
}
}</script>
008.watchEffect函数
与watch不同的是,watchEffect函数不用指明监视哪个属性,监视中的回调用到了哪个属性就监视哪个属性。且默认开启立即监视。
- watchEffect与computed有点类似。
所依赖的数据发生改变时,就会重新调用函数。
不同的是:computed更注重计算出来的值(函数的返回值),因此必须写返回值。而watchEffect更注重过程(函数的回调),因从不需要写返回值。
<script>setup(){
let sum = ref(0);
let msg = ref('你好')
let person = reactive({
name:'zhangsan',
age:21,
job:{
j1:{
salary:12
}
}
})
// 不用声明监视哪个数据,用到哪个数据就监视哪个。当数据发生改变时,重新执行该函数。默认开启立即监视
watchEffect(()=>{
let a = sum.value;
let b = person.job.j1.salary;
console.log('数据发生了改变!');
})
return {
sum,
msg,
person
}
}</script>
009.vue3中的生命周期
vue3的生命周期对比vue3的生命周期基本相同。最大的不同就是vue2中的销毁在vue3中变为了卸载。
其次是vue2中是创建实例之后即为生命周期的开始。在创建完毕之后才挂载元素。在vue3中是一切准备就绪后才进入生命周期。
vue2生命周期官方图示:
vue3生命周期官方图示:
在vue3中使用生命周期钩子有两种方式:
- 使用配置项,用法与vue2相同。
<script>import {ref} from 'vue'
export default {
name: 'App',
setup(){
let sum = ref(0);
return {
sum
}
},
// 使用配置项 写生命周期钩子
beforeCreate(){
console.log('beforeCreate');
},
created(){
console.log('created');
},
beforeMount(){
console.log('beforeMount');
},
mounted(){
console.log('mounted');
},
beforeUpdate(){
console.log('beforeUpdate');
},
updated(){
console.log('updated');
},
beforeUnmount(){
console.log('beforeUnmount');
},
unmounted(){
console.log('unmounted');
}
}</script>
- 使用组合式api,使用就引用,接收一个回调函数。在回调函数内写业务逻辑。
<script>import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
export default {
name: 'App',
setup(){
let sum = ref(0);
//使用组合式 API
onBeforeMount(()=>{
console.log('onBeforeMount');
}),
onMounted(()=>{
console.log('onMounted');
}),
onBeforeUpdate(()=>{
console.log('onBeforeUpdate');
}),
onUpdated(()=>{
console.log('onUpdated');
}),
onBeforeUnmount(()=>{
console.log('onBeforeMount');
}),
onUnmounted(()=>{
console.log('onUnmounted');
})
return {
sum
}
},
}</script>
010.自定义hook函数
在src目录下创建一个hooks文件夹,在该文件下写hook函数,当其他组件想使用时引入即可。即做到代码复用的效果。
- 本质为一个函数,把setup中的使用的组合式API进行封装。如:
import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function(){
// 获取坐标点相关的数据
let point = reactive({
x:0,
y:0
})
// 获取坐标点相关的方法
function savePoint(event){
point.x = event.clientX;
point.y = event.clientY
}
// 获取坐标点相关的生命周期钩子
onMounted(()=>{
window.addEventListener('click',savePoint)
})
onBeforeUnmount(() => {
window.removeEventListener('click',savePoint)
})
return point
}
封装好之后在任意一个组件中都可以使用。如:
<template>
<span>{{sum}}</span> <br>
<button @click="sum++">+1</button>
<hr>
<h2>当前鼠标坐标:X:{{point.x}},Y:{{point.y}}</h2>
</template>
<script>import {ref} from 'vue'
import usePoint from '../hooks/usePoint'
export default {
name: 'App',
setup(){
let sum = ref(0);
let point = usePoint()
return {
sum,
point
}
},
}</script>
<style>
</style>
- 类似于vue2中的mixin
- 自定义hook的优势:复用代码,让setup中的逻辑更清晰。
011.toRef和toRefs
作用:创建一个ref对象,其value的值指向另一个对象中的某个属性。
用法:
toRef
<script>import {reactive,toRef,toRefs} from 'vue'
export default {
name: 'App',
setup(){
let person = reactive({
name:'zhangsan',
age:21,
job:{
j1:{
salary:12
}
}
})
return {
name:toRef(person,'name'),
age:toRef(person,'age'),
salary:toRef(person.job.j1,'salary')
}
}
}</script>
toRefs
<script>import {reactive,toRef,toRefs} from 'vue'
export default {
name: 'App',
setup(){
let person = reactive({
name:'zhangsan',
age:21,
job:{
j1:{
salary:12
}
}
})
return {
...toRefs(person)
}
}
}</script>
应用:要将某个响应式对象中的某个属性单独提供给外部使用时。
扩展:toRefs与toRef的功能一致,但可以批量创建多个ref对象。