1.Composition API
Composition API是一组低嵌入式、函数式的API,他是我们能够更灵活地“组合”组件的逻辑。Composition API的灵感来自于React hooks,比mixin更强大。它可以提供代码逻辑的可复用性,从而实现于模板的无关性,同时函数式编程使代码的可压缩性更强,另外vue3的响应式模块可与其他框架组合。
2. setup函数
setup函数基于Composition API新特性提供了统一的入口,在vue3中,所有的组合API函数都在此使用,只在初始化时执行一次,setup函数会在create生命周期函数之前执行,如果setup函数返回对象,对象中的属性和方法,在<template>模板中可使用。
<template>
<div class="home">
{{ 'msg:' + msg }},{{ say() }}
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
name: 'Home',
setup() {
let msg = 'hello vue3';
function say() {
return '学习使我快乐';
}
return {
msg,
say
}
}
});
</script>
2.1 setup中的参数
setup中有两个参数,第一个:props,第二个:conText。
- props:包含props配置声明且传入了的所有属性的对象,也就是父组件给子组件传值,使用props接收。第一个参数接收的就是props所有属性。
- conText:返回的属性有attrs,slots,emit
attrs: 包含没有在props配置中声明的对象,相当于(this.$attrs)。
slots:包含所有传入的插槽的对象,相当于this.$slots。
emit:用来分发自定义事件的函数,相当于this.$emit。
父组件
<template>
<div>
<Child name="子组件" :age="18" gender="男" @showMsg="showChildMsg">
<div>{{msg}}</div>
</Child>
<div>{{msg}}</div>
</div>
</template>
<script lang='ts'>
import { defineComponent, ref } from 'vue'
import Child from './../components/child.vue'
export default defineComponent({
name:'setup-params',
components: { Child },
setup() {
let msg:any = ref('')
let showChildMsg = (val: string) => {
msg.value = val
// console.log(val)
// msg = '666'
// console.log(msg)
}
return {
msg,
showChildMsg
}
}
})
</script>
<style scoped>
</style>
子组件
<template>
<div class="child">
<h1>{{name}}</h1>
<h2>{{age}}</h2>
<slot></slot>
<button @click="showMsg">展示信息</button>
</div>
</template>
<script lang='ts'>
import { defineComponent } from 'vue'
export default defineComponent({
name:'child',
props: {
name: {
type: String,
defalut: ''
},
age: {
type: Number,
defalut: 0
}
},
setup(props, conText:any) {
console.log(props.name)
console.log(conText.attrs, conText.attrs.genter)
console.log(conText.slots)
console.log(conText.emit)
const showMsg = () => {
// console.log(666)
conText.emit("showMsg", '我是子组件')
}
return {
showMsg
}
}
})
</script>
<style scoped>
</style>
3. ref函数
ref函数可以将数据转化为响应式数据,一般用来定义一个基本类型的响应式数据。
<template>
<div>
这里是ref
<br />
{{num}}<button @click="increase" type="button">+</button>
</div>
</template>
<script lang='ts'>
import { defineComponent, ref } from 'vue'
export default defineComponent({
name:'ref',
setup() {
let num = ref(0);
let increase = () => {
num.value++
console.log(num.value)
}
return {
num,
increase
}
}
})
</script>
<style scoped>
</style>
3.1 toRefs函数
toRefs可以将响应式对象中所有属性包装为ref对象,并返回包含这些对象的普通对象,比如在setup函数中返回一个使用扩展运算符对象的响应式数据,这时这个对象类型的属性不再是响应式数据,可以使用toRefs将对象中的每个属性都转换成响应式的。
<template>
<div>
{{name}}→{{time}}
<br/>
<button @click="change">换个项目</button>
</div>
</template>
<script lang='ts'>
import { defineComponent, reactive, toRefs } from 'vue'
export default defineComponent({
name:'torefs',
setup() {
let state = reactive({
name: '跑步',
time: '30min'
});
let change = () => {
state.name = '游泳'
state.time = '2h'
}
return {
...toRefs(state),
change
}
}
})
</script>
<style scoped>
</style>
3.2 toRef函数
toRef为响应式对象上的某个属性创建一个ref引用,更新时引用对象会同步更新,注意如果通过toRef创建的数据修改时,不会触发视图界面的更新,因为toRef的本质是引用,与原始数据关联。
<template>
<div>
{{obj.total}} <button @click="update">更新</button>
</div>
</template>
<script lang='ts'>
import { defineComponent, reactive, toRef } from 'vue'
export default defineComponent({
name:'toref',
setup() {
let obj = reactive({
total: 10
})
let update = () => {
console.log(666)
let num:any = toRef(obj, 'total')
num.value++
}
return {
obj,
update
}
}
})
</script>
<style scoped>
</style>
3.3 使用customRef
customRef用于自定义一个ref,可以显示的控制依赖追踪和触发响应,接受一个工厂函数,两个参数分别用于追踪的track与用于触发响应式的trigger,并返回一个带有get和set属性的对象,在实际开发中可以实现防抖函数。
4. reactive函数
reactive()的用法与ref()用法相似,也是将数据变成响应式数据,当数据发生变化时模板视图也会自动更行。不同的是reactive用于引用类型的数据,对象数组等。
<template>
<div>
这里是reavtive
<br />
<ul>
<li
v-for="item in category"
:key="item.id"
>{{item.title}}</li>
</ul>
<button @click="pushEl">添加元素</button>
</div>
</template>
<script lang='ts'>
import { defineComponent, reactive } from 'vue'
export default defineComponent({
name:'reactive',
setup() {
let category = reactive([
{ id: 1, title: '小学' },
{ id: 2, title: '中学' },
{ id: 3, title: '大学' }
])
let pushEl = () => {
category.push({ id: 4, title: '研究生'})
}
return {
category,
pushEl
}
}
})
</script>
<style scoped>
</style>
注意:ref也可以用于引用数据类型,它的底层也封装了reactive
5. hook
自定义hook钩子函数的作用类似于vue2的mixin,是一个可复用的功能函数,自定义hook的优势可以很清楚知道复用功能代码的来源。
1.创建hook.ts文件
import { ref, onMounted, onBeforeUnmount } from "vue"
//自定义hook,hook就是钩子函数,命名规范通常用use开头
export function useMousePosition() {
// 坐标初始化
let x = ref(0)
let y = ref(0)
let mousePosition = (event: MouseEvent) => {
x.value = event.pageX
y.value = event.pageY
}
onMounted(() => {
document.addEventListener('click', mousePosition)
})
onBeforeUnmount(() => {
document.removeEventListener('click', mousePosition)
})
return {
x,
y
}
}
- 导入hook.ts中的功能函数
<template>
<div>
<h5>hook的使用</h5>
<div>鼠标的位置 x:{{x}},y:{{y}}</div>
</div>
</template>
<script lang='ts'>
import { useMousePosition } from './../hook/hook'
import { defineComponent } from 'vue'
export default defineComponent({
name:'hook',
setup() {
let postion = useMousePosition()
let { x, y } = postion
return {
x,
y
}
}
})
</script>
<style scoped>
</style>
6. shallowReactive与shallowRef
6.1 shallowReactive
shallowReactive只处理对象最外层属性的响应式也就是浅层响应式。
6.2 shallowRef
shallowRef只处理基本数据类型的响应式,不进行引用类型的响应式。
7. provide和inject组件通讯
provide和inject可以轻松实现跨层级组件通讯,provide在父子组件中返回要传给下级的数据,inject在需要使用这个数据的子辈组件或者孙辈组件等下级组件中注入数据。
父子组件
<template>
<div></div>
<inject-son></inject-son>
</template>
<script lang='ts'>
import { defineComponent, provide, ref } from 'vue'
import InjectSon from './../components/InjectSon.vue'
export default defineComponent({
name:'provide',
components: { InjectSon },
setup() {
let text = ref('你好!')
provide('msg', text)
}
})
</script>
<style scoped>
</style>
子组件
<template>
<div>{{accMsg}}我是子组件</div>
<InjectGrandSom></InjectGrandSom>
</template>
<script lang='ts'>
import { defineComponent, inject } from 'vue'
import InjectGrandSom from './InjectGrandson.vue'
export default defineComponent({
name:'injectson',
components: { InjectGrandSom },
setup() {
let accMsg = inject<any>('msg')
return {
accMsg
}
}
})
</script>
<style scoped>
</style>
孙组件
<template>
<div>{{accMsg}}我是孙组件</div>
</template>
<script lang='ts'>
import { defineComponent, inject } from 'vue'
export default defineComponent({
name:'injectgrandson',
setup() {
let accMsg = inject<any>('msg')
return {
accMsg
}
}
})
</script>
<style scoped>
</style>
最终
8. 响应式数据的判断
如果要检查一个数据是否为响应式数据可以通过isRef、isReactive、isReadonly、isProxy方法实现。
- isRef:检查一个值是否为一个ref对象。
- isReactive:检查一个对象是否是由reactive创建的响应式代理。
- isReadonly:检查一个对象是否有readonly创建的只读代理。
- isProxy:检查一个对象是否由reactive或者readonly方法代理创建。
9. 组件间的双向数据绑定
vue3和vue2实现组件之间的双向绑定的方式不太一样,其原因是因为v-model内部的实质变化,vue2的v-model默认属性名是value,事件名是input,vue3的默认属性名是modelValue,事件名是update:modelValue。