「Vue3学习篇」-Composition API及setup

907 阅读5分钟

『引言』

在传统的组件中,随着业务复杂度越来越高,代码量不断的增加,会导致整个代码的逻辑都不易阅读和理解。

Vue3 组合式 API(Composition API) 主要用在大型组件中提高代码逻辑的可复用性。

Vue3 使用组合式 API 的地方为 setup。Vue3 中使用 setup 定义响应式的数据和方法。

在 setup 中,我们可以按逻辑对部分代码进行分组,然后提取逻辑片段并与其它组件共享代码。因此,组合式 API(Composition API) 编写的代码更有条理。

『Composition API 的优势』

『Options API 存在的问题』

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

功能不能独立出来,如果功能特别多就会显得很乱。

看下面演示动画:

edafb39ca510194ddc3bf7e772731575.gif 由上面的演示动画可以看出,所有功能都拆散了。

d412ebc3e25db55328ba82c559f93812.gif

通过上面这个演示动画可以看到,data中保存着n个功能的数据,methods中保存着n个功能的方法,看起来十分的混乱。

『Composition API 的优势』

主要表现在,我们能够整洁的组织我们的代码,函数,让相关功能的代码更加有条理的组织在一起。

看下面演示:

af78457af245313a5b470440e33d929b.gif

eecff57b18483ba1d0d9fb236d820320.gif

根据以上动画,我们能很直观的看到 Composition API 的两大优点:代码逻辑的复用性和代码的整洁、清晰性。

『Options API 和 Composition API 举例对比』

通过不同方式实现如下效果:

01.png

通过上面的各个动画可以清晰明了的知道Composition API 的优势。

『Options API 实现』

//App.vue
<template>
  <DemoA  />
  <DemoB  />
</template>

<script>
import DemoA from './components/DemoA.vue'
import DemoB from './components/DemoB.vue'

export default {
  name: 'App',
  components: {
    DemoA,
    DemoB
  }
}
</script>
//components/DemoA.vue
<template>
  <h1>{{ message }}</h1>
</template>

