【学习笔记】vue3的新特性Composition API

1.Composition API

Composition API是一组低嵌入式函数式的API,他是我们能够更灵活地“组合”组件的逻辑。Composition API的灵感来自于React hooks,比mixin更强大。它可以提供代码逻辑的可复用性,从而实现于模板的无关性,同时函数式编程使代码的可压缩性更强,另外vue3的响应式模块可与其他框架组合。

2. setup函数

setup函数基于Composition API新特性提供了统一的入口,在vue3中,所有的组合API函数都在此使用,只在初始化时执行一次,setup函数会在create生命周期函数之前执行,如果setup函数返回对象,对象中的属性和方法,在<template>模板中可使用。

<template>
  <div class="home">
    {{ 'msg:' + msg }},{{ say() }}
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'Home',
  setup() {
    let msg = 'hello vue3';
    function say() {
      return '学习使我快乐';
    }
    return {
      msg,
      say
    }
  }
});
</script>

image.png

2.1 setup中的参数

setup中有两个参数,第一个:props,第二个:conText。

  • props:包含props配置声明且传入了的所有属性的对象,也就是父组件给子组件传值,使用props接收。第一个参数接收的就是props所有属性。
  • conText:返回的属性有attrs,slots,emit
    attrs: 包含没有在props配置中声明的对象,相当于(this.$attrs)。
    slots:包含所有传入的插槽的对象,相当于this.$slots。
    emit:用来分发自定义事件的函数,相当于this.$emit。

父组件

<template>
  <div>
    <Child name="子组件" :age="18" gender="男" @showMsg="showChildMsg">
      <div>{{msg}}</div>
    </Child>
    <div>{{msg}}</div>
  </div>
</template>

<script lang='ts'>
import { defineComponent, ref } from 'vue'
import Child from './../components/child.vue'
export default defineComponent({
  name:'setup-params',
  components: { Child },
  setup() {
    let msg:any = ref('')
    let showChildMsg = (val: string) => {
      msg.value = val
      // console.log(val)
      // msg = '666'
      // console.log(msg)
    }
    return {
      msg,
      showChildMsg
    }
  }
})
</script>
<style scoped>
</style>

子组件

<template>
  <div class="child">
    <h1>{{name}}</h1>
    <h2>{{age}}</h2>
    <slot></slot>
    <button @click="showMsg">展示信息</button>
  </div>
</template>

<script lang='ts'>
import { defineComponent } from 'vue'
export default defineComponent({
  name:'child',
  props: {
    name: {
      type: String,
      defalut: ''
    },
    age: {
      type: Number,
      defalut: 0
    }
  },
  setup(props, conText:any) {
    console.log(props.name)
    console.log(conText.attrs, conText.attrs.genter)
    console.log(conText.slots)
    console.log(conText.emit)
    const showMsg = () => {
      // console.log(666)
      conText.emit("showMsg", '我是子组件')
    }
    return {
      showMsg
    }
  }
})
</script>
<style scoped>
</style>

3. ref函数

ref函数可以将数据转化为响应式数据,一般用来定义一个基本类型的响应式数据。

<template>
  <div>
    这里是ref
    <br />
    {{num}}<button @click="increase" type="button">+</button>
  </div>
</template>

<script lang='ts'>
import { defineComponent, ref } from 'vue'
export default defineComponent({
  name:'ref',
  setup() {
    let num = ref(0);
    let increase = () => {
      num.value++
      console.log(num.value)
    }
    return {
      num,
      increase
    }
  }
})
</script>
<style scoped>
</style>

3.1 toRefs函数

toRefs可以将响应式对象中所有属性包装为ref对象,并返回包含这些对象的普通对象,比如在setup函数中返回一个使用扩展运算符对象的响应式数据,这时这个对象类型的属性不再是响应式数据,可以使用toRefs将对象中的每个属性都转换成响应式的。

<template>
  <div>
    {{name}}→{{time}}
    <br/>
    <button @click="change">换个项目</button>
  </div>
</template>

<script lang='ts'>
import { defineComponent, reactive, toRefs } from 'vue'
export default defineComponent({
  name:'torefs',
  setup() {
    let state = reactive({
      name: '跑步',
      time: '30min'
    });
    let change = () => {
      state.name = '游泳'
      state.time = '2h'
    }
    return {
      ...toRefs(state),
      change
    }
  }
})
</script>
<style scoped>
</style>

3.2 toRef函数

toRef为响应式对象上的某个属性创建一个ref引用,更新时引用对象会同步更新,注意如果通过toRef创建的数据修改时,不会触发视图界面的更新,因为toRef的本质是引用,与原始数据关联。

<template>
  <div>
    {{obj.total}} <button @click="update">更新</button>
  </div>
</template>

<script lang='ts'>
import { defineComponent, reactive, toRef } from 'vue'
export default defineComponent({
  name:'toref',
  setup() {
    let obj = reactive({
      total: 10
    })
    let update = () => {
      console.log(666)
      let num:any = toRef(obj, 'total')
      num.value++
    }
    return {
      obj,
      update
    }
  }
})
</script>
<style scoped>
</style>

