Skip to content

Vue3 新特性


Composition API (组合式)

Composition API 是 Vue3 引入的组件组织方式,重点在于逻辑复用和按关注点拆分代码。相较于 Vue2 的 Options API,它更适合处理大型组件中的跨区域逻辑聚合。

简单计数器在 Vue2 中的写法:

js
export default {
  data() {
    return {
      count: 0,
    }
  },
  methods: {
    increment() {
      this.count++
    },
  },
  watch: {
    count(newVal) {
      console.log('count changed:', newVal)
    },
  },
}

对应的 Vue3 写法:

js
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
function increment() {
  count.value++
}

watch(count, (newVal) => {
  console.log('count changed:', newVal)
})
</script>

<template>
  <button @click="increment">{{ count }}</button>
</template>

计数器逻辑还可以继续抽成 composable:

js
function useCounter() {
  const count = ref(0)
  const increment = () => count.value++
  return { count, increment }
}
// 在组件中使用
const { count, increment } = useCounter()

相对于 Options API,Composition API 的主要特点:

  • 集中组织逻辑
  • 可复用性高
  • 对 TypeScript 的类型推断支持度更高
  • 组件的维护更方便

setup

setup() 是组合式 API 的入口函数,主要用于在组件初始化阶段声明响应式状态、组合逻辑以及对外暴露模板可见的绑定。

基本使用:

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

export default {
  setup() {
    const count = ref(0)

    // 返回值会暴露给模板和其他的选项式 API 钩子
    return {
      count
    }
  },

  mounted() {
    console.log(this.count) // 0
  }
}
</script>

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

setup() 有两个参数:

  1. props:和标准的组件一致,一个 setup 函数的 props 是响应式的,并且会在传入新的 props 时同步更新。
  2. context:第二个参数是 Setup 上下文对象,会暴露在 setup 中常用的一些能力:
js
export default {
  setup(props, context) {
    // 透传 Attributes(非响应式的对象,等价于 $attrs)
    console.log(context.attrs)

    // 插槽(非响应式的对象,等价于 $slots)
    console.log(context.slots)

    // 触发事件(函数,等价于 $emit)
    console.log(context.emit)

    // 暴露公共属性(函数)
    console.log(context.expose)
  },
}

setup() 的几个边界:

  1. setup() 不包含对组件实例的访问权,在 setup() 顶层访问 this 会是 undefined
  2. setup() 应返回同步对象。只有组件作为 Suspense 后裔时,才允许使用 async setup()

<script setup>setup() 的语法糖,用于在单文件组件中直接声明组合式逻辑。

js
<script setup>
// 变量
const msg = 'Hello!'

// 函数
function log() {
  console.log(msg)
}
</script>

<template>
  <button @click="log">{{ msg }}</button>
</template>

等价于

js

<script>
import { defineComponent } from 'vue'

export default defineComponent({
  setup() {
    // 变量
    const msg = 'Hello!'

    // 函数
    function log() {
      console.log(msg)
    }

    // 暴露给模板使用
    return {
      msg,
      log
    }
  }
})
</script>

<template>
  <button @click="log">{{ msg }}</button>
</template>

相对于普通的 <script><script setup> 的差异主要在于:

  • 样板代码更少。
  • props 和自定义事件可以直接用 TypeScript 声明。
  • 模板会被编译到同一作用域,减少渲染上下文代理开销。
  • IDE 类型推导路径更直接。

Teleport

Teleport 官方定义如下:

<Teleport> 是一个内置组件,可将组件内部的一部分模板“传送”到该组件 DOM 结构外层的位置。

典型场景

  • 弹窗(modal)
  • 全屏遮罩层(overlay)
  • 悬浮菜单(dropdown)
  • tooltip、通知栏等不希望被 CSS 继承/干扰的元素

Teleport 处理的问题是:逻辑代码和模板虽然位于同一组件中,但目标 DOM 可能需要脱离当前层级,否则容易受到上层样式和布局上下文影响。

Teleport 允许目标 DOM 直接渲染到指定外层容器,减少样式污染和层级限制。

Teleport 只改变 DOM 渲染位置,不改变组件逻辑关系。

运行时机制:

被 Teleport 包裹的 DOM 会被打上特殊标记:

js
export const TELEPORT: unique symbol = Symbol(__DEV__ ? `Teleport` : ``)

运行时会生成特殊 VNode,并在 patch 阶段(虚拟 DOM 转真实 DOM)识别为 TELEPORT 后走专门分支处理。


Suspense

<Suspense> 是 Vue3 新增的内置组件,用于在组件树中协调异步依赖处理。它允许上层等待下层多个嵌套异步依赖解析完成,并在等待阶段渲染加载状态。

渲染流程核心步骤

  1. 遇到 <Suspense> 标签
  • 编译器将其编译成 vnode,type 是内置组件 SuspenseImpl
  • 其子插槽 defaultfallback 分别作为 slots.defaultslots.fallback
  1. 执行 default 插槽
  • Vue 会尝试执行 default 插槽中的组件 setup()
  • 如果这些组件中的 setup() 返回了 Promise(或 await 表达式),Vue 会将它们加入等待队列 suspense.asyncDeps
  1. 渲染 fallback
  • suspense.asyncDeps > 0,Vue 会先渲染 fallback 插槽内容
  • fallback 是实际插入 DOM 的元素(如 <Loading />
  1. 等待异步内容完成
  • 每个异步组件 resolve 后会调用 suspense.depsResolved(),减少计数
  • 所有 asyncDeps 全部完成后,Vue 会:
  • 卸载 fallback 插槽
  • 挂载 default 插槽的实际渲染内容(即真实组件内容)

Fragment

Vue2 要求 template 具有单一根节点;Vue3 允许多个根节点,并在运行时将其组织为 Fragment 结构。


响应系统重构

Vue 3 使用基于 Proxy 的响应式系统替代 Vue 2 中的 Object.defineProperty 实现,响应式核心因此支持了更完整的对象操作拦截。


核心变化:基于 Proxy 构建响应式对象

Vue 版本响应式实现方式
Vue 2Object.defineProperty(递归劫持,难以监听新增/删除)
Vue 3Proxy(动态劫持,无需递归,支持新增/删除)

核心 API 简介

API 名称作用描述
reactive()将对象变为响应式对象(深层)
ref()创建响应式的基本类型(如字符串、数字等)
computed()创建计算属性,自动依赖收集
watch()响应式侦听器,监听值的变化
effect()Vue 内部依赖追踪的原始 API(不常用)