Vue

153 阅读4分钟

vue3

回顾 vue2.x 生命周期钩子函数:

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeDestroy
  • destroyed

认识 vue3.0 生命周期钩子函数

  • setup 创建实例前
  • onBeforeMount 挂载 DOM 前
  • onMounted 挂载 DOM 后
  • onBeforeUpdate 更新组件前
  • onUpdated 更新组件后
  • onBeforeUnmount 卸载销毁前
  • onUnmounted 卸载销毁后

setup 函数

  • setup 函数是一个新的组件选项, 作为组件中 compositionAPI 的起点
  • 从生命周期角度来看, setup 会在 beforeCreate 钩子函数之前执行
  • setup 中不能使用 this, this 指向 undefined
  • 在模版中需要使用的数据和函数,需要在 setup 返回。

reactive 函数

  • 作用: 传入一个复杂数据类型,将复杂类型数据, 转换成响应式数据 (返回该对象的响应式代理)
  • 总结: 通常是用来定义响应式 对象数据

ref 函数

  • reactive 处理的数据, 必须是复杂类型, 如果是简单类型无法处理成响应式, 所以有 ref 函数!

  • 作用: 对传入的数据(一般简单数据类型),包裹一层对象, 转换成响应式。

  • ref 函数接收一个的值, 返回一个 ref 响应式对象, 有唯一的属性 value

  • 在 setup 函数中, 通过 返回值 ref 对象的 value 属性, 可以访问到值

  • 在模板中, ref 属性会自动解套, 不需要额外的 .value

  • ref 函数也支持传入复杂类型,传入复杂类型,也会做响应式处理

computed

<script setup lang="ts">
import { computed, ref } from 'vue'

const age = ref<number>(18)
const newage = computed(() => {
  return +age.value + 1
})
const nextNewage = computed({
  get() {
    return age.value + 2
  },
  set(value) {
    age.value = value - 2
  },
})
</script>

<template>
  <div>今年年龄:<input v-model.number="age" /></div>
  <div>明年年龄:{{ newage }}</div>
  <div>后年的年龄:<input type="text" v-model.number="nextNewage" /></div>
</template>

<style scoped></style>

watch

watch监视, 接收三个参数

  1. 参数1: 监视的数据源
  2. 参数2: 回调函数
  3. 参数3: 额外的配置

监视 ref 简单数据

监听单个 ref

// 监听单个ref
const money = ref(100)
watch(money, (value, oldValue) => {
  console.log(value)
})

监听多个 ref

// 监听多个ref
const money = ref(100)
const count = ref(0)
watch([money, count], value => {
  console.log(value)
})

监听 ref 复杂数据

监听 ref 复杂数据,需要通过 watch 第三个参数进行配置深度监听

// 监听ref复杂数据
const user = ref({
  name: 'zs',
  age: 18
})
watch(
  user,
  value => {
    console.log('user变化了', value)
  },
  {
    // 深度监听,,,当ref的值是一个复杂数据类型,需要深度监听
    deep: true,
    immediate: true
  }
)

监听对象的某个属性变化

const user = ref({
  name: 'zs',
  age: 18
})
watch(
  () => user.value.name,
  value => {
    console.log(value)
  }
)

监听 reactive 数据

监视单个 reactive,不需要开启深度监听

const user = reactive({
  name: 'zs',
  age: 18
})
const count = ref(0)
watch(user, value => {
  console.log('user变化了', value)
})

监视 reactive 某个属性

const user = reactive({
  name: 'zs',
  age: 18
})
const count = ref(0)
watch(
  () => user.name,
  value => {
    console.log('user变化了', value)
  }
)

监视多个 reactive 和 ref 使用数组

组件通讯-父传子

  1. 父组件提供数据
  2. 父组件将数据通过属性传递给子组件
  3. 子组件通过 defineProps 进行接收
  4. 子组件渲染父组件传递的数据

父组件:

<script setup>
import { ref } from 'vue'
// 在setup语法中,组件导入之后就能够直接使用,不需要使用components进行局部注册
import Son from './components/Son.vue'

const money = ref(100)
const car = ref('玛莎拉蒂')
</script>

<template>
  <div>
    <h1>我是父组件</h1>
    <div>金钱:{{ money }}</div>
    <div>车辆:{{ car }}</div>
    <hr />
    <Son :money="money" :car="car"></Son>
  </div>
</template>

<style lang="less" scoped></style>

子组件:

<template>
  <div>
    <h3>我是子组件</h3>
    <div>{{ money }} --- {{ car }}</div>
  </div>
</template>

<script setup>
// defineProps: 接收父组件传递的数据
defineProps({
  money: Number,
  car: String,
})

</script>

