当前位置:网站首页>组合式API-composition-api

组合式API-composition-api

2022-06-23 03:55:00 tianruine

setup()

基本使用

原理就是之前我们写的options API写成了API-composition-api

首先我们创建一个vue后缀的文件名,然后里面的内容是这样

注意:在setup()不能访问this

<script>
import { ref } from 'vue' //从vue里面把组合Api解构出来
export default {
  data() {  //这还是之前的optioons API
    return {
      title: 'hello'
    }
  },

  setup() {  //在这个函数里面的使用就是组合API
    const counter = ref(0)

    // 不能访问 this
    // console.log(this.title)

    return {
      counter
    }
  },

  mounted() {
    console.log(this.counter)
  }
}
</script>

<template>
  <h1>{
   {counter}}</h1>
</template>

<style>
  
</style>

访问props

注意:在组合API ref或者reactive等等API的时候是具有响应式特点的,但是要是通过props从父组件中把这些具有响应是的数据传递给子元素,直接使用props.obj,这样的数据数据有响应式的,是指传递过来一个对象,这个对象有响应式,但是要是通过props传递过来的是一个值就没有响应式了,在子元素使用的时候是没有响应式特点的,要是想要让他具有响应式特点,可以通过toRef,或者toRefs进行传递

例子:在父元素

<script>
import { onMounted, reactive, ref, watchEffect,toRef } from 'vue'
import Child from './Child.vue'
export default {
  setup() {
    const title = ref('hello')

    const obj = reactive({
      x: 100,
      y: 200
    })
    onMounted(() => {
      setTimeout(() => { //计时器改变,看看子元素是否具有响应式
        title.value = 'world'
        obj.x = 1000
        obj.y = 2000
      }, 2000)
    })
    return {
      title,
      obj
    }
  },

  components: {
    Child
  }
}
</script>

<template>
  <Child :title="title" :obj="obj"></Child> //在这里通过自定义属性把父元素的值传递给子元素
</template>

在子组件中

<script>
import { toRef, toRefs } from 'vue'

export default {
  props: ['title', 'obj'], //在这里接收父组件传递过来的值

  setup(props) {
     const {obj} = props //在这里直接2解构props里面传递过来的对象是具有响应式的
    // const{title}=props//是里面的值就没有响应式
     //const obj=props.obj 传递过来的是一个对象就具有响应式了
    const x = toRef(props.obj, 'x')//之前写成const x = props.obj.x是没有响应式的,需要toRef来让他具有响应式
    const title = toRef(props, 'title')// const title = props.title没有响应式
    const { title: title2, obj: { value } } = toRefs(props)
    const y = toRef(value, 'y')//通过toRefs可以直接把props里面的值都让他具有响应式,但是对象也用的时候需要对里面的值在进行toRef或者toRefs

    return {
      
      title,
      title2,
      x,
     y,
      obj
      
    }
  }
}
</script>

<template>
  <h1>{
   {title}}</h1>
  <h1>{
   {title2}}</h1>
   <h1>{
   {x}}</h1>
     <h1>{
   {y}}</h1>
  {
   {obj}}

</template>

setup的上下文

1、attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于 this.$attrs

父子组件通信过程中,父组件把数据传递过来,如果子组件没有用props进行接收,就会出现在attrs中,而vm中没有

例如:在父组件传递过来的

在child组件中并没有用props接收,在setup里console.log(attrs);就是

 注意一点,在属性前面加冒号与不加冒号的区别是,加冒号传递过来的是父组件里面的变量,不加冒号就是字符串obj,字符串title,这也就是父子通信,自定义属性加冒号的原因,不加冒号传递的是属性的字符串值

2、emit: 分发自定义事件的函数, 相当于 this.$emit

在父元素中

在子组件中触发事件并且可以传值过去

3、slots: 收到的插槽内容, 相当于 this.$slots 

4.expose() 通过ref后代想要暴露给什么

例如,在父元素绑定子元素的ref

在子元素 setup函数中

那么在父元素去ref的时候只能拿到这个{x:100}

 与渲染函数一起用

import { h } from 'vue'
export default {
  setup() {
    return () => h('div', {class: 'abc'}, h('a', {href: 'http://www.baidu.com'}, '百度'))
  }   //h函数第一个参数是根节点,第二个参数是属性,第三个参数是子节点
}
</script>
<template>
</template>

页面结构

组合API

import { ref, onMounted, reactive, computed, readonly, watchEffect, watch } from 'vue'

