Vue3的Composition API

本文详细介绍了Vue3的计算属性与监视,生命周期,并与Vue2的写法做出分析对比。展示了自定义hook函数,toRef与toRefs的详细代码示例;也介绍不常用的Composition API

文章目录

        • 1、computed计算属性
        • 2、watch监视函数
          • 1)Vue2的监视属性写法:
          • 2)Vue3的监视属性写法:
            • 情况1:监视一个或多个ref的响应式数据,可正确获取newValue,oldValue,
            • 情况2:监视reactive的响应式数据的全部属性
            • 情况3:监视reactive所定义的一个响应式数据中的某个属性
            • 情况4:监视reactive所定义的一个响应式数据中的某些属性
            • 情况5:特殊情况,此处监视的是reactive定义的对象的某个属性,所以deep配置有效
        • 3、watchEffect函数
        • 4、Vue3生命周期
        • 5、自定义hook函数
        • 6、toRef与toRefs
        • 7、shallowReactive 与shallowRef
        • 8、readonly与shallowReadonly
        • 9、toRaw与markRaw
            • 1)toRaw:
            • 2)markRaw:
        • 10、customRef
        • 11、provide与inject
        • 12、响应式数据的判断
        • 13、Composition API的优势
        • 14、新的组件
            • 1)Fragment
            • 2)Teleport
            • 3)Suspense
        • 15、其他
            • 1)全局API的转移
            • 2)其他改变
        • 16、getCurrentInstance

1、computed计算属性

1)计算属性-简写(没有考虑计算属性被修改的情况)

import {computed} from "vue";
person.fullName = computed(()=>{
  return person.firstName + '-' + person.lastName
})

2)计算属性-完整(考虑读和写)

person.fullName = computed({
  get(){
    return person.firstName + '-' + person.lastName
  },
  set(newValue){
    const nameArr = newValue.split('-')
    person.firstName = nameArr[0]
    person.lastName = nameArr[1]
  }
})
2、watch监视函数
1)Vue2的监视属性写法:
watch:{
  sum(newValue,oldValue){  //简单写法
    console.log(newValue,oldValue)
  },
  sum:{       //完整写法
    immediate:true,
    deep:true,
    handle(newValue,oldValue){
      console.log(newValue,oldValue)
    }
  }
},
2)Vue3的监视属性写法:

watch()函数的参数,参数1:监视的对象,参数2:监视的回调;参数3:监视的配置项

watch()函数的两个小“坑”:

1、监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)

2、监视reactive定义的响应式数据中某个属性时:deep配置有效

代码展示

情况1:监视一个或多个ref的响应式数据,可正确获取newValue,oldValue,
watch ([sum,msg],(newValue,oldValue)=> {
  console.log('sum变了',newValue,oldValue)
},{immediate:true})
情况2:监视reactive的响应式数据的全部属性

注意:1、此处无法正确获取oldValue; 2、强制开启深度监视(deep配置无效)

watch(pers,(newValue,oldValue)=>{
  console.log('pers变化',newValue,oldValue)
})

结果可看出oldValue与newValue值相等
在这里插入图片描述

情况3:监视reactive所定义的一个响应式数据中的某个属性
watch(()=>pers.age,(newValue,oldValue)=>{
  console.log('age变化',newValue,oldValue)
})
情况4:监视reactive所定义的一个响应式数据中的某些属性
watch([()=>pers.age,()=>pers.name],(newValue,oldValue)=>{
  console.log('name与age',newValue,oldValue)
})
情况5:特殊情况,此处监视的是reactive定义的对象的某个属性,所以deep配置有效
watch(()=>pers.job,(newValue,oldValue)=>{
  console.log('深度监视job',newValue,oldValue)
},{deep:true})
3、watchEffect函数

watch的套路是:既要指明监视的属性,也要指明监视的回调

wachEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性

wachEffect像computed:

​ 1)computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。

​ 2)而wachEffect更注重的是过程(回调函数的函数体),所以不用写返回值。

watchEffect(()=>{
  const x1 = sum.value
  const x2 = pers.job.j1.salary
  console.log('watchEffect的回调',x1,x2)
})
4、Vue3生命周期

1)、Vue3.0中可以继续使用Vue2.x中的生命周期钩子,但有有两个被更名:

beforeDestroy 改名为beforeUnmount

destroyed 改名为unmounted

2)、Vue3.0也提供了Composition API形式的生命周期钩子,与Vue2.x中钩子对应关系如下:

beforeCreate ===>setup()
created =======>setup()
beforeMount===>onBeforeMount
mounted=======>onMounted
beforeUpdate ===>onBeforeUpdate
updated=======>onUpdated
beforeUnmount ==>onBeforeUnmount
unmounted=====>onUnmounted

代码

import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted} from 'vue'
setup(){
  onBeforeMount(()=>{console.log('--onBeforeMount--')})
  onMounted(()=>{console.log('--onMounted--')}),
  onBeforeUpdate(()=>{console.log('--onBeforeUpdate--')}),
  onUpdated(()=>{console.log('--onUpdated--')}),
  onBeforeUnmount(()=>{console.log('--onBeforeUnmount--')}),
  onUnmounted(()=>{console.log('--onUnmounted--')})
  return{}
},
5、自定义hook函数

