1.setup
1.setup
:Vue3的新配置项,值为一个函数.
2.setup
时所有Composition API(组合APi)'表演舞台'
3.组件中所用到的:数据、方法等等,均要配置在setup中
4.setup函数的两种返回值
:
- 若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用
- 若返回一个渲染函数,则可以自定义渲染内容.
5.注意点:
1.尽量不要与Vue2配置混用
Vue2
配置(data、methods、computed...)中可以访问setup
中的属性、方法- 但在
setup
中不能访问到Vue2
配置(data、methods、computed...) - 如果有重名,
setup
优先
- 2.
setup
不能是一个async
函数,因为返回值不再是return
的对象,而是promise
,模板看不到return
对象中的属性.
6.setup
的语法糖 在<script></script>
标签里面 加上一个setup
可以不用return
直接在script
中写我们的代码
<script setup>
import { reactive } from 'vue'
const obj = reactive({
person:'张三'
})
</script>
2.ref函数
- 作用:定义一个响应式数据
语法:const xxx=ref(initValue)
- 创建一个包含响应式数据的引用对象(reference对象,简称ref对象)
- JS中操作数据:
xxx.value
- 模板中读取数据,不需要
.value
,直接<div>{{xxx}}</div>
备注
- 接收的数据可以是:基本类型,也可以是对象类型
- 基本类型的数据:响应式依然是靠
Object.defineProperty()
的get
与set
完成的 - 对象类型的数据:内部求助了Vue3的一个新函数---
reactive
函数
3.reactive函数
- 作用:定义一个对象类型的响应式数据(基本类型别用他,用r ef函数)
- 语法:
const 代理对象=reactive(被代理对象)
接收一个对象(或数组),返回一个代理器对象(proxy对象) - reactive定义的响应式数据时
深层次的
- 内部基于ES6的
Proxy
实现,通过代理对象操作对象内部数据都是响应式的
4.Vue3中的响应式原理
Vue2.x的响应式
实现原理:
- 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截、数据劫持
- 数组类型:通过重写更新数组的一系列方法实现拦截.(对数据的变更方法进行了包裹).
Object.defineProperty(data,'count',{
get(){},
set(){},
})
存在问题:
- 新增属性、删除属性,界面不会更新.
- 直接通过下标修改数组,界面不会自动更新.(.$set .$delete去处理)
Vue3的响应式
实现原理:
- 通过Proxy(代理):拦截对象中任意属性的变化,包括:属性值的读写、属性的添加.
- 通过Reflect(反射):对被代理的属性进行操作.
MDN文档中描述的Proxy与Reflect:
new Proxy(data, {
// 拦截读取属性值
get(target, prop) {https://www.baiyeovo.cn/admin/write-post.php?cid=290#wmd-editarea
return Reflect.get(target, prop)
},
// 拦截设置属性值或添加新属性
set(target, prop, value) {
return Reflect(target, prop, value)
},
//拦截删除属性
deleteProperty(target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
proxy.name = 'tom'
4.reactive对比ref
从定义数据角度对比:
- ref用来定义:基本数据类型
- reactive用来定义:对象(或数组)类型数据
- 备注:ref也可用来定义对象(或数组)类型数据,它内部会自动通过
reactive
转为代理对象.
从原理角度对比
- ref通过
Object.defineProperty()
的get
与set
来实现响应式(数据劫持). - reactive通过使用
Proxy
来实现响应式(数据劫持),并通过Reflect
操作源对象
内部的数据.
- ref通过
从实用角度来对比
- ref定义数据:操作数据需要
.value
,读取数据时模板中直接读取不需要.value
- reactive定义的数据:操作数据与读取数据,均不需要
.value
- ref定义数据:操作数据需要
5.setup的两个注意点
setup
执行的时机- 在
beforeCreated
之前执行一次,this
是undefined
.
- 在
setup
的参数props
:值为对象,包含:组件外部传递过来,且组件内部 声明接收了的属性context
:上下文对象attrs
:值为对象,包含:组件外部传递过来,但是没有在props
配置中声明的属性,相当于this.$attrs
.slot
:收到的插槽内容,相当与this.$slot
.emit
:分发自定义事件的函数,相当于this.$emit
.
6.计算属性与监视
- 与Vue2.x中的
computed
配置功能一致 - 写法
import {computed} from 'vue'
setup() {
//数据
let person = reactive({
firstName: '张',
lastName: '三'
})
//计算属性--简写(没有考虑计算属性被修改的情况 )
// person.fullNAme=computed(()=>{
// return person.firstName+ '-'+ person.lastName
// })
//计算属性--完整写法(考虑读和写 )
person.fullNAme = computed({
get() {
return person.firstName + '-' + person.lastName
},
set(value) {
const nameArr = value.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
return {
person
}
}
- 与Vue2.x中
watch
配置功能一致 两个小坑:
- 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置无效)
- 监视reactive定义的响应式数据中的某个属性时:
deep
配置有效
总结:
监测基本数据类型(有oldValue) 监测reactive定义的数据(没有oldValue)
//情况一:监测ref所定义的一个响应式数据
watch(sum,(newVal,oldVal)=>{
console.log('sum变了',newVal,oldVal)
},{immediate:true})
//情况二:监视ref所定义的多个响应式数据
watch([sum,msg],(newVal,oldVal)=>{
console.log('person变了',newVal,oldVal)
},{immediate:true})
// 情况三:监视reactive所定义的一个响应式数据,
// 1.注意:此处无法正确获取oldValue
// 2.注意:强行开启了深度监视(deep配置无效)
watch(person,(newVal,oldVal)=>{
console.log('person变了',newVal,oldVal)
},{deep:false})
//情况四:监视react所定义的一个响应式数据中的某个属性
watch(()=>person.age,(newVal,oldVal)=>{
console.log('person的age变了',newVal,oldVal)
})
// 情况五:监视reactive所定义的一个响应式数据中的某些属性
watch([()=>person.name,()=>person.age],(newVal,oldVal)=>{
console.log('person的jname或age变化了',newVal,oldVal)
})
//特殊情况
watch(()=>person.job,(newVal,oldVal)=>{
console.log('person的job.变化了',newVal,oldVal)
},{deep:true}) //此处由于监视的时reactive所定义的对象中的某个属性,所以deep配置有效
v-model语法糖
在vue2.0中v-mode语法糖简写的代码 <Son :value="msg" @input="msg=$event" />
在vue3.0中v-model语法糖有所调整:<Son :modelValue="msg" @update:modelValue="msg=$event" />
总结: vue3.0
封装组件支持v-model的时候,父传子:modelValue 子传父 @update:modelValue
补充: vue2.0
的 xxx.sync 语法糖解析 父传子 :xxx 子传父 @update:xxx 在vue3.0 使用 v-model:xxx 代替。
<template>
<div class="container">
<h2>子组件 {{modelValue}} <button @click="fn">改变数据</button></h2>
</div>
</template>
<script>
export default {
name: 'Son',
props: {
modelValue: {
type: Number,
default: 0
}
},
setup (props, {emit}) {
const fn = () => {
// 改变数据
emit('update:modelValue', 100)
}
return { fn }
}
}
</script>
watchEffect 函数
watch
的套路是:既要指明监听的属性,又要指明监听的函数watchEffect
的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性watchEffect
有点像computed
:- 但
computed
注重的计算出来的值(回调函数的返回值),所以必须要写返回值 - 而
watchEffect
更注重的是过程(回调函数的函数体),所以不用写返回值
- 但
watchEffect(()=>{
const x1=sum.value
const x2=person.age
console.log('watchEffect配置的回调执行了');
})
Vue3生命周期函数
Vue3.0 中可以继续使用Vue2.x中的生命周期钩子,但有两个被更名:
beforeDestroy
改名为beforeUnmount
destoryed
改名为Unmounted
Vue3.0也提供了Composition API形式的生命周期钩子,与Vue2.x钩子对应关系如下:
beforecreated
===>setup()
created
===>setup()
beforeMount
===>onBeforeMount
mounted
===>onMonunt
beforeUpdate
===>onBeforeUpdate
Updated
===>onUpdate
beforeDestroy
===>onBeforeUnmount
destoryed
===>onUnmounted
自定义hook函数
- 什么是
hook
---本质是一个函数,把setup中使用的composition API 进行了封装 - 类似于Vue2.x中的
mixin
- 自定义
hook
的优势:复用代码,让setup
中的逻辑更清楚易懂
toRef
- 作用:toRef是函数,转换响应式对象中某个属性为单独响应式数据,并且值是关联的。
- 语法:`const name=toRef(person,'name')
- 应用:要将响应式对象中的某个属性单独提供给外部使用
- 扩展:
toRefs
与toRef
功能一致,但可以创建多个ref
对象,语法:toRefs(person)
toRefs
toRefs
是函数,转换响应式对象中所有属性为单独响应式数据,对象成为普通对象,并且值是关联的
其他Composition API
1.shallowReactive与shallowRef
- shallowReactive:只处理对象最外层的响应式(浅响应式)
- shallowRef:只处理基本数据类型的响应式,不进行对象的响应式处理
什么时候使用?
- 如果有一个对象数据,解构比较深,但变化时只是最外层属性变化===>shallowReactive
- 如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换===>shallowRef
2.readonly与shallowReadonly
- readonly:让一个响应式数据变为只读(深只读)
- shallowReadonly:让一个响应式数据变为只读(浅只读)
- 应用场景:不希望数据被修改时
3.toRaw与markRaw
toRaw:
- 作用:将一个由
reactive
生成的响应式对象转为普通对象
- 使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新
- 作用:将一个由
markRaw
- 作用:标记一个对象,使其永远不会再成为响应式对象
- 应用场景:
1.有些值不应该被设置为响应式的,例如复杂的第三方类库等.
2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能.
4.customRef
- 作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行显示控制
- 实现防抖效果:
<template>
<div class="div">
<input type="text" v-model="keyWord" />
<h3>{{ keyWord }}</h3>
</div>
</template>
<script>
import { customRef } from 'vue'
export default {
setup() {
//自定义一个ref--名为:myRef
function myRef(value,delay) {
let timer=null
// console.log('myRef',value)
return customRef((track, trigger) => {
return {
get() {
console.log(`有人从myRef这个容器中读取了数据,我把${value}給它了`)
track() //通知vue追踪calue的变化(提前和get商量一下,让他认为这个value是有用的)
return value
},
set(newValue) {
console.log(`有人从myRef这个容器中该为了:${newValue}`)
clearTimeout(timer)
timer = setTimeout(() => {
value = newValue
trigger() //通知vue重新解析模板
},delay)
}
}
})
}
let keyWord = myRef('hello',500)
return {
keyWord
}
}
}
</script>
<style scoped>
.div {
width: 100px;
height: 100px;
}
</style>
5.provide与inject
- 作用:实现
祖孙组件间
通信 - 套路:父组件有一个
provide
,子组件有一个inject
选项来开始使用这些数据 具体写法:
1.祖组件中:setup(){ ..... let car=reactive({name:'奔驰',price:'40万'}) provide('car',car) ..... }
2.孙组件中:
setup(props,context){ ..... const car=inject('car') return{ car} .......
6.响应式数据的判断
- isRef:检擦一个值是否为一个ref对象
- isReactive:检查一个对象是否由
reactive
创建的响应式代理 - isReadonly:检查一个对象是否由
readonly
创建的只读代理 - isProxy:检查一个对象是否由
reactive
或者readonly
方法创建的代理
新的组件
1.Fragment
- 在vue2中:组件必须有一个根标签
- 在vue3中:组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中
- 好处:减少标签层级,减少内存占用
2.Teleport
- 什么时Teleport? ----
Teleport
是一种能够将我们组件html结构移动到指定位置的技术
<teleport top="移动位置">
<div v-if="isShow" class="mask">
<div class="dialog">
<h3>我是一个弹窗</h3>
<button @click="isShow=false">关闭弹窗</button>
</div>
</div>
<teleport>
3.Suspense
等待异步组件渲染时一些后备内容,获得更好的用户体验
- 异步引入组件
import {defineAsyncComponent} from 'vue' const Child =defineAsyncComponent(()=>import(./component/Child.vue))
- 使用
Suspense
包裹组件,并配置好default
与fallback
<template> <div> <h3>我是App组件</h3> <Suspense> <template #default> <Child></Child> </template> <template v-slot:fallback> <h3>加载中....</h3> </template> </Suspense> </div> </template>
Vue3自定义指令的生命周期
app.directive('directiveName', {
// 在绑定元素的 attribute 或事件监听器被应用之前调用, 在指令需要附加须要在普通的 v-on 事件监听器前调用的事件监听器时,这很有用
created() {},
// 当指令第一次绑定到元素并且在挂载父组件之前调用
beforeMount() {},
// 在绑定元素的父组件被挂载后调用
mounted() {},
// 在更新包含组件的 VNode 之前调用
beforeUpdate() {},
// 在包含组件的 VNode 及其子组件的 VNode 更新后调用
updated() {},
// 在卸载绑定元素的父组件之前调用
beforeUnmount() {},
// 当指令与元素解除绑定且父组件已卸载时, 只调用一次
unmounted() {},
});
vue3获取dom元素
获取单个元素
<template>
<div ref="myRef">获取单个DOM元素</div>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
setup() {
const myRef = ref(null);
onMounted(() => {
console.dir(myRef.value);
});
return {
myRef
};
}
};
</script>
获取多个dom元素(一般用于获取数组)
<template>
<div>获取多个DOM元素</div>
<ul>
<li v-for="(item, index) in arr" :key="index" :ref="setRef">
{{ item }}
</li>
</ul>
</template>
<script>
import { ref, nextTick } from 'vue';
export default {
setup() {
const arr = ref([1, 2, 3]);
// 存储dom数组
const myRef = ref([]);
const setRef = (el) => {
myRef.value.push(el);
};
nextTick(() => {
console.dir(myRef.value);
});
return {
arr,
setRef
};
}
};
</script>
最新回复