【Vue核心篇Ⅵ】Vue3初识、组合式API、生命周期、组件通信、模版引用

173 阅读5分钟

vue3初识

Vue2 vs Vue3

Vue2

  • 选项式API(对象形式)
  • 只能有一个根元素/使用 this 访问变量
  • 打包工具 Webpack
  • 状态管理 Vuex
  • 语法高亮插件 Vetur

Vue3

  • 组合式API(函数形式)
  • 可以有多个根元素/使用 .value 访问变量
  • 打包工具 Vite
  • 状态管理 Pinia
  • 语法高亮插件 Volar

创建第一个Vue3项目

切换npm下载源为淘宝

npm set registry https://registry.npmmirror.com/

安装pnpm

npm install -g pnpm

使用pnpm代替npm创建项目

pnpm create vue #或 npm init vue@latest

Vue3组件化增删改查

  • 在父组件中完成列表渲染、删除
  • 将弹层单独封装为一个子组件,新增/编辑时打开弹层,在子组件中请求更新
  • 父组件通过 ref 函数可以得到子组件的实例,调用子组件中的方法打开弹层,同时传入当前行内容供数据回显
  • 子组件更新完毕后,要定义 defineEmits 通知父组件更新列表,重新渲染

读取/修改 ref 数据记得 .value

子组件实例中的方法只有 defineExpose 暴露出去才能让父组件访问

组合式API

setup

setup函数 是 Vue3 代码执行的入口。语法:

setup() {
    // Vue3代码
    // 其中的变量和方法,必须返回后才能在模板中使用
}

在组件初始化时,setup 函数最先被执行

setup 函数中不能使用 this 获取组件实例

在 script 标签上添加 setup 属性语法糖,可以省略 setup 函数,直接写 Vue3 代码

ref&reactive

ref函数/reactive函数 用来定义响应式数据。语法:

import { ref } from 'vue'
const 变量 = ref(值)
import { reactive } from 'vue'
const 变量 = reactive({
    属性1: 值1,
    属性2: 值2,
    ... ...
})

reactive 不能封装简单类型的数据

ref 既支持简单类型数据,也支持复杂类型数据,但必须通过 .value 访问数据

ref 封装复杂类型数据时,响应式的实现依赖于函数内部的 reactive 函数

reactive响应失效问题

只有 reactive 函数处理过的对象是响应式的,这点一定要注意:

let person = reactive({
    name: '张三',
    age: 18
})
// 这里将 person 变量赋值了一个新对象
person = {
    name: '李四',
    age: 23
}
// => person 失去了响应式特性

computed

computed函数 用来定义计算属性。语法:

import { computed } from 'vue'
const 变量 = computed(()=>{
    return 计算后的值
})

watch

watch函数 用来定义监听器。语法:

watch([变量1,变量2...],(newVal,oldVal) => {
    // 数据变化后的操作
},{
    immediate: true, //初始化时立即执行一次
    deep: true //深度监听
})

当监听多个数据时,任何一个数据改变都会触发这个回调,此时 newVal、oldVal 将得到一个数组

监听对象类型时,由于存在引用关系,newVal 与 oldVal 得到的都是变化后的对象

组合式API进阶

编译宏函数

Vue3 提供了一些全局的编译宏函数,不需要导入就可以直接使用:

  • defineProps():用于组件通信(父传子)
  • defineEmits():用于组件通信(子传父)
  • defineExpose():暴露子组件中的方法,供父组件使用

生命周期函数

image-20231101150420565.png

onMounted(()=>{
    // DOM挂载完毕后的操作
})

Vue3 中,没有 onBeforeCreate/onCreated 钩子函数,所有的初始化操作通常在 onMounted 函数中进行

同一钩子函数可以多次调用,它们从上往下依次执行

父子组件通信

eg.父子组件通信案例

<template>
    <div class="dad">
        <h3>父组件</h3>
        <Son :money="money" @change-money="changeMoney"></Son>
    </div>
</template>
<script setup>
import Son from './Son.vue'
import { ref } from 'vue'
const money = ref(100)
const changeMoney = (newMoney) => {
    money.value += newMoney
}
</script>
<template>
    <div class="son">
        <h3>子组件</h3>
        <p>老子给的钱:{{ money }}</p>
        <button @click="getMoney">再多给点吧</button>
    </div>
</template>

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

// 通知父组件修改
const emit = defineEmits(['change-money'])
const getMoney = () => {
    emit('change-money', 100)
}
</script>

change-money.gif

Vue3 中,不需要显式地注册一个组件,导入就可以使用

子传父的本质是:子组件通过触发父组件中的某个方法,通知父组件进行数据更新(有时可以利用子传父的特点来访问子组件内部的数据)

跨层级组件通信

跨层级组件通信的过程:

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

  2. 底层组件通过 inject 函数接收数据

eg.跨层级组件通信案例

// 父组件
import Son from './Son.vue';
import { provide, ref } from 'vue';
const money = ref(100)
const changeMoney = (newMoney) => {
    money.value += newMoney
}
provide('money', money)
provide('changeMoney', changeMoney)
// 子组件
import { inject } from 'vue';
// 接收数据用来渲染
const money = inject('money')
// 接收函数用来通知父组件进行数据更新
const changeMoney = inject('changeMoney')
<template>
    <div>
        <h3>我是子组件</h3>
        <p>老子给的钱:{{ money }}</p>
        <button @click="changeMoney(100)">再多给点吧</button>
    </div>
</template>

change-money.gif

模版引用

在 Vue3 中,使用 ref 函数可获取DOM元素或组件实例:

  1. 定义一个空的 ref 变量
  2. 在模版中绑定这个变量 ref="变量名"
  3. 在组件加载完毕后,通过 变量名.value 获取DOM元素/组件实例

eg.操作DOM元素

<h3 ref="h3">模版引用</h3>
const h3 = ref(null)
// 只有加载完毕后,才能获取到DOM元素
onMounted(()=>{
    h3.value.style.color = 'red'
})

eg.操作组件实例

<el-form ref="form"></el-form>
const form = ref(null)
onMounted(()=>{
    form.value.validate()
})

在组件内部,需要显式暴露其中的属性或方法:

const validate = ()=> {
    console.log('正在进行表单校验...')
}
// 显式暴露组件内部的属性和方法
defineExpose({
    validate
})

推荐阅读:Vue3 组合式API官方参考文档