注意:如果使用 defineProps 接收数据,这个数据只能在模板中渲染,如果想要在 script 中也操作 props 属性,应该接收返回值。

// defineProps: 接收父组件传递的数据
const props = defineProps({
  money: Number,
  car: String
})
console.log(props.money)
console.log(props.car)

组件通讯-子传父

  1. 子组件通过 defineEmit 获取 emit 对象(因为没有 this)
  2. 子组件通过 emit 触发事件,并且传递数据
  3. 父组件提供方法
  4. 父组件通过自定义事件的方式给子组件注册事件

子组件:

<script setup>
defineProps({
  money: Number,
  car: String
})
// 参数数组中进行声明,可以触发哪些自定义事件
const emit = defineEmits(['changeMoney'])

const change = () => {
  emit('changeMoney', 10)
}
</script>

父组件:

<script setup>
import { ref } from 'vue'
// 在setup语法中,组件导入之后就能够直接使用,不需要使用components进行局部注册
import Son from './components/Son.vue'

const money = ref(100)
const car = ref('玛莎拉蒂')
const changeMoney = num => {
  money.value = money.value - num
}
</script>

<Son :money="money" :car="car" @changeMoney="changeMoney"></Son>

依赖注入 - provide 和 inject

依赖注入, 可以非常方便的实现 跨层级的 组件通信

image.png

传递单个数据

父组件利用 provide 提供数据

<script setup>
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
provide('money', money)
</script>

<template>
  <div>
    <h1>我是父组件</h1>
    <div>金钱:{{ money }}</div>
    <hr />
    <Son></Son>
  </div>
</template>

<style lang="less" scoped></style>

子组件 (子孙后代, 都可以拿到这个数据)

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

const money = inject('money')
</script>

<template>
  <div>
    <h3>我是子组件--{{ money }}</h3>
    <button>修改数据</button>
  </div>
</template>

<style lang="less" scoped></style>

传递多个数据

<script setup>
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
const house = ref('大别墅123')
provide('parentData', {
  money,
  house
})
</script>

可以多次传递和接收

根组件:

<script setup>
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
const house = ref('大别墅')
provide('money', money)
provide('house', house)
</script>

子组件:

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

const money = inject('money')
const house = inject('house')
</script>

子组件跨组建向上传

父组件提供方法

<script setup>
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
// 接收子组件传递过来的数据,并修改money
const changeMoney = val => {
  console.log(val)
  money.value = val
}
provide('changeMoney', changeMoney)
</script>

子组件调用方法通过参数传递数据

<script setup>
import { inject } from 'vue'
const changeMoney = inject('changeMoney')
</script>
<template>
  <div>
    <h3>我是子组件</h3>
    <button @click="changeMoney(100000)">修改数据</button>
  </div>
</template>

模板中 ref 的使用

获取模板的元素(dom 元素,组件)

ref 操作 dom

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

const hRef = ref(null)
const clickFn = () => {
  hRef.value.innerText = '我不是标题'
}
</script>

<template>
  <div>
    <h1 ref="hRef">我是标题</h1>
    <button @click="clickFn">操作DOM</button>
  </div>
</template>

ref 操作组件

<template>
<h1>父组件:</h1>
<Form ref="formRef"></Form>
<button @click="getForm">获取表单组件</button>
</template>

<script setup>
import {ref} from 'vue'
import Form from './components/Form.vue'

const formRef = ref(null)

const getForm = () => {
  formRef.value.validate()
}

</script>

<style lang='less' scoped></style>

需要配合 defineExpose

<template>我是Form表单组件</template>

<script setup>
const validate = () => {
  console.log('进行表单校验')
}
defineExpose({
  validate
})
</script>

<style lang="less" scoped></style>

vue3 中废弃了过滤器

vue3.0 中不能使用过滤器,直接使用函数进行替代

补充-toRefs 函数

使用场景: 如果对一个响应数据, 进行解构 或者 展开, 会丢失他的响应式特性!

原因: vue3 底层是对 对象 进行监听劫持

作用: 对一个响应式对象的所有内部属性, 都做响应式处理

  1. reactive/ref 的响应式功能是赋值给对象的, 如果给对象解构或者展开, 会让数据丢失响应式的能力
  2. 使用 toRefs 可以保证该对象展开的每个属性都是响应式的
<template>
  <div>{{ name }}</div>
  <div>{{ age }}</div>
  <button @click="age++">改值</button>
</template>

<script setup>
import { ref, toRefs } from 'vue'
const user = ref({
  name: 'zs',
  age: 18
})
const { name, age } = toRefs(user.value)
</script>

问题小结: toRefs 函数的作用是什么 ?

作用: 对一个 响应式对象 的内部属性,保证展开或者解构出的数据也是响应式的