一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第二天,点击查看活动详情。
这篇文章主要记录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的舞台
- 新的配置项,只在初始化的时候执行一次,
setup执行的时间是早于beforeCreate的,所以this是undefined - 组合所有的API,可以包括数据、方法、vue的各生命周期
- setup值是个函数,包含两个参数,如果返回值是个对象,那么对象中的属性可以在模板中使用
- setup中写的生命周期钩子函数执行时间是早于配置项中写的钩子函数的
下图中setup中的DOM打印时间在befroeMount之后在mounted之前
setup的两个参数
props:组件外部传递过来且组件内部接收了的参数,是响应式的,属性在父组件中修改后组件会及时更新;不可以解构;解构之后将不再是响应式的;可以通过toRefs属性解决
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暴露出去的内容,所有保护了组件的封装性
上图中:组件里面定义了msg1,但是没有return出去所以在父组件获取组件实例的时候看不到;而可以看到组件实例中的其他内容
上图中:使用了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
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.refs;所以需要使用vue中暴露的ref
- 定义一个常量,使用ref,传入null
- return出去这个常量
- 使用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>