什么是hook:本质是一个函数,把setup函数中使用的Composition AP进行了封装。

类似于vue2.x中的mixin。

自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂。

代码实例:实现点击鼠标获取xy位置的功能

新建userPoint.js文件

import {onMounted,onBeforeUnmount,reactive} from 'vue'
export default function savePoint() { //实现鼠标打点
  let point = reactive({x:0,y:0})
  function savePoint(event){
    point.x = event.pageX
    point.y = event.pageY
    console.log(event.pageX,event.pageY)
  }
  onMounted(()=>{window.addEventListener('click',savePoint)})
  onBeforeUnmount(()=>{window.removeEventListener('click',savePoint)})
  return point
}

自定义hook函数引用

import usePoint from "../hooks/usePoint";
setup(){
  let point = usePoint()
  return{point}
}
6、toRef与toRefs

作用:创建一个ref对象,其value值指向另一个对象中的某个属性值

语法:const name =toRef(person,'name'

应用:要将响应式对象中的某个属性单独提供给外部使用时

扩展:toRefs 与toRef功能一致,但可以批量创建多个ref对象

语法:...toRefs(person)

代码

<h3>姓名:{{name}}</h3>
<h3>薪水:{{job.j1.salary}}</h3>
setup(){
  let person = reactive({
    name:'张三', 
    job:{j1:{salary:10}}
  })
  return{ ...toRefs(person),
  }
}
7、shallowReactive 与shallowRef

shallowReactive:只处理对象最外层属性的响应式(浅响应式)。

shallowRef:只处理基本数据类型的响应式,不处理对象的响应式。

​ *如果有一个对象数据,结构比较深,但变化时只是外层属性变化,使用shallowReactive。

​ *如果有一个对象数据,后续功能不会修改该对象中的属性,而是用新的对象来替换,使用shallowRef。

let person = shallowReactive({//只考虑第一层数据
  name:'张三',
  age:18,
  job:{
    j1:{salary:10}
  }
})
let num = shallowRef({  
    y:0  //处理对象类型无响应式
})
<h3>{{num.x}}</h3>   //只能进行整体替换
<el-button @click="num = {x:33}">切换num</el-button>
8、readonly与shallowReadonly

readonly:让一个响应式数据变为只读的(深只读);

shallowReadonly:让一个响应式数据变为只读的(浅只读);

应用场景:不希望数据被修改时:

person = readonly(person)  //person不会被改变
person = shallowReadonly(person)  //person第一层数据不会被改变
9、toRaw与markRaw
1)toRaw:

作用:将一个由reactive生成的响应式对象转为普通对象;

使用场景:用于读取响应式对象对应的普通对象,对这个普通对象的所有操作,不会引起页面更新;

const p = toRaw(person)
2)markRaw:

作用:标记一个对象,使其永远不会再成为响应式对象;

使用场景:

1.有些值不应被设置为响应式的,例如复杂的第三方类库等;

2.当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能;

function addCar() {
  let car ={name:'奔驰',price:40}
  person.car = markRaw(car)
}
10、customRef

作用:
1)用于自定义返回一个ref对象,可以显式地控制依赖追踪和触发响应,接受工厂函数;
2)两个参数分别是用于追踪的 track 与用于触发响应的 trigger,并返回带有 get 和 set 属性的对象;

注意:通过 customRef 返回的 ref 对象,和正常 ref 对象一样,通过 x.value 修改或读取值

实现防抖效果:

import {customRef} from 'vue'
setup(){//自定义一个ref:myRef
  function myRef(value,delay){
    let timer
    return customRef((track,trigger)=>{
      return{
       get(){
         track()  //通知vue追踪value的变化
         return value
       },
        set(newValue){
          value = newValue
          clearTimeout(timer)
         timer = setTimeout(()=>{
            trigger() //通知vue去重新解析模板
          },delay)
        }
      }
    })
  }
  let keyWord = myRef('hello',500) //使用自定义customRef
  return{keyWord}
}
11、provide与inject

作用:实观祖组件与后代组件的跨级通信

套路:父姐件有一个provide选项来提供数据,子组件有一个inject 选项来开始使用这些数据
Vue3的Composition API_第1张图片

//祖组件中
import {reactive,toRefs,provide} from 'vue'
setup(){
  let car = reactive({
    name:'奔驰',
    price:'40W'
  })
  provide('car',car)
  return{ ...toRefs(car)}
}
//后代组件中
import {inject,toRefs} from "vue";
setup(){
  let car = inject('car')
  console.log('car---',car)
  return{ ...toRefs(car)}
}

注:父子组件间传值还用props

父传子:

//父组件引用子组件
<box-card :card="card" :fromRouteName="fromRouteName" ></box-card>
//子组件接收
interface Props {
  card: CardInfo,
  fromRouteName:String
}

const props = withDefaults(defineProps<Props>(), {     //defineProps是编译宏,不用引入
  card: () =>
    reactive({
      url: '',
      description: '',
      type:'',
    }),
  fromRouteName:{
      type:String
  }
})
//子组件接受的数据在templete中使用
 <img :src="card.url" />--{{card.type}}--{{fromRouteName}}
