Vue3 新特性
Vue3 新特性
Composition API (组合式)
Composition API 是 vue3 引入的一种新的编写方式,主要是为了更好的组织和复用逻辑,解决 vue2 的 Options API (选项式)在大型项目中逻辑分散的问题。
例如,一个简单的计数器组件
在 Vue2 中的写法为
export default {
data() {
return {
count: 0,
}
},
methods: {
increment() {
this.count++
},
},
watch: {
count(newVal) {
console.log('count changed:', newVal)
},
},
}
使用 Vue3 的写法为
<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>
其中 计数器可以抽出
function useCounter() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
}
// 在组件中使用
const { count, increment } = useCounter()
相对于 Option API ,Composition API 的优势在于
- 集中组织逻辑
- 可复用性高
- 对 ts 的类型推断支持度更高
- 组件的维护更方便
setup
setup()钩子是 vue3 中新增的钩子函数,是组件在使用组合式 API 的入口,官方给出的使用场景主要是以下两点:
- 需要在非单文件组件中使用组合式 API 时。
- 需要在基于选项式 API 的组件中集成基于组合式 API 的代码时。
基本使用
<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() 有两个参数
- props:和标准的组件一致,一个 setup 函数的 props 是响应式的,并且会在传入新的 props 时同步更新。
- context: 第二个参数是一个 Setup 上下文对象。上下文对象暴露了其他一些在 setup 中可能会用到的值:
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 需要注意
- setup() 不包含对组件实例的访问权,在 setup() 顶层访问 this 会是 undefined
- setup() 应该返回一个同步对象。唯一可以使用 anync setup()的情况是组件是 Suspense 组件的后裔。
<script setup>
是 setup() 钩子的语法糖,当使用单文件组件和组合式 API 时的官方推荐,
<script setup>
// 变量
const msg = 'Hello!'
// 函数
function log() {
console.log(msg)
}
</script>
<template>
<button @click="log">{{ msg }}</button>
</template>
等价于
<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>
拥有以下优势
- 更少的样板内容,更简洁的代码。
- 能够使用纯 TypeScript 声明 props 和自定义事件。
- 更好的运行时性能 (其模板会被编译成同一作用域内的渲染函数,避免了渲染上下文代理对象)。
- 更好的 IDE 类型推导性能 (减少了语言服务器从代码中抽取类型的工作)。
Teleport
Teleport 官方给出的定义为:
<Teleport>
是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。
主要使用场景是以下几个地方:
- 弹窗(modal)
- 全屏遮罩层(overlay)
- 悬浮菜单(dropdown)
- tooltip、通知栏等不希望被 CSS 继承/干扰的元素
关于Teleport所想解决的痛点是,在理想状态下逻辑代码和模版代码都在一个单文件组件中,dom可能会被渲染在层级很深的地方,部分dom的css受到上层css影响,影响dom的样式。
使用Teleport可以是dom 直接渲染在最外层,避免了其他css对样式的影响。
Teleport 只 改变渲染的dom树,不会改变组件中的逻辑关系。
主要实现原理:
使用Teleport 包裹的dom 会使用
export const TELEPORT: unique symbol = Symbol(__DEV__ ? `Teleport` : ``)
去标记,生成一个特殊的VNode,
在patch阶段(将虚拟dom到真实dom转换的过程)判断是否是TELEPORT,进行分流处理
Suspense
<Suspense>
是vue3中新增的一个内置组件,用来在组件树中协调对异步依赖的处理。让我们可以在组件树上层等待下层的多个嵌套异步依赖项解析完成,并可以在等待时渲染一个加载状态。
渲染流程核心步骤
遇到
<Suspense>
标签- 编译器将其编译成 vnode,type 是内置组件
SuspenseImpl
- 其子插槽
default
和fallback
分别作为slots.default
和slots.fallback
- 编译器将其编译成 vnode,type 是内置组件
执行 default 插槽
- Vue 会尝试执行 default 插槽中的组件 setup()
- 如果这些组件中的 setup() 返回了 Promise(或 await 表达式),Vue 会将它们加入等待队列
suspense.asyncDeps
渲染 fallback
- 当
suspense.asyncDeps > 0
,Vue 会先渲染 fallback 插槽内容 - fallback 是实际插入 DOM 的元素(如
<Loading />
)
- 当
等待异步内容完成
- 每个异步组件 resolve 后会调用
suspense.depsResolved()
,减少计数 - 所有 asyncDeps 全部完成后,Vue 会:
- 卸载 fallback 插槽
- 挂载 default 插槽的实际渲染内容(即真实组件内容)
- 每个异步组件 resolve 后会调用
Fragment
相较与vue2的每个template
必须包含唯一一个root元素,vue3可以包含多个root元素,
在vue3中,template
中包含多个Root节点,Vue 会自动将其包装成一个 Fragment
响应系统重构
Vue 3 使用全新的响应式系统取代了 Vue 2 中的 Object.defineProperty
,采用了 ES6 的 Proxy
技术,彻底重构响应式核心,使其更加灵活、高效、功能丰富。
核心变化:基于 Proxy 构建响应式对象
Vue 版本 | 响应式实现方式 |
---|---|
Vue 2 | Object.defineProperty (递归劫持,难以监听新增/删除) |
Vue 3 | Proxy (动态劫持,无需递归,支持新增/删除) |
核心 API 简介
API 名称 | 作用描述 |
---|---|
reactive() | 将对象变为响应式对象(深层) |
ref() | 创建响应式的基本类型(如字符串、数字等) |
computed() | 创建计算属性,自动依赖收集 |
watch() | 响应式侦听器,监听值的变化 |
effect() | Vue 内部依赖追踪的原始 API(不常用) |