Vue ⑧-Vue3 | 组合式API

在这里插入图片描述

Vue3 预体验

需求:点击按钮,让数字 + 1

vue2 的 选项式 API:

<script>
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>

vue3 的 组合式 API:

<script>
import { ref } from 'vue'
const count = ref(0)
const increment = () => count.value++
</script>
  • 代码量变少了
  • 分散式维护转为集中式维护,更易封装复用

create-vue

create-vue 是 Vue 官方新的脚手架工具,底层切换到了 vite(下一代构建工具),为开发提供极速响应。

  • 前提环境条件:已安装 16.0 或更高版本的 Node.js

  • 创建一个Vue应用:npm init vue@latest,这一指令将会安装并执行 create-vue
    在这里插入图片描述

关键文件:

  1. vite.config.js - 项目的配置文件 基于 vite 的配置
  2. package.json - 项目包文件 核心依赖项变成了 Vue3.x 和 vite
  3. main.js - 入口文件 createApp 函数创建应用实例
  4. app.vue - 根组件 SFC 单文件组件 script - template - style
    变化一:脚本 script 和模板 template 顺序调整
    变化二:模板 template 不再要求唯一根元素
    变化三:脚本 script 添加 setup 标识支持组合式API
  5. index.html - 单页入口 提供 id 为 app 的挂载点

组合式 API

setup 选项的写法和执行时机

<script>
export default {
  setup() {

  },
  beforeCreate() {

  },
}
</script>

在这里插入图片描述

setup 选项中写代码的特点

<script>
export default {
  setup() {
    // 数据
    const message = 'Hello Vue 3'
    // 函数
    const logMessage = () => {
      console.log(message)
    }
    return {
      message,
      logMessage
    }
  },
  beforeCreate() {
    console.log('beforeCreate 函数')
  }
}
</script>

<template>
  <div>message: {{ message }}</div>
  <button @click="logMessage">Log Message</button>
</template>

<script setup> 语法糖

<script> 标签中,使用 <script setup> 语法糖,可以省略掉 export defaultreturn 关键字,直接在 script 标签中书写代码。

<script setup>
// 数据
const message = 'Hello Vue 3'
// 函数
const logMessage = () => {
  console.log(message)
}
</script>

<template>
  <div>message: {{ message }}</div>
  <button @click="logMessage">Log Message</button>
</template>

reactive()

作用:接受对象类型数据的参数传入并返回一个响应式的对象

<script setup>
// 1. reactive:接受一个对象类型的数据,返回一个响应式的对象,当数据发生变化时,视图会自动更新
import { reactive } from 'vue'
const state = reactive({
  count: 0
})
const setCount = () => {
  state.count++
}
</script>

ref()

作用:接收简单类型或者对象类型的数据传入并返回一个响应式的对象

<script setup>
// 1. ref:接受简单类型或者复杂类型,返回一个响应式对象
// 本质:是在原有传入数据的基础上,外层包了一层对象
// 底层:包成复杂类型之后,再借助 reactive 实现的响应式
// 注意:
// 1. ref 不能直接访问数据,需要通过 .value
// 2. 在 template 中 .value 不需要加(帮我们扒了一层)

// 推荐:以后声明数据,统一用 ref => 统一编码规范
import { ref } from 'vue'
const count = ref(0)

const setCount = () => {
  count.value++
}
</script>

computed计算属性函数

计算属性基本思想和 Vue2 的完全一致,组合式 API 下的计算属性只是修改了写法

核心步骤:

  • 导入 computed 函数
  • 执行函数 在回调参数中 return 基于响应式数据做计算的值,用变量接收
<script setup>
// const 计算属性 => computed(() => {
//   return 计算返回后的结果
//})

import { ref, computed } from 'vue'
// 声明数据
const list = ref([1, 2, 3, 4, 5, 6, 7, 8])

const computedList = computed(() => {
  return list.value.filter(item => item % 2 === 0)
})

const addFn = () => {
  list.value.push(Math.ceil(Math.random() * 100))
}
</script>

<template>
  <div>
    <div>原始数据:{{ list }}</div>
    <div>计算后的数据:{{ computedList }}</div>
    <button @click="addFn">修改</button>
  </div>
</template>

watch函数

作用: 侦听一个或者多个数据的变化,数据变化时执行回调函数

俩个额外参数:

  1. immediate(立即执行)
  2. deep(深度侦听)

侦听单个数据

  1. 导入 watch 函数

  2. 执行 watch 函数传入要侦听的响应式数据 (ref对象) 和回调函数

<script setup>
import { ref, watch } from 'vue'
const count = ref(0)

const changeCount = () => {
  count.value++
}

