Vue3语法Composition Api

155 阅读6分钟

vue3新特性组合API(composition api)

如果我们的业务逻辑较为单一整页代码行数不超过200行,这种方式比较适合也便于阅读和理解。但是!但凡单个组件涉及的的功能过多,代码行数成千上万,Options Api这种模式就是噩梦。如果能把相关的代码逻辑集中管理起来就会好很多,这个问题呢尤大已经帮我们考虑到了,就有了现在大家看到的组合式API,也就是平常说的setup或者说composition API。

Setup

setup 新的组件选项是一个接收 props 和 context 可以返回一个对象的函数,作为组件内使用Composition API的入口点,创建组件实例,然后初始化props,紧接着调用setup函数。它会在beforeCreate钩子之前调用。

setup(props,context){
     return { ... }
}

参数

props

//MyBook.vue

export default {
    props: {
        title: String
    },
    setup(props) {
        console.log(props.title)
    }
}

context. context 是一个普通 JavaScript 对象,暴露了其它可能在 setup 中有用的值

//MyBook.vue

export default {
    setup(props, context) {
        //Attribute(非响应对象,等同于$attrs)
        console.log(context.attrs)
        
        //插槽(非响应式对象,等同于$slots)
        console.log(context.slots)
        
        //触发事件(方法,等同于$emit)
        console.log(context.emit)
        
        //暴露公共property(函数)
        console.log(context.expose)
    }
}
//MyBook.vue
export default {
    setip(props, {attrs, solts, emit, expose}) {
        ...
    }
}

返回值结合模板使用

如果 setup 返回一个对象,那么该对象的 property 以及传递给 setup 的 props 参数中的 property 就都可以在模板中访问到

<!-- MyBook.vue -->
<template>
    <div>{{ collectionNane }}: (( readersNunber }} ({ book.title }}</d1v>
</tenplate>
<script>
    import { ref, reactive } from 'vue'
    export default {
        props: {
            collectionNane: string
        },
        setup(props) {
            //模板中使用响应式数据,需用ref或reactive包裹
            const readersNunber = ref(0)
            const book = reactive({ title: 'vue 3 Guide' })
            
            //暴露给template
            return {
                readersNunber,
                book
            }
        }
    }
</script>

setup注意点

  • 由于在执行setup函数的时候,还没有执

  • 行Created生命周期方法,所以在setup 函数中,无法使用data和methods的变量和方法

  • 由于我们不能在setup函数中使用data和methods,所以Vue为了避免我们错误的使用,直接将setup函数中的this修改成了undefined

响应式

ref 用来将基本数据类型定义为响应式数据

import { ref } form "vue"
setup(props,context){
    //data 定义响应式属性
    const count = ref(1)
    return{
        count
    }
}

reactive 用来将引用类型定义为响应式数据

import { reactive } form "vue"
setup(props,context){
    const obj = reactive({
        name:'jack',
        age:18
    })
    return{
        obj
    }
}

toRefs 会将我们一个响应式的对象转变为一个普通对象,然后将这个普通对象里的每一个属性变为一个响应式的数据

import {toRefs} form "vue"
setup(props,context){
        const obj = reactive({
        name:'jack',
        age:18
    })
    return{
        ...toRefs(obj) //只用name而不是obj.name     就可以调用
    }
}

setup中执行方法

独立方法

为了避免了将功能逻辑都堆叠在setup的问题,可以将独立的功能写成单独的函数

export default {
    name: 'test',
    //外部方法使用
    let funObj = fun()
    
    //返回一个对象,对象属性在模板上显示
    return {
       funObj, 
    }
}

function fun() {
  const score = ref(99)
  return {
    score
  }
}

文件分离方法

将功能函数提取出来放在单独的.ts文件中

import { ref, reactive, toRefs } from "vue";

export const todoList = () => {
    let inputValue = ref('')
    const list = reactive([
      {
        name: 'javascript',
        price: '69.98'
      },
      {
        name: 'html',
        price: '88.88'
      },
    ])
    const addItem = () => {
      //获取输入框内容
      list.push({ name: inputValue, price: 99.98 })
    }
  
    return {
      inputValue,
      list,
      addItem
    }
  }

vue3.2 之 script setup

vue3.2的版本特点

Vue3.2 中 只需要在 script 标签上加上 setup 属性,组件在编译的过程中代码运行的上下文是在 setup() 函数中,无需 return,template 可直接使用。

  • 变量方法无需return
  • 导入的组件无需注册,可直接使用
  • 发布Props和Emits
  • script setup与setup同时并存,分开写
<script>
    export default {
        name: 'CustomName',
        inheritAttrs: false,
        customOptions: {}
    }
</script>

<script setup>
    //script setup logic
