1.Vue3.0 所采⽤的 Composition Api 与 Vue2.x 使⽤ 的 Options Api 有什么不同?
-
options API 结构
- options api组件状态写在data里,方法写在methods里,如果功能很多很复杂,对应的属性列表和方法列表会很长,导致组件难以阅读和理解
-
Composition Api
- ⼀个功能所定义的所有 API 会放在⼀起
- ⼀个功能所定义的所有 API 会放在⼀起
2. Vue3 做了哪些优化
🔧 一、设计优化(根本变革)
1.1 Proxy 替代 Object.defineProperty(响应式系统升级)
- Vue2 使用
Object.defineProperty拦截对象属性,只能劫持已有属性,新增/删除属性不能自动响应。 - Vue3 使用
Proxy,可直接劫持整个对象,支持动态新增/删除属性,性能更高、覆盖面更广。
✅ 优势:更完整的响应式能力,性能更强,不需要为每个属性做递归绑定。
1.2 Composition API(组合式 API)
- 新增
setup()、ref、reactive、computed、watch等。 - 更好地组织逻辑复用(避免 Vue2 的 mixin 命名冲突和逻辑分散问题)。
✅ 优势:逻辑更清晰,利于代码重用,适合大型项目开发。
1.3 更好支持 TypeScript
- Vue2 对 TS 支持不友好(Options API 类型推导困难)。
- Vue3 全面重构,采用类型增强的编译器和组合式 API,更适合 TS 项目。
✅ 优势:代码更健壮,开发体验更好,IDE 自动补全更精准。
🚀 二、性能优化
2.1 编译时优化(编译提升)
Vue3 编译器会进行静态标记(Static Tree Hoisting、Patch Flag):
- 静态提升:将不变的部分提升到渲染函数之外。
- Patch Flag:只对变化部分打补丁,跳过静态部分的 diff。
✅ 优势:避免不必要的更新,提高渲染效率。
2.2 更快的虚拟 DOM
- Vue3 的虚拟 DOM 更轻量、更优化。
- 使用 Block Tree 技术,追踪组件内的动态节点,跳过静态节点对比。
2.3 Tree Shaking 更好
- Vue3 全面模块化(ESM),使用了
rollup编译器 + ESM 输出。 - 未使用的 API 不会被打包(如
v-model、teleport等),减小包体积。
✅ 优势:体积更小,按需导入。
🏗 三、架构升级
3.1 Fragment 支持
- Vue2:组件只能有一个根节点。
- Vue3:允许组件返回多个根节点(Fragment)。
3.2 Teleport(传送门)
- 可以将组件的 DOM 渲染到 DOM 树的任意位置(如弹窗、模态框)。
3.3 Suspense
- 异步组件加载时渲染 fallback 内容,配合
<Suspense>。
3.4 自定义渲染器(Custom Renderer)
- Vue3 内部将渲染逻辑抽象为 Renderer,可以扩展到 Native、WebGL、Canvas、终端等。
🔧 如:Vue + Vue NativeScript、Vue + Vue Terminal Renderer
🧠 四、功能增强
4.1 多个 v-model
<MyInput v-model:title="title" v-model:content="content" />
4.2 Emits 明确声明
defineEmits(['update:value'])
4.3 更好的异步组件支持
- Vue3 异步组件加载支持超时、错误捕获、loading 等细节。
3. 你用vue3写过组件吗?如果让你实现一个modal你会怎么设计
- 设计目标
- 使用composition api
- 支持插槽内容插入
- 支持遮罩点击,ESC关闭
- 支持使用v-model控制显示隐藏
- 使用
<Teleport>将Model移动到body外层避免层级冲突 - 可复用,外部引入使用
🧩 Modal.vue 实现
<!-- components/Modal.vue -->
<template>
<Teleport to="body">
<Transition name="fade">
<div v-if="modelValue" class="modal-overlay" @click.self="handleClose">
<div class="modal-container" ref="modalRef">
<slot></slot>
<button class="close-btn" @click="handleClose">×</button>
</div>
</div>
</Transition>
</Teleport>
</template>
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue'
const props = defineProps<{
modelValue: boolean
}>()
const emit = defineEmits(['update:modelValue'])
const handleClose = () => {
emit('update:modelValue', false)
}
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') handleClose()
}
onMounted(() => {
window.addEventListener('keydown', handleKeyDown)
})
onBeforeUnmount(() => {
window.removeEventListener('keydown', handleKeyDown)
})
const modalRef = ref()
</script>
<style scoped>
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.modal-container {
background: white;
padding: 1.5rem;
border-radius: 8px;
min-width: 300px;
position: relative;
}
.close-btn {
position: absolute;
right: 10px;
top: 10px;
background: transparent;
border: none;
font-size: 1.5rem;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
🧪 使用方式
<script setup lang="ts">
import { ref } from 'vue'
import Modal from '@/components/Modal.vue'
const showModal = ref(false)
</script>
<template>
<button @click="showModal = true">打开弹窗</button>
<Modal v-model="showModal">
<h2>你好 Modal</h2>
<p>这里是内容</p>
</Modal>
</template>
4.Vue3.0性能提升主要是通过哪⼏⽅⾯体现的?
| 优化点 | 描述 |
|---|---|
| 1. Proxy 替代 Object.defineProperty | 响应式系统升级,性能大幅提升,支持更多数据类型 |
| 2. Tree-shaking 友好 | 采用 ES Module 编写,可按需打包,减小体积 |
| 3. 编译优化 (静态提升/静态节点 patch 优化) | 更智能地标记和提升静态节点,减少 runtime 开销 |
| 4. Composition API | 更好的逻辑复用,减少组件代码体积 |
| 5. 更快的 Virtual DOM | Diff 算法更高效,重写了 patch 过程 |
| 6. 更小更快的初始渲染 | 改善首次渲染耗时 |
| 7. SSR 性能翻倍 | Vue3 SSR 渲染速度提升 2 倍以上 |
| 8. 更好的 TypeScript 支持 | 重写了源码,完全使用 TS 编写,类型提示增强 |
1. ✅ Proxy 替代 defineProperty
-
Vue 2 使用
Object.defineProperty()劫持对象属性,但存在以下问题:- 无法监听新增/删除属性
- 数组的变更方法需要 hack(如
push) - 深层嵌套结构开销大
-
Vue 3 使用
Proxy实现响应式:- 可监听所有操作(读、写、删除、in 操作符、枚举等)
- 支持数组/Map/Set 等复杂结构
- 性能大幅提升
2. ✅ Tree-shaking 更友好
- Vue 3 采用全模块化 ES Module 写法
- 未使用的 API 可以被构建工具(如 webpack, rollup, Vite)摇树优化掉
// 只用到 ref 和 watch
import { ref, watch } from 'vue'
结果:打包体积更小!
3. ✅ 编译优化:静态提升、Patch 标记
Vue 3 编译阶段标记了静态节点(VNode),例如:
<!-- 静态内容 -->
<div class="title">Hello</div>
**不会在每次更新中重新创建/比较这个节点,节省 Diff 与 patch 的开销**。
静态提升- vue3 中对不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用
事件监听缓存- 默认情况下绑定事件⾏为会被视为动态绑定,所以每次都会去追踪它的变化
4. ✅ Composition API 更适合逻辑复用
- 逻辑集中、复用性强
- 替代 mixin 中变量冲突、命名模糊的问题
也间接减少了组件嵌套结构带来的渲染负担。
5. ✅ Virtual DOM 优化
- Vue 3 的虚拟 DOM 是重新设计的(比 Vue 2 更轻、更快)
- 静态提升让 Virtual DOM 结构更 lean
- 使用 patchFlag 精准定位更新节点,减少不必要的重渲染
6. ✅ 初始渲染更快
首次渲染(mount)耗时更短,结合静态节点提升、template 编译优化。
7. ✅ 服务端渲染 SSR 性能翻倍
Vue 3 SSR 模块重写,提升如下:
- 多核并发渲染
- 更高效的 string concat
- 组合式 API 更易按需加载
当静态内容⼤到⼀定量级时候,会⽤ createStaticVNode ⽅法在客户端去⽣成⼀个static node,这 些静态 node ,会被直接 innerHtml ,就不需要创建对象,然后根据对象渲染
8. ✅ TypeScript 支持原生好
- Vue 2 是 JS 写的,加了 TS 类型声明,支持不完整
- Vue 3 是用 TS 重写的,类型安全、推导好、IDE 支持强
🚀 性能对比(来自官方 benchmark)
| 项目 | Vue 2.x | Vue 3.0 |
|---|---|---|
| 初始渲染时间 | 慢 | 快 1.2~2 倍 |
| 内存占用 | 高 | 更少 |
| 更新节点数量多时 | 卡 | 更顺滑 |
| SSR 吞吐量 | 一般 | 提升 2 倍以上 |
5. Vue3.0⾥为什么要⽤ Proxy API 替代 defineProperty API ?
一、Vue 2:使用 Object.defineProperty
Object.defineProperty(obj, 'key', {
get() { ... },
set(newVal) { ... }
})
问题:
- 只能劫持对象已有的属性
新增/删除属性时要用Vue.set/delete手动处理。 - 数组劫持有限
数组的索引或长度变动无法被监听,只能通过重写数组方法(如push、splice)解决,属于 hack。 - 递归性能差
需要递归遍历整个对象才能做响应式处理,对深层对象、大对象开销非常大。
✅ 二、Vue 3:使用 Proxy
const proxy = new Proxy(target, {
get(target, key) { ... },
set(target, key, value) { ... },
deleteProperty(target, key) { ... }
})
优点
- 无需遍历 : vue3使用lazy-observer,只有访问属性的时候才会创建响应式,更快更省内存
- 全面监听能力 : 能监听新增、删除、数组索引、长度变化
- 支持 Map / Set / WeakMap / WeakSet : Vue 2 无法监听这些类型,Vue 3 可完美支持。 |
- 更干净的源码结构 : 不再需要重写数组方法等“黑魔法”,更优雅、更可维护
✅ 三、Tree-shaking 友好性 vue3 使用模块化、函数式api (如reactive,ref,computed等) 构建响应式系统,不再强依赖全局类或原型链 - 未使用的API可以被构建工具剔除 - 最终结果就是打包体积更小
- 是基于ES6 模板语法(import和export)
✅ 四、Proxy 带来的响应式系统变化 上面说过了
6. Vue3新特性?
一. 性能优化相关
-
- v-once
- 功能:只渲染一次元素或者组件,之后不会重新渲染
- 适用场景:静态内容或不需要更新的部分。
<p v-once>这个内容只会渲染一次</p>
-
- v-memo
- 功能:对动态节点进行条件性的缓存,只在绑定值变更的时候重新渲染
- 优势:用于部分内容的优化,其余内容不变的复杂结构,减少渲染开销
<div v-memo="[count]"> <p>{{ count }}</p> <p>静态部分不会重新渲染</p> </div>
二. 响应式系统的升级(Proxy代替defineProperty)
-
- 更强大的响应式系统
- 原理变化:Vue 3 使用
Proxy替代了 Vue 2 的Object.defineProperty。 - 优势:
- 支持数组索引和对象属性的新增/删除响应式
- 性能更好
- 可以监听整个对象而不是单个属性
-
reactive&ref
reactive():把一个对象变成响应式对象ref():用于包装原始值- 示例:
const count = ref(0) const state = reactive({ count: 0 })
三、组合式 API(Composition API)
1. setup()
- 所有组合式逻辑的起点,组件中逻辑写在这里。
2. ref / reactive / computed / watch / watchEffect
- 更灵活地组合逻辑,避免 Option API 中的“逻辑分散”。
3. 生命周期钩子(如 onMounted、onUpdated)
- 替代原来的
mounted等选项,适用于组合式 API。
🧩 四、其它新指令与增强
1. v-model 多个绑定
- Vue 3 支持多个
v-model绑定:
<MyComponent v-model:title="title" v-model:content="content" />
2. v-is 支持动态组件标签
- 用于
<component>的语法糖:
<button v-is="dynamicComponent"></button>
3. v-bind 支持多个属性绑定:
<div v-bind="{ id: someId, class: someClass }"></div>
🧪 五、Fragments / Teleport / Suspense
1. Fragments(多根节点支持)
- Vue 3 组件可以返回多个根节点了!
2. Teleport(传送渲染)
- 渲染 DOM 到组件外部:
<teleport to="body">
<div class="modal">这是模态框</div>
</teleport>
3. Suspense(异步组件加载占位)
- 异步组件加载时显示 fallback 内容:
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
📚 六、TypeScript 支持更好
-
Vue 3 从架构上原生支持 TypeScript,包括:
- 类型推导增强
- defineComponent 提供类型约束
- Volar 插件支持
<script setup lang="ts">类型检查
✅ 总结表格
| 特性 | 说明 |
|---|---|
v-once | 内容只渲染一次,静态内容优化 |
v-memo | 条件性缓存,提高动态部分性能 |
| Proxy 响应式系统 | 全面替代 defineProperty,性能和覆盖范围更广 |
| Composition API | 更灵活的组合逻辑方式 |
| Fragments | 支持组件多根节点 |
| Teleport | 将子组件渲染到 DOM 的其他地方 |
| Suspense | 异步组件加载支持 fallback |
| 多个 v-model | 支持多个双向绑定 |
| v-is | 支持动态组件语法糖 |
| TS 原生支持 | 类型推导更强、编辑器体验更好 |
🚀 Vue3 响应式核心 API
1. ref
-
作用:用于包装基本类型值(number、string、boolean 等),或者对象。返回一个带有
.value的响应式引用。 -
特点:
- 在模板中
.value会自动解包。 - 在 JS 中必须通过
.value访问。
- 在模板中
import { ref } from 'vue';
const count = ref(0);
function increment() {
count.value++;
}
✅ 适用场景:
- 基本类型(数字、字符串、布尔)
- 需要一个“独立可变值”的时候,比如定时器 ID、是否显示对话框等。
2. reactive
-
作用:将对象变为深层响应式,返回一个 Proxy 包装的对象。
-
特点:
- 内部属性会递归转为响应式。
- 修改属性时,会触发视图更新。
import { reactive } from 'vue';
const state = reactive({
count: 0,
user: { name: 'Tom', age: 20 }
});
state.count++;
state.user.age++;
✅ 适用场景:
- 管理多个字段的复杂对象/数组。
- 类似 Vue2 的
data()。
3. ref vs reactive
| 对比点 | ref | reactive |
|---|---|---|
| 数据类型 | 基本类型/对象 | 只能对象/数组 |
| 访问方式 | .value | 直接访问 |
| 内部实现 | RefImpl 包装 | Proxy 包装 |
| 解构后响应性 | 丢失(除非配合 toRefs) | 丢失(除非配合 toRefs) |
4. toRef
-
作用:把对象某个属性转成 ref,保持响应式连接。
-
特点:
- 修改
ref.value会影响原对象。 - 修改原对象也会影响这个 ref。
- 修改
import { reactive, toRef } from 'vue';
const state = reactive({ count: 0 });
const countRef = toRef(state, 'count');
countRef.value++; // state.count 也会变
state.count = 10; // countRef.value 也变
✅ 适用场景:
- 当需要将
reactive中的某个字段 单独传递/解构 时,避免丢失响应式。
5. toRefs
- 作用:把整个对象的所有属性都转成
ref。
import { reactive, toRefs } from 'vue';
const state = reactive({ name: 'Tom', age: 20 });
const { name, age } = toRefs(state);
name.value = 'Jerry'; // state.name 也变了
✅ 适用场景:
- 在
setup()中return响应式对象时,避免丢失响应性。
setup() {
const state = reactive({ name: 'Tom', age: 20 });
return { ...toRefs(state) }; // 保持解构后的属性仍然响应式
}
6. shallowRef & shallowReactive
- shallowRef:只有
.value是响应式的,内部对象不做深层响应式。
const obj = shallowRef({ a: 1 });
obj.value.a = 2; // 不会触发视图更新
obj.value = { a: 3 }; // 会触发更新
- shallowReactive:只有对象的第一层属性是响应式的,深层属性不响应。
const state = shallowReactive({ foo: { bar: 1 } });
state.foo.bar = 2; // 不会触发更新
state.foo = { bar: 3 }; // 会触发更新
✅ 适用场景:
- 性能优化,避免深层递归代理。
- 当只关心浅层属性变化时。
7. readonly
- 作用:创建一个只读的响应式代理,防止修改。
const original = reactive({ count: 0 });
const ro = readonly(original);
ro.count++; // 报错(开发环境警告)
✅ 适用场景:
- 向子组件传递只读状态,避免被意外修改。
8. computed
- 作用:基于响应式数据派生出新数据,具备缓存。
import { ref, computed } from 'vue';
const count = ref(1);
const double = computed(() => count.value * 2);
count.value = 2; // double.value 自动更新
✅ 适用场景:
- 依赖已有数据的派生值。
- 避免重复计算。
9. watch & watchEffect
watch:侦听指定数据。
const count = ref(0);
watch(count, (newVal, oldVal) => {
console.log('count changed:', newVal, oldVal);
});
watchEffect:自动收集依赖,立即执行一次。
watchEffect(() => {
console.log('count is', count.value);
});
🎯 总结记忆口诀
- ref:小(基本类型/单值),要
.value - reactive:大(对象/数组),直接用
- toRef:取对象的某个属性当 ref
- toRefs:批量把对象转 ref,常用于解构
- shallowRef / shallowReactive:只做浅层响应式,性能优化
- readonly:只读,保护数据
- computed:缓存派生值
- watch / watchEffect:副作用侦听
vue3父子组件通讯
1. 父传子(Props)
父组件通过 props 传递数据,子组件通过 defineProps 接收。
子组件(Child.vue):
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
msg: String,
count: Number
})
</script>
<template>
<div>
<p>子组件收到:{{ msg }} - {{ count }}</p>
</div>
</template>
父组件(Parent.vue):
<template>
<Child :msg="message" :count="num" />
</template>
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'
const message = ref('Hello Vue3')
const num = ref(10)
</script>
2. 子传父(emit)
子组件通过 emit 派发事件,父组件监听并接收。
子组件(Child.vue):
<script setup>
import { defineEmits } from 'vue'
const emit = defineEmits(['updateMessage'])
function sendMsg() {
emit('updateMessage', '子组件发来的消息')
}
</script>
<template>
<button @click="sendMsg">点我发消息给父组件</button>
</template>
父组件(Parent.vue):
<template>
<Child @updateMessage="getMsg" />
<p>父组件接收:{{ msg }}</p>
</template>
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'
const msg = ref('')
function getMsg(val) {
msg.value = val
}
</script>
3. v-model 语法糖(父子双向绑定)
Vue3 中可以在 子组件中通过 defineProps + defineEmits 实现双向绑定。
子组件(Child.vue):
<script setup>
const props = defineProps({
modelValue: String
})
const emit = defineEmits(['update:modelValue'])
function updateValue(e) {
emit('update:modelValue', e.target.value)
}
</script>
<template>
<input :value="props.modelValue" @input="updateValue" />
</template>
父组件(Parent.vue):
<template>
<Child v-model="text" />
<p>父组件:{{ text }}</p>
</template>
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'
const text = ref('初始值')
</script>
4. ref 获取子组件实例
父组件可以通过 ref 调用子组件的方法或访问数据。
子组件(Child.vue):
<script setup>
import { ref } from 'vue'
const msg = ref('子组件数据')
function sayHello() {
alert('子组件方法被调用啦!')
}
defineExpose({
msg,
sayHello
})
</script>
父组件(Parent.vue):
<template>
<Child ref="childRef" />
<button @click="callChild">调用子组件</button>
</template>
<script setup>
import Child from './Child.vue'
import { ref } from 'vue'
const childRef = ref(null)
function callChild() {
console.log(childRef.value.msg) // 访问子组件数据
childRef.value.sayHello() // 调用子组件方法
}
</script>
总结
- 父传子:
props - 子传父:
emit - 双向绑定:
v-model(modelValue+update:modelValue) - 父调用子:
ref + defineExpose
5. EventBus (事件总线)
在 Vue2 里常用 new Vue() 做事件总线,但在 Vue3 里 Vue 实例不再支持这种用法,所以推荐用 mitt(轻量级事件库)。
eventBus.js:
import mitt from 'mitt'
const bus = mitt()
export default bus
子组件(Child.vue):
<script setup>
import bus from './eventBus.js'
function sendMsg() {
bus.emit('custom-event', '子组件通过 EventBus 发的消息')
}
</script>
<template>
<button @click="sendMsg">通过EventBus发消息</button>
</template>
父组件(Parent.vue):
<script setup>
import bus from './eventBus.js'
import { onMounted, onBeforeUnmount, ref } from 'vue'
import Child from './Child.vue'
const msg = ref('')
onMounted(() => {
bus.on('custom-event', (val) => {
msg.value = val
})
})
onBeforeUnmount(() => {
bus.off('custom-event')
})
</script>
<template>
<Child />
<p>父组件收到:{{ msg }}</p>
</template>
6. Vuex(状态管理)
Vue3 中依然支持 Vuex(但推荐 Pinia 作为替代)。Vuex 的核心是 状态集中管理,父子都可以从全局状态里读写。
store/index.js:
import { createStore } from 'vuex'
const store = createStore({
state: {
msg: '全局共享消息'
},
mutations: {
setMsg(state, payload) {
state.msg = payload
}
},
actions: {
updateMsg({ commit }, payload) {
commit('setMsg', payload)
}
}
})
export default store
main.js:
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
createApp(App).use(store).mount('#app')
子组件(Child.vue):
<script setup>
import { useStore } from 'vuex'
const store = useStore()
function changeMsg() {
store.commit('setMsg', '子组件修改了全局消息')
}
</script>
<template>
<button @click="changeMsg">修改全局消息</button>
</template>
父组件(Parent.vue):
<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'
import Child from './Child.vue'
const store = useStore()
const msg = computed(() => store.state.msg)
</script>
<template>
<Child />
<p>父组件从 Vuex 读取:{{ msg }}</p>
</template>
总结一下 Vue3 父子通信方式:
- props:父传子
- emit:子传父
- v-model:父子双向绑定
- ref + defineExpose:父调用子方法/数据
- provide & inject:跨层级传递(祖先 → 后代)
- EventBus(mitt) :任意组件通信(适合中小型项目)
- Vuex / Pinia:全局状态管理(适合中大型项目)