目录
四、composition api模块化、逻辑重用 (自定义函数Hook)
在Vue2.x中,我们实现一个功能,需要在data中新增数据,在methods/computed/watch中新增业务逻辑,数据和业务逻辑是分离的,不利于管理,为了解决这个弊端,Vue3.0推出了Composition API(组合API),也叫做注入API,实际上setup中定义的数据和业务逻辑,会注入到data和methods中
setup()是组合API的入口函数,可以直接在里面定义变量和方法(数据和业务逻辑),通过对象的形式返回暴露出去
composition api是vue3一系列新的api合集,主要有:
1、ref和reactive
2、computed和watch
3、新的生命周期函数
4、自定义函数Hook
关于vue3更多信息可以参考 https://v3.cn.vuejs.org/api/application-api.html
一、ref和reactive
setup()可以包含许多特性包括data、生命周期、watch、computed等等,注意该方法中无法访问this。
setup(){
const count = ref(0) //data类型
const increase = ()=>{
count.value++ //不能直接更改count
}
const double = computed(()=>{ computed计算属性
return count.value * 2
})
return { //需要将定义的数据返回
count,
increase,
double
}
}
多个数据可以使用reactive,但reactive数据不是响应式数据,需要用toRefs函数将其转化为ref格式/或者使用data.count等形式
setup(){
const data: DataProps = reactive({
count:0 ,
increase:()=>{
data.count++
},
double: computed(()=>{ //reactive更改变量值无需改变
return data.count*2
})
})
const refsData = toRefs(data)
return {
...refsData
}
}
报错,类型推断错误,需要给data设置定义接口
import { computed,reactive, toRefs } from 'vue'
interface DataProps {
count: number;
increase: () => void;
double: number;
}
有利于逻辑整合
优点:vue2的data无法监听属性的增加移除,vue3内通过proxy监听到这些
二、watch
const greetings = ref('')
watch(greetings, ()=>{//单个
//watch([data1,data2], ()=>{ //监听多个
//watch(()=>data.val1, ()=>{ //监听单个信息需要使用函数
document.title = greetings.value
})
return {
updateGreeting
}
三、生命周期
1、beforeUnmoint、unmounted替代beforeDestory、destoryed
在setup函数中如何处理生命周期的函数调用
选项 API | setup内部的钩子 |
---|---|
beforeCreate | 不需要 |
created | 不需要 |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
beforeCreate:表示组件刚刚被创建出来,组件的data和methods还没有初始化好setup
Created: 表示组件刚刚被创建出来,并且组件的data和methods已经初始化好
setup函数的注意点:
1、由于在执行 setup函数的时候,还没有执行 Created 生命周期方法,所以在 setup 函数中,无法使用 data 和 methods 的变量和方法
2、由于我们不能在 setup函数中使用 data 和 methods,所以 Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined
3、setup函数只能是同步的不能是异步的
新增调试用的debug函数
onRenderTracked
- 每次渲染后重新收集响应式依赖
- 在onMounted前触发,页面更新后也会触发
跟踪虚拟 DOM 重新渲染时调用。钩子接收 debugger event
作为参数。此事件告诉你哪个操作跟踪了组件以及该操作的目标对象和键。
它会跟踪页面上所有响应式变量和方法的状态,也就是我们用return返回去的值,它都会跟踪。只要页面有update的情况,它就会跟踪,然后生成一个event对象,我们通过event对象来查找程序的问题所在。
onRenderTriggered
-
每次触发页面重新渲染时自动执行
-
在onBeforeUpdate之前触发
当虚拟 DOM 重新渲染被触发时调用。和 renderTracked
类似,接收 debugger event
作为参数。此事件告诉你是什么操作触发了重新渲染,以及该操作的目标对象和键。
它不会跟踪每一个值,而是给你变化值的信息,并且新值和旧值都会给你明确的展示出来。onRenderTracked是如aoe一般对每个值都进行跟踪,
emit、solt、attr 在setup中用 context.emit|solt|attr 方法来替代
setup(props, context){
return {
// 要绑定的数据和方法
}
}
四、composition api模块化、逻辑重用 (自定义函数Hook)
- Vue3 的 hook函数 相当于 vue2 的 mixin, 不同在与 hooks 是函数
4.1 捕捉鼠标位置demo
在新建文件夹hooks下新建userMousePosition.ts
import {ref, onMounted, onUnmounted } from 'vue'
function userMousePosition(){
const x = ref(0)
const y = ref(0)
const updateMouse = (e: MouseEvent) =>{
x.value = e.pageX
y.value = e.pageY
}
onMounted(()=>{
document.addEventListener('click',updateMouse)
})
onUnmounted(()=>{
document.removeEventListener('click',updateMouse)
})
return {
x,
y
}
}
export default userMousePosition
页面引用,并导出x、y的值
import userMousePosition from '../hooks/userMousePosition'
export default {
name: 'Home',
setup(){
const {x , y} = userMousePosition()
return {
x,
y
}
}
};
4.2 调用接口demo
创建useURLLoader.ts文件
import { reactive, toRefs } from 'vue'
import axios from 'axios'
interface dataProps {
result: any;
loading: boolean;
loaded: boolean;
error: any;
}
function useURLLoader (url: string) {
const data: dataProps = reactive({
result: null,
loading: false,
loaded: true,
error: null
})
axios.get(url).then((rawData) => {
data.loading = false
data.loaded = true
data.result = rawData.data
}).catch(e => {
data.error = e
data.loading = true
data.loaded = false
})
const refsData = toRefs(data)
return {
...refsData
}
}
export default useURLLoader
页面引用
<template>
<div class="home">
<img v-if="loaded" :src="result.message" alt="">
</div>
</template>
<script lang="ts">
import useURLLoader from '../utils/useURLLoader'
import { computed, defineComponent, ref, reactive, toRefs, onRenderTracked, watch } from 'vue'
// import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src
interface DataProps {
count: number;
increase: ()=> void;
double: number;
}
export default defineComponent({
name: 'Home',
components: {
// HelloWorld
},
setup () {
const { result, loading, loaded } = useURLLoader('https://dog.ceo/api/breeds/image/random')
return {
result,
loaded
}
}
})
</script>
五、vue3对ts的支持
1、defineComponent定义组件
defineComponent函数,只是对setup函数进行封装,返回options的对象;
export function defineComponent(options: unknown) {
return isFunction(options) ? { setup: options } : options
}
defineComponent最重要的是:在TypeScript下,给予了组件 正确的参数类型推断 。
六、jsx语法
比如defineComponent实现
import { defineComponent } from 'vue'
const component = defineComponent({
name:'mouse',
setup(props, content) {
}
})
七、teleport(瞬移组件的位置)+父子组件传参
指定被包裹组件挂载在何处
<teleport to="#id"></teleport>
父子组件传参
<template>
<teleport to="#to">
<div id="center" v-if="isOpen">
<h2><slot>this is a model </slot></h2>
<button @click="buttonClick">关闭(向父组件传递数据)</button>
</div>
</teleport>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name:'Modal',
props:{
isOpen: Boolean
},
emits: {
'close-modal':(payload: any)=>{
return payload.type === 'close'
}
},
setup(props, context) {
const buttonClick = ()=>{
return context.emit('close-modal', {
type: 'hello'
})
}
return {
buttonClick
}
},
})
</script>
<style>
</style>
<template>
<div id="to" class="to">
<button @click="openModal">打开吧点数去</button>
<Modal :isOpen="modalIsOPen" @closeModal="closeModal">solt展示</Modal>
</div>
</template>
<script lang="ts">
import { ref } from 'vue';
import Modal from '../components/Modal.vue';
export default {
name:'DefineComponent',
setup() {
const modalIsOPen = ref(false)
const openModal = ()=>{
modalIsOPen.value = true
console.log('触发openModal',modalIsOPen.value )
}
const closeModal = ()=>{
modalIsOPen.value = false
}
return{
openModal,
modalIsOPen,
closeModal
}
},
components:{
Modal: Modal,
}
}
</script>
<style>
</style>
八、异步组件
如果要是用suspense,需要返回一个promise 。suspense希望能够处理异步问题,可以根据不同的情况渲染组件。
<template>
<h1>asyncshow组件</h1>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
name: 'AsyncSHow',
setup(){
return new Promise((resolve)=>{
setTimeout(()=>{
return resolve({
result:42
})
},3000)
})
}
})
</script>
<style>
</style>
<template>
<div>
<Suspense>
<template #default>
<AsyncSHow ></AsyncSHow>
</template>
<template #fallback>
<Modal :isOpen="true"></Modal>
</template>
</Suspense>
</div>
</template>
<script>
import AsyncSHow from '../components/asyncShow'
import Modal from '../components/Modal.vue';
export default {
components: {
AsyncSHow,
Modal
}
}
</script>
<style>
</style>
九、全局api更改
vue2的全局配置在一定程度上修改了vue的属性,容易污染环境。
vue.component =>app.component
vue.directive =>app.directive
vue.nextTick =>nextTick(()=>{})//需要从vue中引入nextTick
vue.mixin =>app.mixin
vue.use =>app.use