</seript>
  • definExpose 暴露组件内部属性给父组件使用
<script setup>
    const a = 1
    const b = ref(2)
    
    defineExpose({
        a,
        b
    })
</script>

computed

<script setup>
    import { computed, ref } from 'vue'
    
    const count = ref(1)
    //通过computed获得doubleCount
    const doubleCount = computed(() => {
        return count.value * 2
    })
    //获取
    console.log( doubleCount.value)
</script>

watch

watch()是懒执行的:仅当数据源变化时,才会执行回调。但在某些场景中,我们希望在创建 侦听器时,立即执行一遍回调。

watch 函数接受 3 个参数:

  • 响应式引用或getter
  • 回调
  • 可选的配置选项
watch(
    message,
    () => {
        //反转
        reversMsg.value = message.value.split('').reverse().join('')
    },
    { immediate: true }//immediate: true立即执行
)

watchEffect()

用watchEffect 函数来简化上面的代码。watchEffect() 会立即执行一遍回调函数,如果这时函数产生了副作用,Vue 会自动追踪副作用的依赖关系,自动分析出响应源。上面的例子可以重写为:

watchEffect(() => {
    reversMsg.value = message.value.split('').reverse().join('')
})

组件通讯

props父传子

与选项式差别不大

图像.png

引入子组件,组件会自动注册

<template>
    <child name = 'jack' />
</template>

<script setup>
    import Child from "./Child.vue";
</script>

emit

图像 (2).png

图像 (3).png

ref 子组件实例和defineExpose

在标准组件写法里,子组件的数据都是默认隐式暴露给父组件的,但在 script-setup 模式下,所有数据只是默认 return 给 template 使用,不会暴露到组件外,所以父组件是无法直接通过挂载 ref 变量获取子组件的数据。

如果要调用子组件的数据,需要先在子组件显示的暴露出来,才能够正确的拿到,这个操作,就是由 defineExpose 来完成。

图像 (4).png

图像 (5).png

获取多个子组件实例:在 v-for 中获取子组件实例 这种情况仅适用于 v-for 循环数是固定的情况 ,因为如果 v-for 循环数 在初始化之后发生改变,那么就会导致 childRefs 再一次重复添加,childRefs 中会出现重复的子组件实例

<template>
    <div v-for="item in 3" :key="item">
        <child :ref='addChildRef' />
    </div>
</template>

<script setup>
    //子组件实例数组
    const childRefs = ref([])
    //通过 addChildRef 方法向 childRefs 添加子组件实例
    const addChildRef = (el) => {
        childRefs.value.push(el)
    }
</script>

插槽slot

图像 (6).png

图像 (7).png

路由useRoute和useRouter

<script setup>
    import Child from "./Child.vue";

    //必须先声明调用
    const route = useRoute()
    const router = useRouter()
    
    //路由信息
    console.log(route.query)
    
    //路由跳转
    router.push('/newPage')
</script>

路由导航守卫

<script setup>
    import { onBeforeRouteLeave,onBeforeRouteUpdate } from 'vue-router'
    
    //添加一个导航守卫,在当前组件将要离开时触发。
    onBeforeRouteLeave((to, from, next) => {
        next()
    })
    
    //添加一个导航守卫,在当前组件更新时触发。
    //在当前路由改变,但是该组件被复用时调用。
    onBeforeRouteUpdate((to,from,next) => {
        next()
    })
</script>

生命周期

通过在生命周期钩子前面加上 “on” 来访问组件的生命周期钩子。

图像 (8).png

Composition Api优点

更好的逻辑复用

组合式 API 最基本的优势是它使我们能够通过组合函数来实现更加简洁高效的逻辑复用。在选项式 API 中我们主要的逻辑复用机制是 mixins,而组合式 API 解决了 mixins 的所有缺陷。

更灵活的代码组织

许多用户喜欢选项式 API 的原因是它在默认情况下就能够让人写出有组织的代码:大部分代码都自然地被放进了对应的选项里。然而,选项式 API 在单个组件的逻辑复杂到一定程度时,会面临一些无法忽视的限制。这些限制主要体现在需要处理多个逻辑关注点的组件中,这是我们在许多 Vue 2 的实际案例中所观察到的。

更好的类型推导

更小的生产包体积

搭配 <script setup> 使用组合式 API 比等价情况下的选项式 API 更高效,对代码压缩也更友好。这是由于 <script setup> 形式书写的组件模板被编译为了一个内联函数,和 <script setup> 中的代码位于同一作用域。不像选项式 API 需要依赖 this 上下文对象访问属性,被编译的模板可以直接访问 <script setup> 中定义的变量,无需一个代码实例从中代理。这对代码压缩更友好,因为本地变量的名字可以被压缩,但对象的属性名则不能。