//子组件在script的使用
props.fromRouteName
props.card.url
12、响应式数据的判断

isRef:检查一个值是否为ref对象;

isReactive:检查一个对象是否是reactive创建的响应式代理;

isReadonly:检查一个对象是否是readonly 创建的只读代理;

isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理;

import {isRef,isReactive,isReadonly,isProxy} from "vue";
isRef(sum)   //true或false
isReactive(person)
isReadonly(sum)
isProxy(sum)
13、Composition API的优势

1)Options APl 存在的问题(Vue2.x)

使用传统OptionsAPl (配置API)中,新增或者修改一个需求,就需要分别在data,methods,computed里修改。

2)Composition API 的优势

能更加优雅的组织代码,函数。让相关功能的代码更加有序的组织在一起。

14、新的组件
1)Fragment

在Vue2中:组件必须有一个根标签

在Vue3中:组件可以没有根标签,内部会将多个标签包含在内置的Fragment虚拟元素中

好处:减少标签层级,减小内存占用

2)Teleport

Teleport(传送)是一种能够将我们的组件html结构移动到指定位置的技术。

<teleport to="body">    //也可传给选择器
    <div v-if="isshow" class="mask">
        <div  class="dialog">
            <h3>我是一个弹窗</h3>
            <el-button @click="isshow = false">关闭弹窗</el-button>
        </div>
    </div>
</teleport>
3)Suspense

等待异步组件时渲染一些额外内容,用户体验更好

// import childSuspence from "./pages/SuspenseDemo/childSuspence";//静态引入
import {defineAsyncComponent} from 'vue'
const childSuspence = defineAsyncComponent(()=>import('./pages/SuspenseDemo/childSuspence'));//动态引入

使用Suspense包裹组件,并配置好default与fallback,Suspense的底层是slot

<div class="app">
    <h3>我是App组件</h3>
    <Suspense>
        <template v-slot:default>
            <child-suspence></child-suspence>
        </template>
        <template v-slot:fallback>
            <h3>加载中....</h3>
        </template>
    </Suspense>
</div>
15、其他
1)全局API的转移

Vue 2.x 有许多全局 API 和配置:

//注册全局组件
Vue.component('myButton',{
    data:()=>({count:0}),
    template:''
})
//注册全局指令
Vue.directive('focus',{
    inserted:el => el.focus()
})

Vue3.0中对这些 APl 做出了调整:将全局的 API,即:Vue.xxx 调整到应用实例(app)上,如下表:

2.x全局API(Vue) 3.x实例API(app)
Vue.confg.xxxx app.config.xxx
Vue.config.productionTip 移除
Vue.component app.component
Vue.directive app.directive
Vue.minix app.mixin
Vue.use app.use
Vue.prototype app.config.globalProperties
2)其他改变

a:data选项应始终被声明为一个函数

b:过渡类名的修改

//Vue2.x写法
.v-enter,
.v-leave-to{opacity:0}
.v-leave,
.v-enter-to{opacity:1}
//Vue3.x写法
.v-enter-from,
.v-leave-to {opacity:0}
.v-leave-from,
.v-enter-to{opacity:1}

c:移除keyCode作为v-on 的修饰符,同时不再支持config.keyCodes

d:移除v-on.native修饰符

父组件中绑定事件

<my-component v-on:close="handleComponentEvent"
    	   v-on:click="handleNativeClickEvent"	/>

子组件中声明自定义事件

<script>
    export default {emits:['close']}
</script> 

e:移除过滤器filter

16、getCurrentInstance

getCurrentInstance 只暴露给高阶使用场景,典型的比如在库中。强烈反对在应用的代码中使用 getCurrentInstance。请不要把它当作在组合式 API 中获取 this 的替代方案来使用。

import { getCurrentInstance } from 'vue'
 
const MyComponent = {
  setup() {
    // 获取当前组件的上下文,下面两种方式都能获取到组件的上下文。
const { ctx }  = getCurrentInstance();  //  方式一,这种方式只能在开发环境下使用,生产环境下的ctx将访问不到
const { proxy }  = getCurrentInstance();  //  方式二,此方法在开发环境以及生产环境下都能放到组件上下文对象(推荐)
  }
}

getCurrentInstance 只能在 setup 或生命周期钩子中调用
如需在 setup 或生命周期钩子外使用,请先在 setup 中调用 getCurrentInstance() 获取该实例然后再使用。

const MyComponent = {
  setup() {
    const internalInstance = getCurrentInstance() // 有效
 
    const id = useComponentId() // 有效
 
    const handleClick = () => {
      getCurrentInstance() // 无效
      useComponentId() // 无效
 
      internalInstance // 有效
    }
    onMounted(() => {
      getCurrentInstance() // 有效
    })
    return () =>
      h(
        'button',
        {
          onClick: handleClick
        },
        `uid: ${id}`
      )
  }
}
 
// 在组合式函数中调用也可以正常执行
function useComponentId() {
  return getCurrentInstance().uid
}

你可能感兴趣的:(vue.js,javascript,前端)