ref 让属性具有响应式

一般里面写的都是基本数据类型

例子:

 setup() {
    const counter = ref({
      x: 100,
      y: {
        a: 300
      }
    })
onMounted(() => {
      setTimeout(() => {
        counter.value.x = 10000 //这里注意在setup函数里面用的时候必须.value
        counter.value.y.a = 30000
      }, 3000)
    })

    return {
      counter  //外面用的话必须return出去
    }

}

reactive

同样也是为了让数据具有响应式,一般写的都是复杂数据类型,

const count = ref(0)
    const obj = reactive({
      count
    })

    obj.count++
    console.log(obj.count)

    count.value = 100
    console.log(obj.count)

    obj.count = 200
    console.log(count.value)

    const books = reactive([ref('JavaScript学习指南')])
    books[0].value = 'html学习指南'
    console.log(books[0].value)

    const map = reactive(new Map([['count', ref(0)]]))
    console.log(map.get('count').value)
    map.set('count.value', 300)
    console.log(map.get('count').value)

computed() 计算属性

   const count = ref(0)
    const addOne = computed(() => count.value + 1) //里面写一个回调,当里面的值改变的时候就会触发
    console.log(addOne.value)
    count.value++
    console.log(addOne.value)

readonly()只读属性

const original = reactive({ count: 0 })
    const copy = readonly(original)
    original.count = 100 //但是赋值的变了,这个只读的也会变成100
    // copy.count = 200 //因为是只读的所以不可以改
    console.log(copy.count) //100

watchEffect()

有个特性,与mobx里面的autorun一样,在最开始就会执行

 const count = ref(0)
    const title = ref('line1')
    const stop = watchEffect(() => console.log(count.value + title.value)) // 总是要执行一次 
    stop()
    count.value = 100
    count.value = 100 //因为两次改变的值是一样的,所以就会执行一次
    title.value = 'line2'

watch() 监听

  const state = reactive({count: 0})
    watch(
      () => state.count, //变成了两个参数,第一个参数是函数,返回的是监听的值
      (count, prevCount) => {
        console.log(count)
        console.log(prevCount)
      }
    )

    state.count++

 watch与watcheffect区别:

  • 执行时机:watchEffect是立即执行的,在页面加载时会主动执行一次,来收集依赖;而watch是惰性地执行副作用,它不会立即执行,但可以配置 immediate,使其主动触发
  • 参数不同:watchEffect只需要传递一个回调函数,不需要传递侦听的数据,它会在页面加载时主动执行一次,来收集依赖;而watch至少要有两个参数(第三个参数是配置项),第一个参数是侦听的数据,第二个参数是回调函数
    结果不同:watchEffect获取不到更改前的值;而watch可以同时获取更改前和更改后的值
     

类型判断

import { ref, isRef, readonly, computed, reactive, unref, toRef, toRefs, isProxy, isReactive, isReadonly } from 'vue'

isRef 判断是不是ref类型

注意通过computed处理的ref类型也是ref类型

  const count = ref(0)
    const count2 = 0
    const count3 = readonly(count)
    const count4 = computed(() => count.valut + 2)
    const count5 = reactive({x: 0})
    console.log(isRef(count)) //1
    console.log(isRef(count2))//0
    console.log(isRef(count3))//0
    console.log(isRef(count4))//1
    console.log(isRef(count5))//0

unref() 不是ref类型的

unref(参数)

isReactive() 

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

 const msg = readonly(ref(2))    
    const msg1 = reactive({a:2})
    const msg2 = ref(2)
    const msg3 = {}
    console.log(isProxy(msg))//1
    console.log(isProxy(msg1))//1
    console.log(isProxy(msg2))//0
    console.log(isProxy(msg3))//0

isReadonly() 判断是不是readonly类型

const msg = readonly(reactive({}))
    const msg2 = ref(0)
    console.log(isReadonly(msg))//1
    console.log(isReadonly(msg2))//0

在setup函数的生命周期

- `beforeCreate` -> 使用 `setup()`
- `created` -> 使用 `setup()`
- `beforeMount` -> `onBeforeMount`
- `mounted` -> `onMounted`
- `beforeUpdate` -> `onBeforeUpdate`
- `updated` -> `onUpdated`
- `beforeDestroy` -> `onBeforeUnmount`
- `destroyed` -> `onUnmounted`

在setup函数中的provide与inject

在上层组件中