<script>
export default{
name: 'DemoA',
data () {
   return {
   message: '我叫wnxx'
   }
}
mounted (){
  console.log("我叫wnxx")
}
</script>
//components/DemoB.vue
<template>
  <h1>{{ message }}</h1>
</template>

<script>
export default{
name: 'DemoB',
data () {
   return {
   message: '我叫pupu'
   }
}
mounted (){
  console.log("我叫pupu")
}
</script>

『Composition API 实现』

//App.vue
<template>
  <DemoA  />
  <DemoB  />
</template>

<script>
import DemoA from './components/DemoA.vue'
import DemoB from './components/DemoB.vue'


export default {
  name: 'App',
  components: {
    DemoA,
    DemoB
  }
}
</script>
//components/DemoA.vue
<template>
  <h1>{{ message }}</h1>
</template>

<script setup>
import useMessage from '../hooks/useMessage'
let message = useMessage('wnxx')
</script>
//components/DemoB.vue
<template>
  <h1>{{ message }}</h1>
</template>

<script setup>
import useMessage from '../hooks/useMessage'
let message = useMessage('pupu')
</script>
//hooks/useMessage.js
import { onMounted, ref } from "vue"
export default function (val) {
  onMounted(() => {
    console.log(`我叫${val}`)
  })
  return ref(`我叫${val}`)
}

Composition API 实现方式可以明显的看出一个优点那就是hooks函数,后面会有详细的介绍。

『setup 组件』

『setup特性及注意点⚠️』

  1. setup函数是 Composition API(组合API)的入口
  2. setup函数中定义的变量和方法,最后都是需要 return 出去的,不然无法在模板中使用。
  3. setup函数执行于beforeCreate和created之前,也就是说setup函数里面无法使用data和methods方法中的数据。
  4. 解决了在vue2中,因为data和methods方法相距太远,导致组件之间无法复用的问题。
  5. script setup模式下,组件不需要注册,直接挂载
  6. 在模板中也可以自动获得自定义的指令。
  7. setup函数只能是同步的不能是异步的。

『setup的执行时机』

beforeCreate() {
    console.log('beforeCreate执行了');
  },
setup() {
    console.log('setup执行了');
    return {};
  },

setup执行.png

  1. 可以通过console.log看到setup是在beforeCreate生命周期之前执行的(只执行一次),setup执行在创建实例之前,beforeCreate之前执行一次, 此时组件对象还没有创建,this是undefined
  2. 不能通过this来访问data/computed/methods/props
  3. 其实所有的composition API相关回调函数中也都不可以

『setup的返回值』

  1. 一般都返回一个对象: 为模板提供数据,也就是模板中可以直接使用此对象中的所有属性/方法
  2. 返回对象中的属性会与data函数返回对象的属性合并成为组件对象的属性
  3. 返回对象中的方法会与methods中的方法合并成功组件对象的方法
  4. 如果有重名, setup优先

🌟🌟注意: 一般不要混合使用: methods中可以访问setup提供的属性和方法, 但在setup方法中不能访问data和methods

🌟🌟注意: setup不能是一个async函数: 因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性数据

『setup的参数』

setup() 函数接收两个参数 props 和 context。
第一个参数 props,它是响应式的,当传入新的 prop 时,它将被更新。
第二个参数 context 是一个普通的 JavaScript 对象,它是一个上下文对象。里面有如下三个值:
可以通过 es6 语法解构 setup(props, {attrs, slots, emit})
1. attrs:值为对象,包含组件外部传递过来,没有在props配置中声明的属性的对象,相当于this.$attrs。
2. slots:包含所有传入的插槽内容的对象,相当于this.$slots。
3. emit:用来分发自定义事件的函数,相当于this.$emit。
🌟🌟注意🌟🌟: 在 setup 中你应该避免使用 this,因为它不会找到组件实例。
setup 的调用发生在 data property、computed property 或 methods 被解析之前,
所以它们无法在 setup 中被获取。

『attrs和props示例』

//父组件
<template>
 <h1>父组件</h1>
 <child :msg="msg" msg2='wnxx'></child>
</template>

<script setup>
import { ref } from 'vue'
// 引入子组件 
import Child from './components/Child.vue'

export default {
name: 'App', 
components: { Child },
const msg = ref('hello,setup')
}
</script>
//子组件
<template>
 <h2>子组件</h2>
 <h3>msg:{{ msg }}</h3>
</template>

<script>
export default {
name: 'Child', 
props: ['msg'],
    setup(props, {attrs, slots, emit}) { 
    console.log('props:', props)
    //msg: "hello,setup" 
    console.log('attrs:', attrs)
    //msg2: "wnxx" 
    return {}
    }
}
</script>

『emit示例』

//父组件
<template>
 <h1>父组件</h1>
 <child @show="show"></child>
</template>

<script setup>
// 引入子组件 
import Child from './components/Child.vue'

export default {
name: 'App', 
components: { Child },
    const show = () => { 
    console.log('name:', 'wnxx')
    }
}
</script>
//子组件
<template>
 <h2>子组件</h2>
 <button @click='emitBtn'>按钮</button>
</template>

<script>
export default {
name: 'Child', 
    setup(props, { emit }) { 
        const emitBtn = () => { 
        emit('show')
        }
        return { 
        emitBtn
        }
    }
}
</script>

『setup的使用』

setup() 来写组合式 API 时

内部定义的属性和方法,必须使用 return 暴露到上下文,外部才能够使用,否则就会报错。

写法为:

<template>
  <h1>{{ message }}</h1>
</template>

<script>
export default {
    setup(){
    import useMessage from '../hooks/useMessage'
        let message = useMessage('wnxx')
        return {
        message
        }
    }
}
</script>

使用 script setup 语法糖时

不需要 return 和 setup函数,只需要全部定义到 script setup 内。

写法为:

<template>
  <h1>{{ message }}</h1>
</template>

<script setup>
import useMessage from '../hooks/useMessage'
let message = useMessage('wnxx')
</script>

setup 函数示例

简单做一个小案例,完成如下效果:

setup示例.png

第一种方式

<template> 
<p>你喜欢的歌手是:{{selected}}</p> 
<button v-for="(item, key) in data.singers" 
:key="key" 
@click="selectSinges(key)">{{item}}
</button> 
</template> 
<script lang="ts"> 
import { reactive, ref} from "vue"
export default {
name: "SingesSelect", 
    setup() { 
        const data = reactive({ 
            singers: ["周杰伦", "汪苏泷", "李荣浩"] 
        })
        const selected = ref("")
        const selectSinges = (params: number): void => { 
            selected.value = data.singers[params]
        } 
        return { 
            data, 
            selected, 
            selectSinges
        } 
    } 
}

第二种方式

<template>
  <div>
    <p>你喜欢的歌手是:{{ selected }}</p> 
    <button v-for="(item, key) in data.singers" 
    :key="key" 
    @click="selectSinges(key)">{{item}}
    </button> 
  </div> 
</template>

<script setup lang="ts">
 import { reactive, ref } from 'vue'
      const data = reactive({ 
          singers: ["周杰伦", "汪苏泷", "李荣浩"] 
      })
      const selected = ref("")
      const selectSinges = (params: number): void => { 
          selected.value = data.singers[params]
      } 
</script>

这两种方式只是在写法上有一点的区别,一种使用setup()来写,另一种使用script setup语法糖,总体来说差别不是很大。