Vue3(一)

436 阅读7分钟

一、创建Vue3工程

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

二、Vue3中的响应式

1、setup函数

在vue3中不再像vue2一样需要多个配置项组合(选择式API),换了一种方式把所有的都放到setup中,setup相当于就是一个大舞台,把所有的配置项都往里面写

setup函数有两种返回值:1、返回一个对象 2、返回一个渲染函数,

export default {
	name: 'App',
	setup() {
		const name = '张三'
		const age = 18
		return {
			name,
			age,
		}
 	}
}

因为setup中访问不到vue2中的属性方法(但2可访问3),如果混用的话,有重名setup优先。

2、ref函数

作用:定义响应式数据

	import { ref } from 'vue'
	const name = ref('张三')
    const age = ref(18)

在js中操作数据:count.value

ref定义数据类型可以是对象也可以是其他复杂一点的,操作时都要 .value

const person = ref({name: 'zzy', age: 18})
person.value.name = 'ktton'
3、reactive函数

作用:定义一个对象类型的响应式数据(基本数据类型用ref就可以了)

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

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

Vue3相当于Vue2减少了this操作,不用干什么都要先this指向再进行操作

4、vue3中响应式的原理

对于ref和reactive是不一样的,ref简单类型是通过:Object.defineProperty()的get与set,当然啊,ref定义的复杂类型是通过reactive的Proxy,

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

不管怎么样,总结来说,vue3中新增的就是对于复杂数据类型通过Proxy实现响应式,也就是两个点: 1、通过 Proxy(代理): 拦截对象中任意属性的变化, 包括:属性的增删、属性值的读写等。

​ 2、通过Reflect(反射):对源对象进行上述操作

(1)Vue2响应式原理

vue2是通过Object.defineProperty()对对象类型数据进行getter setter来监视数据,但是问题也随之而来,直接的去进行新增属性、删除属性界面不会更新,直接通过下标修改数组,界面也不会更新

(2)Vue3中的Proxy

在vue3中解决了vue2中的两个问题

 let person ={
        name:'crt',
        age :18
    }

    const  p = new Proxy(person,{
        get(target,propName){
            console.log(`有人读到了这些数据`,target,propName);
            // target 是读取到传入的数据
            // propName 是读取到的是谁
            return target[propName]
        },
        set(target,propName,value){
            // set修改
            console.log(`有人修改了person中的${propName},修改后的值为:${value}`);
            target[propName] = value
        },
        deletePropenrty(target,propName){
            console.log(`有人删除了person中的${propName}`);
            return delete target[propName]
        }
    })

image-20230527204013039.png

要是想要更加严谨一些,可以通过Reflect来进行增珊改查,就不需要去捕获错误(让框架少报红)


    let person ={
        name:'crt',
        age :18
    }

    const  p = new Proxy(person,{
        get(target,propName){
            console.log(`有人读到了这些数据`,target,propName);
            // target 是读取到传入的数据
            // propName 是读取到的是谁
            // return target[propName]
            return Reflect.get(target,propName)
        },
        set(target,propName,value){
            // set修改
            console.log(`有人修改了person中的${propName},修改后的值为:${value}`);
            // target[propName] = value
            return Reflect.set(target,propName) 
            
        },
        deletePropenrty(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函数的执行时机是在beforeCeate之前,在所有生命周期的最前面,这个时候this的指向是undefined,所有setup里面不能使用this来访问组件实例

(2)setup参数

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

props: ['name', 'age']
setup(props, context) {
  console.log(props) // Proxy{name:'ktton',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(){
    let fullName = computed(()=>{
        return person.firstName + '-' + person.lastName
    })
}

如果想要修改计算属性,需要通过get/set的写法来进行修改

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函数

跟vue2的功能一样,就换了个写法

基本数据:

<template lang="">
   <h2>当前求和:{{sum}}</h2>
   <h2>信息:{{msg}}</h2>
   <button @click="sum++">点我+1</button>
   <h2>工作:{{person.job.type}}</h2>
   <h2>薪水:{{person.job.salary}}</h2>
    <button @click="person.job.type+='!'">点击修改</button>

</template>
<script>
    import {ref,reactive,watch} from 'vue'
export default {
    name:'App',
    setup(){
        let sum = ref(0)
        let msg = ref('hello')
        const person = reactive({
            name :"crt",
            age :18,
            job:{
                type:'IT',
                salary: '10K'
            }
        })

一、监视参数

(1)监听ref定义响应式数据

   // watch监听 
         watch(sum,(newValue,oldValue)=>{
         console.log(`值进行了变化`,oldValue,newValue); //监听单个ref响应数据
         })

(2)监听多个ref定义数据

//监听多个ref数据
            watch([sum,msg],(newValue,oldValue)=>{
            console.log('sum值或msg进行变化',newValue,oldValue)
            },{immediate:true}) //立即监听

(3)监听reactive定义的响应式数据

监听reactive数据的问题:

1、第一个是reactive定义的数据,监视时回调中无法获得oldValue!oldValue和new一样

2、第二个是,监视reactive定义的数据,默认开启的deep:true,且deep不能改成false

// 监听reactive数据
            watch(person,(newValue,oldValue)=>{
                console.log('发生了变化',newValue,oldValue);
            },{immediate:true})

(4)监听reactive定义数据中的某个属性(指定)

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

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

(5)监听reactive定义中的某些属性(一些)

watch([()=>person.job,()=>person.name],(newValue,oldValue)=>{
	console.log('person的job变化了',newValue,oldValue)
},{immediate:true})
二、如果我是使用ref去定义对象数据类型那么我监视的时候要不要写.value
import {ref,reactive,watch} from 'vue'
export default {
    name:'App',
    setup(){
        let sum = ref(0)
        let msg = ref('hello')
        const person = ref({
            name :"crt",
            age :18,
            job:{
                type:'code',
                salary: '100K'
            }
        })
        //要的

是需要的,ref去定义对象之类的,其实根本就是去借组reactive的Proxy实现响应式的

//如果是监听person中的某一个属性就要写.value
watch(person.value,(newValue,oldValue)=>{
	console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:false}) //此处的deep配置不奏效

//如果是监视大体,person就不用写.value
watch(person,(newValue,oldValue)=>{
	console.log('person变化了',newValue,oldValue)
},{immediate:true,deep:true}) 

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

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

    watchEffect(()=>{
        const x1 = sum.value
        const x2 = person.age
        console.log('watchEffect配置的回调执行了')
    })
    

可以说wathcEffect可以通过监视多个不同的属性不像watch只能监听一个

四、自定义hook

Vue 3 中的自定义 Hook 是一个函数,用于在组件之间共享逻辑代码。如果多个组件都需要类似的数据获取、事件处理、状态更新等操作,可以将这些操作封装在一个自定义 Hook 中,从而提高代码复用性,减少重复代码。


//我把下面这些逻辑操作封装到hook里面,然后暴露出来
//然后让app直接调用封装好的名称就好了


export const  p = new Proxy(person,{
get(target,propName){
console.log(`有人读到了这些数据`,target,propName);
// target 是读取到传入的数据
// propName 是读取到的是谁
// return target\[propName]
return Reflect.get(target,propName)
},
set(target,propName,value){
// set修改
console.log(`有人修改了person中的${propName},修改后的值为:${value}`);
// target\[propName] = value
return Reflect.set(target,propName)

        },
        deletePropenrty(target,propName){
            console.log(`有人删除了person中的${propName}`);
            // return delete target[propName]
            return Reflect.deleteProperty(target,propName)
        }
    }
    return p