export default {
    components: {
      Child
    },

    setup() {
      const title = ref('hello') //通过这种方式传递是具有响应式的
       provide('title',title) //第一个是变量名,第二个是传的参数,要是传多个的时候title可以传递一个对象,到时候再取值,要是想传递多个变量就需要定义多个provide了
    }
  }}

在下层组件inject接收的时候

export default {
  setup() {
    //const title = inject('title', 'hello') r如果provide没有传值的时候第二个参数是默认值
    const title = inject('title') 通过inject拿到在provide定义的'title'变量
    return {
      title
    }
  }
}

setup函数的语法糖

只需要在<script setup></script>标签写上setup就可以了

例子1:

<script setup>
  import { ref, provide } from 'vue'
  import Child from './Child.vue'
//之前是下面这种写法,加上标签之后就没注释的写法,不用return出数据,不用components写子组件了
  // export default {
  //   components: {
  //     Child
  //   },

  //   setup() {  //
  //     const title = ref('hello')

  //     // provide('title')
  //   }
  // }
  const title = ref('hello')
  provide('title', title)
</script>
<template>
  <Child></Child>
</template>
<style>
</style>

当有参数,例如props的写法

<script>
import { toRef, toRefs } from 'vue'
export default {
  props: ['title', 'obj'],

 setup(props){
     const obj=props.obj
     console.log(props)
       const {title:title4}=props
    const x = toRef(props.obj, 'x')

    return {
      
      x,
      obj,
      title4
    // }
 }
    
}
  
}

</script>

<template>
   <h1>{
   {x}}</h1>
  {
   {obj.x}}
  {
   {title4}}
</template>

用语法糖的写法,不需要return,不需要export default,但是注意接收参数的写法props,用到defineProps(),可以写一个数组也可以写一个对象

<script setup>
import { toRef, toRefs } from 'vue'

 // props: ['title', 'obj'],

 const props = defineProps(['title', 'obj'])
     //const obj = props
     const obj=props.obj
     console.log(props)
       const {title:title4}=props
    const x = toRef(props.obj, 'x')
    

    // return {
      
    //   //title,
    //  // title2,
    //   x,
    //   obj,
    //   title4
    // }
  

</script>

<template>
   <h1>{
   {x}}</h1>
  {
   {obj.x}}
  {
   {title4}}
</template>

context中的参数的时候

defineProps 和 defineEmits

在 <script setup> 中必须使用 defineProps 和 defineEmits API 来声明 props 和 emits ,它们具备完整的类型推断并且在 <script setup> 中是直接可用的:

emit用defineEmits

<script setup>
const props = defineProps({
  foo: String
})

const emit = defineEmits(['change', 'delete'])
// setup code
</script>
  • defineProps 和 defineEmits 都是只在 <script setup> 中才能使用的编译器宏。他们不需要导入且会随着 <script setup> 处理过程一同被编译掉。

  • defineProps 接收与 props 选项相同的值,defineEmits 也接收 emits 选项相同的值。

  • defineProps 和 defineEmits 在选项传入后,会提供恰当的类型推断。

  • 传入到 defineProps 和 defineEmits 的选项会从 setup 中提升到模块的范围。因此,传入的选项不能引用在 setup 范围中声明的局部变量。这样做会引起编译错误。但是,它可以引用导入的绑定,因为它们也在模块范围内。

使用 <script setup> 的组件是默认关闭的,也即通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup> 中声明的绑定。

defineExpose

为了在 <script setup> 组件中明确要暴露出去的属性,使用 defineExpose 编译器宏:

expose()的写法

<script setup>
import { ref } from 'vue'

const a = 1
const b = ref(2)

defineExpose({
  a,
  b
})
</script>

useSlots 和 useAttrs

在 <script setup> 使用 slots 和 attrs 的情况应该是很罕见的,因为可以在模板中通过 $slots 和 $attrs 来访问它们。在你的确需要使用它们的罕见场景中,可以分别用 useSlots 和 useAttrs 两个辅助函数:

<script setup>
import { useSlots, useAttrs } from 'vue'

const slots = useSlots()
const attrs = useAttrs()
</script>

useSlots 和 useAttrs 是真实的运行时函数,它会返回与 setupContext.slots 和 setupContext.attrs 等价的值,同样也能在普通的组合式 API 中使用

原网站

版权声明
本文为[tianruine]所创,转载请带上原文链接,感谢
https://blog.csdn.net/m0_66195841/article/details/125397584