2

options-api

首先我们来看看 option-api。

default export {    
    data() {
        return {
            state:0
        }
    },
    methods:{
        myFun() {
            console.log(this.state)
        }
    }
}

上面代码的方式就是 option-api, 也是 vue2.x 中最常用的方式。

composition-api

在vue3.x中,可以使用下面的 composition-api 方式。

default export {    
    setup(){
    const state = ref(0)
    function myFunOne() {
      console.log(state.value)
    }
    function myFunTwo() {
      console.log(state.value + 1)
    }
    return {
      state,
      myFunOne
    }
    }
}

解决痛点

看下面的图片(来源:https://composition-api.vuejs.org/#code-organization),相同业务的代码颜色相同,可以看见 Options API 的相同业务代码分散在各处,这样后期维护起来就会非常麻烦,而Composition-Api 解决了这个痛点。
image.png

知识点梳理

关于 composition-api 所包含的函数有以上这么多,下面根据相关例子一个个来学习。

setup

setup功能是新的组件选项。它是组件内部使用Composition-API的入口。

可选参数

它有两个可选参数 props 和 context。
这两个参数可以解构使用。

  • props 和 vue2.x props是一样的,里面的属性是 父组件传给自组子或 vue-router 传给 页面的参数。
  • context具有与vue2.x 中 this.$attrs,this.$slots,this.$emit,this.$parent,this.$root对应的属性。

示例代码

  props: {
    msg: String
  },
  // 需要在 emits 中声明 不然会报错 下面会有解释
  emits: {
    sayhi: payload => {
      // validate payload by returning a boolean
      return payload
    }
  },
    // 1. 一般使用
    // setup(props, context) {
    //  console.log('====>', props)
    //  context.emit('sayhi', 'hi')
    //}
    // 2. 可解构使用
    setup({ msg }, { emit }) {
       console.log('====>', msg)
       emit('sayhi', 'hi')
    }

注意点⚠️

在 setup 中使用 emit , 需要在emits 中声明它,否则会报以下错误。

Extraneous non-emits event listeners (此处是函数名) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option.

ref

<template>
  <div>
    <div>{{ state }}</div>
    <button @click="addOne">新增1</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, onMounted } from "vue"
export default defineComponent({
  setup(){
    // 通过 ref 初始化一个响应式的对象
    const state = ref(0)
    
    // 在生命周期 onMounted  中打印 state
    onMounted(() => {
      console.log(state)
    })
    
    function addOne() {
        state.value += 1
    }
    
    return {
        state,
        addOne
    }
    }
})
</script>

我们再来看看这段代码。
在 dom挂载完毕的生命周期 onMounted  中打印 state , 发现它是一个对象。
image.png
因为 state 是通过ref 传参,然后返回的一个对象( ref 是一个函数,它接受一个参数,返回的一个响应式对象 )。我们初始化的这个 0 作为参数包裹到这个对象中去,在未来可以检测到改变并作出对应的相应。
addOne  函数中, 若要改变 这个 state 响应式对象的值, 则需要通过 赋值 state.value  来实现。
在 template 模板中 ,vue 已经帮我们自动获取了其 value属性,所以我们只需要传{{ state }}  即可。

reactive

reactive 和 ref 非常相似, 也是一个函数, 但是其接收一个对象。我们改造上面学习 ref 的 例子来了解 reactive。

<template>
  <div>
    <div>{{ state }}</div>
    <button @click="addOne">新增1</button>
  </div>
</template>

<script lang="ts">
import { defineComponent, onMounted, reactive, toRefs } from "vue"
export default defineComponent({
  setup(){
    // 通过 reactive 初始化数据
    const data = reactive({
      state: 0,
    })
    // const state = ref(0)
    // 在生命周期 onMounted  中打印 state
    onMounted(() => {
      console.log('无 toRefs', data)
      console.log('通过 toRefs 处理', toRefs(data))
    })
    
    function addOne() {
        data.state += 1
    }
    
    return {
        // ...data, // 这样导出会使数据失去响应式
        ...toRefs(data), // 通过 toRefs 使得 data 中数据获得响应式
        addOne
    }
    }
})
</script>

image.png
与 ref 相比:

  • reactive 可以将 零散的 变量聚集在一个对象中。
  • reactive 中的变量 的取值和赋值不需要 取其 value 属性。


注意点:
使用 reactive 时要记得使用 toRefs 保证 reactive  对象属性保持响应性。

watch 和 watchEffect

代码示例

// 通过 reactive 初始化数据
const data = reactive({
  state: 0,
  num: 2,
  arr: [1, 2, 3]
})
// 简单监听
watch(data, () => {
  console.log(data.state)
})
// 下面直接监听data.state会报错。watch 监听的目标只能是getter/effect函数、ref、reactive对象或数组。
// watch(data.state, (newValue, oldValue) => {
//   console.log('newValue', newValue)
//   console.log('oldValue', oldValue)
// })

// 监听 新的值和旧值
watch(() => data.state, (newValue, oldValue) => {
  console.log('newValue', newValue)
  console.log('oldValue', oldValue)
})
// watch 监听多个属性,返回的也是多个值的数组
watch([() => data.num, () => data.state], (newValue, oldValue) => {
  console.log('old', oldValue)
  console.log('new', newValue)
})
// watchEffect 不需要指定监听的属性 
watchEffect(()=>{
  console.log('watchEffect ===>', data.state);
});

watch

A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types. 

watch 监听源只能是getter/effect函数、ref、reactive对象或数组。
具体用法如上述代码所见

  1. 简单监听值的改变。
  2. 可以获取到新值与旧值。
  3. watch 只能监听指定的属性而做出变更,vue3.x可以同时监听多个属性。

watchEffect

watchEffect 和 watch 类似,可以监听属性的变化。具体使用方式如上述代码。

不同点

  1. watchEffect 不需要指定监听的属性,它会自动的收集依赖,只要我们回调中引用到了 响应式的属性, 那么当这些属性变更的时候,这个回调都会执行,而 watch 只能监听指定的一个或多个属性而做出变更。
  2.  watch  可以获取到新值与旧值,而 watchEffect是拿不到的 oldValue 
  3. watchEffect 在组件初始化的时候就会默认执行一次,而 watch 不需要。



computed

计算属性和vue2.x 中一样,代码示例如下。

<template>
  <div>
    <div>{{ state }}</div>
    <div>{{ double }}</div>
    <button @click="addOne">新增1</button>
  </div>
</template>

<script lang="ts">
import { computed, defineComponent, reactive, toRefs } from "vue"
export default defineComponent({
  setup(){
    // 通过 reactive 初始化数据
    const data = reactive({
      state: 0,
    })
    // 通过计算属性获得的新的值
    const double = computed(() => data.state * 2)
    
    function addOne() {
        data.state += 1
    }
    
    return {
        ...toRefs(data), // 通过 toRefs 使得 data 中数据获得响应式
        addOne,
        double
    }
    }
})
</script>

参考文献
composition-api 官方文档


邢走在云端
303 声望25 粉丝

you async, i await.