3.3 使用customRef

customRef用于自定义一个ref,可以显示的控制依赖追踪和触发响应,接受一个工厂函数,两个参数分别用于追踪的track与用于触发响应式的trigger,并返回一个带有get和set属性的对象,在实际开发中可以实现防抖函数。

4. reactive函数

reactive()的用法与ref()用法相似,也是将数据变成响应式数据,当数据发生变化时模板视图也会自动更行。不同的是reactive用于引用类型的数据,对象数组等。

<template>
  <div>
    这里是reavtive
    <br />
    <ul>
      <li
        v-for="item in category"
        :key="item.id"
      >{{item.title}}</li>
    </ul>
    <button @click="pushEl">添加元素</button>
  </div>
</template>

<script lang='ts'>
import { defineComponent, reactive } from 'vue'
export default defineComponent({
  name:'reactive',
  setup() {
    let category = reactive([
      { id: 1, title: '小学' },
      { id: 2, title: '中学' },
      { id: 3, title: '大学' }
    ])
    let pushEl = () => {
      category.push({ id: 4, title: '研究生'})
    }
    return {
      category,
      pushEl
    }
  }
})
</script>
<style scoped>
</style>

注意:ref也可以用于引用数据类型,它的底层也封装了reactive

5. hook

自定义hook钩子函数的作用类似于vue2的mixin,是一个可复用的功能函数,自定义hook的优势可以很清楚知道复用功能代码的来源。

1.创建hook.ts文件

import { ref, onMounted, onBeforeUnmount } from "vue"
//自定义hook,hook就是钩子函数,命名规范通常用use开头
export function useMousePosition() {
  // 坐标初始化  
  let x = ref(0)
  let y = ref(0)
  let mousePosition = (event: MouseEvent) => {
    x.value = event.pageX
    y.value = event.pageY
  }
  onMounted(() => {
    document.addEventListener('click', mousePosition)
  })
  onBeforeUnmount(() => {
    document.removeEventListener('click', mousePosition)
  })
  return {
    x,
    y
  }
}
  1. 导入hook.ts中的功能函数
<template>
  <div>
    <h5>hook的使用</h5>
    <div>鼠标的位置 x:{{x}},y:{{y}}</div>
  </div>
</template>

<script lang='ts'>
import { useMousePosition } from './../hook/hook'
import { defineComponent } from 'vue'
export default defineComponent({
  name:'hook',
  setup() {
    let postion = useMousePosition()
    let { x, y } = postion
    return {
      x,
      y
    }
  }
})
</script>
<style scoped>
</style>

6. shallowReactive与shallowRef

6.1 shallowReactive

shallowReactive只处理对象最外层属性的响应式也就是浅层响应式。

6.2 shallowRef

shallowRef只处理基本数据类型的响应式,不进行引用类型的响应式。

7. provide和inject组件通讯

provide和inject可以轻松实现跨层级组件通讯,provide在父子组件中返回要传给下级的数据,inject在需要使用这个数据的子辈组件或者孙辈组件等下级组件中注入数据。

父子组件

<template>
  <div></div>
  <inject-son></inject-son>
</template>

<script lang='ts'>
import { defineComponent, provide, ref } from 'vue'
import InjectSon from './../components/InjectSon.vue'
export default defineComponent({
  name:'provide',
  components: { InjectSon },
  setup() {
    let text = ref('你好!')
    provide('msg', text)
  }
})
</script>
<style scoped>
</style>

子组件

<template>
  <div>{{accMsg}}我是子组件</div>
  <InjectGrandSom></InjectGrandSom>
</template>

<script lang='ts'>
import { defineComponent, inject } from 'vue'
import InjectGrandSom from './InjectGrandson.vue'
export default defineComponent({
  name:'injectson',
  components: { InjectGrandSom },
  setup() {
    let accMsg = inject<any>('msg')
    return {
      accMsg
    }
  }
})
</script>
<style scoped>
</style>

孙组件

<template>
  <div>{{accMsg}}我是孙组件</div>
</template>

<script lang='ts'>
import { defineComponent, inject } from 'vue'
export default defineComponent({
  name:'injectgrandson',
  setup() {
    let accMsg = inject<any>('msg')
    return {
      accMsg
    }
  }
})
</script>
<style scoped>
</style>

最终

image.png

8. 响应式数据的判断

如果要检查一个数据是否为响应式数据可以通过isRef、isReactive、isReadonly、isProxy方法实现。

  • isRef:检查一个值是否为一个ref对象。
  • isReactive:检查一个对象是否是由reactive创建的响应式代理。
  • isReadonly:检查一个对象是否有readonly创建的只读代理。
  • isProxy:检查一个对象是否由reactive或者readonly方法代理创建。

9. 组件间的双向数据绑定

vue3和vue2实现组件之间的双向绑定的方式不太一样,其原因是因为v-model内部的实质变化,vue2的v-model默认属性名是value,事件名是input,vue3的默认属性名是modelValue,事件名是update:modelValue。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容