你是否从还在焦虑从vue2过渡到vue3,那就看看这里

2,477 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第二天,点击查看活动详情

1.jpg

这篇文章主要记录vue3的基础知识点,提供给需要从vue2过渡到vue3的朋友,因为我也是需要从vue2过渡到vue3,所以在此记录学习过程,vue3知识点文章将会持续更新
祝愿看到文章的朋友身体健康;如果可以麻烦一键三连

说在前面

确保@vue/cli版本在4.5.0以上, vue --version查看版本

如果需要可以安装或者升级@vue/cli, npm install -g @vue/cli

模板内容写法改变

template中可以不用使用一个跟节点包裹内容

<template>
	<input type="text" v-model="keyword">
	<h3>{{keyword}}</h3>
</template>

setup——vue3的舞台

  1. 新的配置项,只在初始化的时候执行一次setup执行的时间是早于beforeCreate的,所以this是undefined
  2. 组合所有的API,可以包括数据、方法、vue的各生命周期
  3. setup值是个函数,包含两个参数,如果返回值是个对象,那么对象中的属性可以在模板中使用
  4. setup中写的生命周期钩子函数执行时间是早于配置项中写的钩子函数的

下图中setup中的DOM打印时间在befroeMount之后在mounted之前

image.png

setup的两个参数

props:组件外部传递过来且组件内部接收了的参数,是响应式的,属性在父组件中修改后组件会及时更新;不可以解构;解构之后将不再是响应式的;可以通过toRefs属性解决

image.png

image.png context:setup中不能访问this;所以提供context参数(上下文);可以访问vue中重要的属性attrs、emit、slots、expose;但是不能访问$refs、data、computed这些,但是这些都可以直接写到setup中,也就不需要通过context访问

setup中的attrs、emit、slots

attrs:对应vue2.x的$attrs,包含组件外部传递过来,但是没有在props中声明的属性

emit:对应vue2.x的事件分发$emit

slots:对应vue2.x的插槽$slots

setup中的expose

expose用于组件的封装性

通过ref我们是可以获取组件实例(或者DOM元素)的,组件实例中会包含组件的所有信息,包括DOM节点、数据、方法等(这里的数据、方法只能拿到在setup中return出去的属性);通过使用expose,我们只能获取到expose暴露出去的内容,所有保护了组件的封装性

image.png 上图中:组件里面定义了msg1,但是没有return出去所以在父组件获取组件实例的时候看不到;而可以看到组件实例中的其他内容

image.png 上图中:使用了expose之后,只能看到用expose暴露的信息;并且可以通过暴露出来的方法修改组件中的属性值;看下面的例子

expose举例

// 组件cm-message
<template>
    <div>{{observed.a}}{{msg}}</div>
</template>
<script>
import { reactive,ref } from "vue"
export default {
    name: 'Message',
    setup(props,{expose}) {
        const observed = reactive({
            a: 2
        })
        const msg = ref(10)
        function setObservedA(value) {
            observed.a = value;
            msg.value = value;
        }
        expose({
            setObservedA
        })
        return {observed,msg}
    }
}
</script>

使用组件;初始化时页面展示33;点击按钮时候页面展示55;所以是响应式的

<template>
    <cmMessage ref="message"></cmMessage>
    <button @click="clickBtn">点击</button>
</template>
<script>
import cmMessage from './components/cm-message.vue'
import { ref,onMounted } from "vue"


export default {
    name: 'App',
    components: {
        cmMessage
    },
    setup() {
        const message =ref(null)
        onMounted(() => {
            message.value.setObservedA(3)
        })
        function clickBtn() {
            message.value.setObservedA(5)
        }
        return {message,clickBtn}
    }
}
</script>

ref和reactive

vue2.x中基础数据类型和引用类型都是使用defineProperty实现的

vue3.x中是使用Proxy实现的

ref函数

  • 定义一个响应式的数据,返回一个包含响应式数据的引用对象(reference对象)
  • 在js中通过xxx.value获取数据,在模板中不需要使用.value
  • 可以接收基本数据类型也可以接收引用数据类型;对于基本数据类型,响应式依然是靠Object.defineProperty()的get与set完成的 ;对于引用数据类型,是基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作
  • 对于引用类型即使是使用ref函数定义,底层也是借助了reactive函数的,在js中访问的时候依然需要xxx.value

image.png

reactive函数

  • 定义一个引用类型的响应式数据(基本数据类型最好不用);返回一个代理对象(Proxy的实例对象,简称proxy对象)
  • reactive定义的对象响应是深层次的;表现为直接修改深层次的对象数据是响应式的
<template>
    <button ref="btn" @click="clickBtn">点击{{obj.b.b.a}}</button>
</template>


<script>
import {ref,reactive} from "vue"


export default {
    name: 'App',
    setup() {
        const msg = ref('这里是组件的双向绑定')
        const obj = reactive({
            a:1,
            b:{
                a:2,
                b:{
                    a:3
                }
            }
        })
        console.log(obj)
        console.log(msg)
        function clickBtn() {
            // 直接这样修改是可以的
            obj.b.b.a = 1
        }
        return {msg,obj,clickBtn}
    }
}
</script>

总结:

定义基本数据类型的数据使用ref函数,定义引用数据类型的数据用reactive函数

不要纠结ref函数是否可以定义引用数据类型的数据;(代码证明是可以的,并且会内部借助reactive函数转换为Proxy代理对象,所以直接使用reactive函数即可)即使可以也不要这样做,没有意义并且代码可读性差

Proxy响应式原理

  • 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
  • 通过Reflect(反射): 对源对象的属性进行操作
let person = {
    name:'张三',
    age:18
}
const p = new Proxy(person, {
	// 拦截读取属性值
    get (target, prop) {
        console.log(`有人读取了p身上的${prop}属性`)
    	return Reflect.get(target, prop)
    },
    // 拦截设置属性值或添加新属性
    set (target, prop, value) {
        console.log(`有人修改了p身上的${prop}属性,我要去更新界面了!`)
    	return Reflect.set(target, prop, value)
    },
    // 拦截删除属性
    deleteProperty (target, prop) {
        console.log(`有人删除了p身上的${prop}属性,我要去更新界面了!`)
    	return Reflect.deleteProperty(target, prop)
    }
})
person.name = 'tom'

通过ref获取组件或者DOM

在setup中没有this,也拿不到vue实例,所以不能再使用this.refssetup中的参数context上下文中也没有refs,setup中的参数context上下文中也没有refs;所以需要使用vue中暴露的ref

  1. 定义一个常量,使用ref,传入null
  2. return出去这个常量
  3. 使用ref绑定这个常量
<template>
    <cmMessage ref="message"></cmMessage>
    <button ref="btn">点击</button>
</template>
<script>
import cmMessage from './components/cm-message.vue'
import { ref,onMounted } from "vue"


export default {
    name: 'App',
    components: {
        cmMessage
    },
    setup() {
        const message =ref(null)
        const btn =ref(null)
        onMounted(() => {
            // 这里获取的是组件实例
            console.log(message)
            // 这里获取的是DOM
            console.log(btn)
        })
        return {message,btn}
    }
}
</script>

在v-for中使用ref

下面代码将打印出10个div的DOM节点

<template>
    <div v-for="item in 10" :key="item" :ref="ListRef">{{item}}</div>
</template>


<script>
export default {
    name: 'HelloWorld',
    setup() {
        const ListRef = (ref)=>{
            // 如果后面需要ref可以使用变量将他们存起来
            if(ref){
                console.log(ref)
            }
        }
        return {ListRef}
    }
}
</script>

image.png