// 1. 监视单个数据的变化
watch(count, (newValue, oldValue) => {
  console.log('count', newValue, oldValue)
})

</script>

<template>
  <div>{{ count }}</div>
  <button @click="changeCount">+1</button>
</template>

侦听多个数据

同时侦听多个响应式数据的变化,不管哪个数据变化都需要执行回调

<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const nickname = ref('张三')

const changeCount = () => {
  count.value++
}
const changeNickname = () => {
  nickname.value = '李四'
}

// 2. 监视多个数据的变化
watch([count, nickname], (newArr, oldArr) => {
  console.log('watch', newArr, oldArr)
})

</script>

<template>
  <div>{{ count }}</div>
  <button @click="changeCount">+1</button>
  <div>{{ nickname }}</div>
  <button @click="changeNickname">修改昵称</button>
</template>

immediate

在侦听器创建时立即触发回调,响应式数据变化之后继续执行回调

<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const nickname = ref('张三')

const changeCount = () => {
  count.value++
}
const changeNickname = () => {
  nickname.value = '李四'
}

// 3. immediate 立即执行
watch(
  [count, nickname],
  (newArr, oldArr) => {
    console.log('watch', newArr, oldArr)
  },
  {
    immediate: true
  }
)

</script>

<template>
  <div>{{ count }}</div>
  <button @click="changeCount">+1</button>
  <div>{{ nickname }}</div>
  <button @click="changeNickname">修改昵称</button>
</template>

deep

默认机制:通过 watch 监听的 ref 对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启 deep 选项

const state = ref({count: 0})
watch(state, () => console.log('数据变化了'))

const changeStateByCount = () => {
  // 直接修改属性 => 不会触发回调
  state.value.count++
}
<script setup>
import { ref, watch } from 'vue'

// 4. deep 深度监视,默认 watch 进行的是 浅层监视
//    const ref1 = ref(简单类型) 可以直接监视
//    const ref2 = ref(复杂类型) 监视不到复杂类型内部数据的变化
const userInfo = ref({
  name: 'zs',
  age: 18
})

const setUserInfo = () => {
  // 此时修改了 userInfo.value 对象的地址,默认的 watch 才能监视到
  // userInfo.value = { name: 'ls', age: 19}

  userInfo.value.age++
  userInfo.value.name = 'ls'
}

watch(
  userInfo,
  (newValue) => {
    console.log('userInfo', newValue)
  },
  {
    deep: true
  }
)

</script>

<template>
  <div>{{ userInfo }}</div>
  <button @click="setUserInfo">修改userInfo</button>
</template>

精确侦听对象的某个属性

需求:在不开启deep的前提下,侦听 age 的变化,只有 age 变化时才执行回调

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

const userInfo = ref({
  name: 'zs',
  age: 18
})

const setUserInfo = () => {
  userInfo.value.age++
  userInfo.value.name = 'ls'
}

// 5. 对于对象中的属性,进行监视
watch(
  () => userInfo.value.age,
  (newValue, oldValue) => {
    console.log('userInfo.age', newValue, oldValue)
  }
)

</script>

<template>
  <div>{{ userInfo }}</div>
  <button @click="setUserInfo">修改userInfo</button>
</template>

Vue3 的生命周期 API

选项式API组合式API
beforeCreate/createdsetup
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted
<script setup>
import { onMounted } from 'vue'

// beforeCreate 和 created 的相关代码
// 一律放在 setup 中执行

const getList = () => {
  setTimeout(() => {
    console.log('getList')
  }, 2000)
}
// 一进入页面的请求
getList()

// 如果有些代码需要在 mounted 生命周期中执行
onMounted(() => {
  console.log('mounted生命周期函数')
})
// 写成函数的调用方式,可以调用多次,并不会冲突,而是按照顺序依此执行
onMounted(() => {
  console.log('mounted生命周期函数2')
})
</script>

组合式API - 父子通信

组合式API下的父传子

基本思想

  1. 父组件中给子组件绑定属性
  2. 子组件内部通过 props 选项接收

父组件

<script setup>
// 引入子组件
import sonComVue from './son-com.vue'

<template>
  {/* 绑定属性 message */}
  <sonComVue message="this is app message">
</template>

子组件

<script setup>
// 通过 defineProps "编译器宏" 接受子组件传递的数据
const props = defineProps({
  message: String
})
</script>

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

组合式API下的子传父

基本思想

  1. 父组件中给子组件标签通过@绑定事件
  2. 子组件内部通过 emit 方法触发事件

父组件

<script setup>
// 引入子组件
import sonComVue from './son-com.vue'

const getMessage = (message) => {
  console.log(message)
}
</script>

<template>
  {/* 绑定事件 changeMessage */}
  <sonComVue @get-message="getMessage">
</template>

子组件

