组合式API-composition-api

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出去
    }

}

之前在vue2中在组件或者dom挂载ref,可以通过this.$refs获取,但是在vue3中的setup中不能使用this,所以通过ref获取,刚开始定义一个变变量btn,const btn =ref(null) ,这个btn必须与写在子组件或者dom节点的上ref='btn'是一致的,因为setup刚开始属于create,这时候是null,当dom节点挂载完成之后,写在组件或者子组件里的ref就会把null覆盖

例子

父组件

父组件
<template>
    <ChildVue ref="childRef" /> //这里的childRef必须与setup函数里面的一致
</template>
 
<script setup lang="ts">
import { ref } from 'vue';
import { onMounted } from 'vue';
import ChildVue from './Child.vue';
 
const childRef = ref(null);
console.log(childRef.value); // undefined。因为这里相当于create还没有挂载dom与组件实例
 
onMounted(() => {
    console.log(childRef.value); // Proxy {…}
    // 调用子组件方法
    childRef.value.foo(); // foo  //只能获取到子组件的foo方法,因为子组件只暴露这个方法
});
</script>
 
<style>
</style>

子组件

<template>child demo</template>
 
<script setup lang="ts">
const foo = () => {
  console.log("foo");
}
defineExpose({  //子组件只暴露出去这个方法
  foo
});
</script>
 
<style>
</style>

 

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 中使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 3的组合式API是一种新的编程模式,它使得在Vue组件中可以更灵活地组织和复用逻辑。下面是对Vue 3组合式API的介绍: 1. Composition API组合式API):Vue 3中引入了Composition API,它允许我们将逻辑按照功能进行组合,而不是按照生命周期钩子进行划分。这样可以更好地组织和复用代码。 2. setup函数:在Vue 3中,我们需要在组件中使用setup函数来定义组合式APIsetup函数在组件创建之前执行,并且接收两个参数:props和context。我们可以在setup函数中定义响应式数据、计算属性、方法等。 3. reactive函数:reactive函数是Vue 3中用来创建响应式数据的函数。我们可以使用reactive函数将普通对象转换为响应式对象,从而实现数据的双向绑定。 4. ref函数:ref函数是Vue 3中用来创建单个响应式数据的函数。与reactive函数不同,ref函数返回一个包装过的对象,我们需要通过.value属性来访问和修改数据。 5. computed函数:computed函数用来创建计算属性。与Vue 2中的计算属性类似,我们可以使用computed函数来定义一个依赖其他响应式数据的属性。 6. watch函数:watch函数用来监听响应式数据的变化。我们可以使用watch函数来执行一些副作用操作,比如发送网络请求或者更新DOM。 7. 生命周期钩子:在Vue 3中,生命周期钩子函数被废弃了,取而代之的是使用setup函数来处理组件的生命周期逻辑。我们可以在setup函数中使用onMounted、onUpdated等函数来模拟Vue 2中的生命周期钩子。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值