<script setup>
// 通过 defineEmits 编译器宏生成 emit 方法
const emit = defineEmits(['get-message'])

const sendMessage = () => {
  // 触发自定义事件,并传递参数
  emit('get-message', 'this is son message')
}
</script>

<template>
  <button @click="sendMessage">发送消息</button>
</template>

模板引用

通过ref标识获取真实的dom对象或者组件实例对象

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

// 模板引用(可以获取到 dom,也可以获取组件)
// 1. 调用 ref函数,生成一个 ref对象
// 2. 通过 ref标识,进行绑定
// 3. 通过 ref对象.value 即可访问到绑定的元素(必须等到元素渲染完才可以获取到)
const inp = ref(null)
</script>

<template>
  <div>
    <!-- 通过 ref标识,绑定元素 -->
    <input ref="inp" type="text">
  </div>
</template>

defineExpose()

默认情况下在 <script setup> 语法糖下组件内部的属性和方法是不开放给父组件访问的。

可以通过 defineExpose 编译宏指定哪些属性和方法允许访问

<script setup>
const cnt = 99
const sayHi = () => {
  alert('Hi')
}

defineExpose({
  sayHi,
  cnt
})
</script>

provide & inject

顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信

  1. 顶层组件通过 provide 函数提供数据

  2. 底层组件通过 inject 函数获取数据

跨层传递普通数据

顶层组件

provide('key', 'this is top message')

底层组件

const message = inject('key')

跨层传递响应式数据

顶层组件

const count = ref(100)
provide('count-key', count)

底层组件

const count = inject('count-key')

跨层传递方法

顶层组件可以向底层组件传递方法,底层组件调用方法修改顶层组件中的数据

顶层组件

const sayHi = () => {
  alert('Hi')
}
provide('sayHi-Key', sayHi)

底层组件

const sayHi = inject('sayHi-Key')
sayHi()

defineOptions

背景说明:

  • <script setup> 之前,如果要定义 props, emits 可以轻而易举地添加一个与 setup 平级的属性。
setup() {

},
props: {},
emits: {}
  • 但是用了 <script setup> 后,就没法这么干了 setup 属性已经没有了,自然无法添加与其平级的属性。

为了解决这一问题,引入了 defineProps 与 defineEmits 这两个宏。但这只解决了 props 与 emits 这两个属性。

如果我们要定义组件的 name 或其他自定义的属性,还是得回到最原始的用法——再添加一个普通的 <script> 标签。

这样就会存在两个 <script> 标签。让人无法接受。

<script>
export default {
  name: "componentName"
}
</script>

<script setup>
// 这里是不能直接定义 name 的,所以需要两个 <script> 标签
<script>

使用 defineOptions 解决这个问题

<script setup>
// 用于定义组件的选项。它允许你在组件内部以更简洁的方式设置组件的配置项
defineOptions({
  name: 'componentName',
  props: {
    message: String
  },
  emits: ['custom-event']
})
</script>

<template>
  <div>{{ message }}</div>
</template>

defineModel

在Vue3中,自定义组件上使用 v-model,相当于传递一个 modelValue属性,同时触发 update:modelValue 事件

<Child v-model="isVisible">
<!-- 相当于 -->
<Child :modelValue="isVisible" @update:modelValue="isVisible = $event">

而在 Child 组件中,我们需要先定义 props,再定义 emits 。其中有许多重复的代码。如果需要修改此值,还需要手动调用 emit 函数。

父组件

<script setup>
import MyInput from './my-input.vue'
import { ref } from 'vue'
const txt = ref('123456')
</script>

<template>
<div>
  <MyInput v-model="txt"></MyInput>
  {{ txt }}
</div>
</template>

子组件

<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  modelValue: String
})
const emits = defineEmits(['update:modelValue'])

</script>

<template>
  <input
    type="text"
    :value="modelValue"
    @input="e => emit('update:modelValue', e.target.value)"
  >
</template>

使用 defineModel 简化

<script setup>
import { defineModel } from 'vue'
const modelValue = defineModel()
</script>

<template>
<div>
  <input
    type="text"
    :value="modelValue"
    @input="e => modelValue = e.target.value"
  >
</div>
</template>

plate>

{{ txt }}
```

子组件

<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  modelValue: String
})
const emits = defineEmits(['update:modelValue'])

</script>

<template>
  <input
    type="text"
    :value="modelValue"
    @input="e => emit('update:modelValue', e.target.value)"
  >
</template>

使用 defineModel 简化

<script setup>
import { defineModel } from 'vue'
const modelValue = defineModel()
</script>

<template>
<div>
  <input
    type="text"
    :value="modelValue"
    @input="e => modelValue = e.target.value"
  >
</div>
</template>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值