1. 简述 Vue3 相比 Vue2 有哪些主要改进
1. 性能优化
-
更快的虚拟 DOM:Vue3 的虚拟 DOM 重写,diff 算法更高效。
-
编译优化:静态提升、事件缓存、内联缓存等,让运行时更快。
-
体积更小:源码用 TypeScript 重写,按需 Tree-shaking,最小构建比 Vue2 小约 40%。
2. Composition API
-
新增
setup():可以在一个函数里集中管理逻辑。 -
reactive/ref/computed/watch等独立 API,更灵活、更易复用。 -
逻辑复用不再依赖 mixins,更清晰、更可维护。
例如 Vue2 用 data + methods 分散逻辑,Vue3 可以用 Composition API 把同一逻辑写在一起。
3. 更好的 TypeScript 支持
-
Vue3 源码完全用 TypeScript 重写。
-
API 类型提示更友好,开发时更容易发现问题。
4. 新的组件功能
-
Teleport:允许把组件渲染到 DOM 的任意位置。 -
Suspense:支持异步组件的加载占位和 fallback UI。 -
Fragment:支持组件返回多个根节点,不必强制包裹一个<div>。 -
emits选项:显式声明组件事件,增强类型推导。
5. 响应式系统升级
-
Vue2 基于
Object.defineProperty,不能监听新增/删除属性,数组变动支持有限。 -
Vue3 改为基于
Proxy,可以完整监听对象、数组、Map、Set 等数据结构。
6. 生命周期钩子改名(在 Composition API 中)
-
beforeCreate→setup -
created→setup -
其他钩子加
on前缀(如mounted→onMounted)。
7. 更灵活的构建方式
-
支持 自定义渲染器(Custom Renderer) ,不仅能跑在浏览器,还能扩展到小程序、桌面应用等。
-
Vue2 只能依赖 Vue 内核,Vue3 把核心和运行时拆分,更模块化。
2. 简述解释 Vue3 compositon API是什么
1. 什么是 compositon API
-
Composition API 是 Vue3 新增的一套 基于函数的 API,用来组织和复用组件逻辑。
-
它的核心入口是
setup()函数,所有逻辑都可以在setup里通过函数调用来组合。 -
Composition API = 用函数(setup、ref、reactive、watch 等)来组织逻辑,让代码更清晰、可复用、可维护,特别适合大型项目。
2. 关键点
-
逻辑集中
在 Vue2(Options API)里,数据写在data,方法写在methods,监听写在watch,生命周期写在各自钩子里,相关逻辑被拆散。
Vue3 的 Composition API 允许把同一逻辑写在一起,更清晰。 -
API 函数化
Vue3 提供ref、reactive、computed、watch等函数,可以灵活组合来实现响应式。 -
逻辑复用更强
可以把一组逻辑封装成 可复用的函数(composition function) ,在不同组件里复用,替代 Vue2 的 mixins。
3. 简述 setup() 函数在 Vue3 中起什么作用
在 Vue3 中,setup() 是组件的核心入口函数,它的作用可以简述为:
1. 初始化组件逻辑
- 在这里定义响应式数据(
ref、reactive)、计算属性(computed)、监听器(watch)。
2. 替代 Vue2 的 data、methods、computed 等
- 逻辑更集中,不再分散在多个选项里。
3. 返回给模板使用
setup()返回的对象,会暴露给模板(template)使用。
4. 接收组件上下文
- 通过参数
props获取父组件传入的数据。 - 通过
context获取attrs、slots、emit等。
5. 逻辑复用
- 可以调用自定义的组合函数(composition functions),实现逻辑模块化和复用。
4. 简述 ref 和 reactive 的区别
ref 和 reactive 的区别
| 特性 | ref | reactive |
|---|---|---|
| 定义数据 | 用来定义 基本类型(数字、字符串、布尔值等),也可包裹对象 | 用来定义 对象/数组 的响应式 |
| 访问方式 | 通过 .value 访问或修改值(模板里自动解包,不需要 .value) | 直接通过属性访问,不需要 .value |
| 返回值 | 返回一个包含 .value 的对象 | 返回对象的 代理 (Proxy) |
| 深层响应式 | 对象类型时,内部属性也是响应式的(ref 内部也用 reactive 实现) | 默认是 深层响应式(对象嵌套也会被递归代理) |
| 适用场景 | 单一值、原始类型变量 | 复杂对象、数组、集合等 |
示例:
ref
import { ref } from 'vue'
const count = ref(0)
count.value++ // 修改
console.log(count.value) // 访问
reactive
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: { name: 'Alice' }
})
state.count++ // 修改
state.user.name = 'Bob' // 嵌套属性也响应式
总结
ref:适合 基本类型 或需要一个独立可变值的情况,要通过.value访问。reactive:适合 对象或数组,直接操作属性即可,内部会自动深度代理。
5. 简述 Vue3 中的 watch 和 watchEffect 有何不同
| 特性 | watch | watchEffect |
|---|---|---|
| 监听目标 | 需要显式指定监听的源(如 ref、reactive 属性、getter 函数) | 不需要指定,自动收集副作用函数中用到的响应式依赖 |
| 是否立即执行 | 默认 不会 立即执行,只有依赖变化时才触发(可设置 immediate: true 让它立即执行) | 创建时会 立即执行一次,然后依赖变化时再次执行 |
| 回调参数 | 回调函数接收 新值和旧值 ((newVal, oldVal) => {}) | 回调函数里 没有新旧值参数,而是直接重新执行整个函数 |
| 适用场景 | 更适合 精确侦听某个数据源 的变化 | 更适合 快速响应所有依赖变化(类似自动运行的副作用) |
示例:
watch
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`count: ${oldVal} -> ${newVal}`)
})
count.value++
// 输出:count: 0 -> 1
watchEffect
import { ref, watchEffect } from 'vue'
const count = ref(0)
watchEffect(() => {
console.log(`count is: ${count.value}`)
})
count.value++
// 输出:
// count is: 0 (立即执行一次)
// count is: 1 (依赖变化再次执行)
总结
watch:适合精确监听某个数据源的变化,并能获取新旧值。watchEffect:适合自动收集依赖并立即执行一次,简化逻辑,适合快速响应式副作用。
6. 简述 Vue3 中的 Suspense 组件是如何工作的
1. 什么是 Suspense
Suspense是 Vue3 新增的一个 内置组件。- 用来 等待异步组件或异步逻辑加载完成,在加载期间展示一个备用 UI(fallback)。
2. 工作原理
- 异步依赖
当Suspense包裹的子组件里有 异步组件 或setup()里返回 Promise 时,会被认为是「挂起状态」。 - fallback 占位
在挂起状态下,Suspense会先渲染fallback插槽里的内容(比如“加载中”)。 - 完成加载
等待所有异步依赖完成后,再切换显示default插槽里的真实内容。
示例:
<template>
<Suspense>
<!-- 默认内容:需要等待 -->
<template #default>
<AsyncComponent />
</template>
<!-- 占位内容:异步未完成时展示 -->
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</template>
<script setup>
const AsyncComponent = defineAsyncComponent(() =>
new Promise((resolve) => {
setTimeout(() => {
resolve(import('./MyComponent.vue'))
}, 2000) // 模拟网络延迟
})
)
</script>
页面效果:
- 前 2 秒显示 `加载中...`。
- 2 秒后显示 `MyComponent` 的内容。
总结
Vue3 的 Suspense 组件通过 等待异步组件或 setup() 中的 Promise,在加载期间展示 fallback 插槽,加载完成后再渲染 default 插槽,从而优雅地处理异步渲染。
7. 简述 Vue3 中的 Teleport 组件有什么作用
1. 什么是 Teleport
Teleport是 Vue3 新增的一个 内置组件。- 作用是:把子组件的 DOM 结构渲染到指定的 DOM 节点中,而不是渲染在它本来的父组件里。
2. 作用和场景
(1) 脱离父组件层级
- 解决一些全局 UI(弹窗、模态框、通知、菜单)需要渲染到
body或应用根节点的问题。
(2) 不影响逻辑归属
- 虽然 DOM 渲染位置被移动,但逻辑上依然属于原来的组件层级,仍然能访问响应式数据、触发事件。
示例
<template>
<button @click="show = true">打开弹窗</button>
<Teleport to="body">
<div v-if="show" class="modal">
<p>这里是弹窗内容</p>
<button @click="show = false">关闭</button>
</div>
</Teleport>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(false)
</script>
效果:
- 虽然 `Teleport` 写在当前组件里,但最终渲染的 `<div class="modal">` 会被直接挂到 `<body>` 下。
- 弹窗不会被父容器的 CSS 约束,避免层级问题。
总结
Vue3 的 Teleport 用来 把组件渲染内容传送到指定的 DOM 节点,常用于全局 UI(如模态框、弹窗、提示框),既保持逻辑归属,又能灵活控制渲染位置。
8. 简述 Vue3 如何优化性能
1. 更快的虚拟 DOM
- Vue3 重写了虚拟 DOM,Diff 算法更高效。
- 避免不必要的比较,提升更新性能。
2. 编译时优化
- 静态提升:静态节点只生成一次,后续复用,不再重复创建。
- 事件缓存:不会在每次渲染时重新生成相同的事件函数。
- Patch Flag:编译器会给动态节点打标记,运行时只更新有变化的部分。
3. 响应式系统升级
- Vue2 用
Object.defineProperty,Vue3 改为 Proxy,可以更高效地追踪变化。 - 支持完整的数据结构(对象、数组、Map、Set 等),避免 Vue2 里的一些性能 hack。
4. 按需 Tree-shaking
- Vue3 源码模块化设计,未使用的功能不会被打包,体积更小,运行时更快。
5. Fragment 支持
- 支持多根节点,减少不必要的 DOM 包裹层(Vue2 需要额外的
<div>)。
6. 更高效的组件初始化
setup()逻辑集中,减少组件初始化的开销。
7. Suspense + Teleport 优化渲染体验
Suspense:异步组件加载时,先显示占位 UI,避免白屏。Teleport:把弹窗等 DOM 直接渲染到目标位置,避免深层嵌套影响性能。
总结
Vue3 通过 编译优化(静态提升、Patch Flag)+ Proxy 响应式系统 + Tree-shaking + 更快的虚拟 DOM 等手段,大幅提升了性能和渲染效率,同时带来了更好的开发体验。
9. 简述 Vue3 的 provide 和 inject 是如何工作的
1. 什么是 provide / inject
provide和inject是 Vue3 提供的 依赖注入机制。- 用于 跨层级组件通信,避免一层层通过 props 传递数据。
2. 工作方式
(1) 父组件中 provide
- 使用
provide定义要共享的数据(键值对)。
(2) 后代组件中 inject
- 使用
inject获取到provide提供的数据。
⚡ 即使组件层级很深,中间的组件也不需要关心传递,后代可以直接拿到数据。
示例
#父组件
<script setup>
import { provide, ref } from 'vue'
const themeColor = ref('blue')
provide('color', themeColor)
</script>
<template>
<Child />
</template>
#子孙组件
<script setup>
import { inject } from 'vue'
const color = inject('color', 'defaultColor') // 第二个参数是默认值
</script>
<template>
<p>主题颜色:{{ color }}</p>
</template>
这样,`Child` 或更深层组件都能直接拿到 `color`
⚠ 注意点
provide提供的是 引用(如果是 ref/reactive,会保持响应式)。inject拿到的数据就是同一个引用,修改时需要谨慎。inject可以设置默认值(当没有对应的provide时使用)。
总结
provide 和 inject 就像 “数据发布者”与“订阅者” ,用来在祖先组件和后代组件之间共享数据,避免繁琐的 props 逐层传递,常用于全局配置、主题、依赖注入场景。
10. 简述 Vue3 如何处理组件的异步加载
1. Vue3 异步组件加载的方式
(1) defineAsyncComponent
- Vue3 提供了一个专门的 API:
defineAsyncComponent - 用来将一个工厂函数(返回
import()动态导入的 Promise)包装成异步组件。
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() =>
import('./MyComponent.vue')
)
(2) 支持加载状态与错误处理
defineAsyncComponent 可以接收配置对象,添加 loading 组件、错误组件、延迟/超时控制:
const AsyncComp = defineAsyncComponent({
loader: () => import('./MyComponent.vue'),
loadingComponent: LoadingComp, // 加载时显示
errorComponent: ErrorComp, // 加载失败时显示
delay: 200, // 延迟显示 loading(毫秒)
timeout: 3000 // 超过 3s 报错
})
(3) 结合 <Suspense> 使用
Vue3 新增了 <Suspense> 组件,能优雅处理异步组件加载:
<Suspense>
<template #default>
<AsyncComp />
</template>
<template #fallback>
<p>加载中...</p>
</template>
</Suspense>
👉 fallback 会在 AsyncComp 加载期间显示,加载完成后替换为真正内容。
总结
Vue3 通过 defineAsyncComponent + <Suspense> 提供了强大的异步加载机制,不仅能实现按需加载,还能优雅地处理 加载中状态、错误回退、超时控制,从而优化用户体验和性能。
11. 简述 Vue3 中的响应式系统是如何工作的
1. 基于 Proxy 实现
- Vue2 使用
Object.defineProperty,只能劫持对象的已有属性。 - Vue3 使用 ES6 Proxy,可以直接拦截对象的各种操作(读写、删除、遍历等)。
- 更强大,支持对象、数组、
Map、Set等复杂数据结构。
2. 依赖收集 (track)
- 当组件渲染时,读取响应式数据 → Vue 会通过 Proxy 的
get捕获操作 → 把当前副作用函数(渲染函数或watchEffect等)记录为依赖。
3. 触发更新 (trigger)
- 当响应式数据发生变化时 → Proxy 的
set捕获操作 → Vue 通知所有依赖这个数据的副作用函数重新执行 → 从而更新视图。
4. 核心 API
reactive():把对象变成响应式代理。ref():用于包装基本类型或对象,生成带.value的响应式引用。computed():基于依赖自动缓存的计算属性。effect()/watchEffect():副作用函数,依赖会被自动追踪。
示例
import { reactive, effect } from 'vue'
const state = reactive({ count: 0 })
effect(() => {
console.log(`count is: ${state.count}`)
})
state.count++
// 输出:count is: 1
// 这里 `effect` 在第一次运行时收集了 `state.count` 依赖,后续修改 `state.count` 时会自动触发更新。
总结
Vue3 的响应式系统基于 Proxy + 依赖收集 + 派发更新 实现,能更高效、全面地追踪数据变化,支持更多数据结构,并为 Composition API 提供了坚实的基础。
12. 简述 Vue3 中的自定义指令是如何定义和使用
1. 在组件中局部注册
<script setup>
const vFocus = {
mounted(el) {
el.focus()
}
}
</script>
<template>
<input v-focus />
</template>
2. 在应用中全局注册
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 注册一个全局自定义指令 v-focus
app.directive('focus', {
mounted(el) {
el.focus()
}
})
app.mount('#app')
使用
<input v-focus />
Vue3 自定义指令的生命周期钩子
和 Vue2 类似,但在 Vue3 里名字改动了一点:
- created:指令绑定到元素时调用(新)。
- beforeMount:元素插入 DOM 前调用。
- mounted:元素插入 DOM 后调用。
- beforeUpdate:所在组件更新前调用。
- updated:所在组件更新后调用。
- beforeUnmount:组件卸载前调用。
- unmounted:组件卸载后调用。
示例
app.directive('color', {
beforeMount(el, binding) {
el.style.color = binding.value
},
updated(el, binding) {
el.style.color = binding.value
}
})
用法
<p v-color="'red'">这段文字是红色</p>
总结
Vue3 的自定义指令通过 app.directive(全局)或对象语法(局部)定义,提供了一系列生命周期钩子,可以在元素挂载、更新、卸载时操作 DOM,常用于 聚焦、样式控制、权限控制等 DOM 操作场景。
13. 简述 Vue3 (Vue Router 4)中的路由和 Vue2 (Vue Router 3)有哪些变化
1. 安装和创建方式变化
# vue2
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({ routes })
new Vue({ router }).$mount('#app')
# vue3
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes
})
const app = createApp(App)
app.use(router)
app.mount('#app')
2. history 创建方式不同
Vue2:mode: 'history' | 'hash'
Vue3:通过 工厂函数 创建
createWebHistory()→ history 模式createWebHashHistory()→ hash 模式createMemoryHistory()→ SSR 场景
3. Composition API 支持更好
提供了基于 Vue3 的 setup() 使用的组合式 API:
useRouter()→ 获取路由实例useRoute()→ 获取当前路由对象
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
router.push('/home')
console.log(route.params.id)
4. 动态路由和类型支持更强
-
Vue Router4 更好地支持 TypeScript,路由参数有类型推导。
-
动态路由添加/删除方式简化:
router.addRoute({ path: '/about', component: About })
router.removeRoute('about')
5. 路由守卫改进
- 使用方式基本相同,但 Vue Router4 内部基于 Promise 机制,支持
async/await,可以更优雅地写异步逻辑。
6. 更好的错误处理
- 重复导航报错(如多次 push 同一路由),Vue Router4 会自动忽略,不需要手动
catch。
总结
Vue3 使用的 Vue Router 4 在 API 设计上更符合 Vue3 风格:
- 使用
createRouter+ 工厂函数创建 history - 提供组合式 API (
useRouter/useRoute) - 改进动态路由和 TypeScript 支持
- 更好的错误和异步处理
14. Vue3 如何与 Vuex4 一起使用
1. Vuex4 是什么
-
Vuex4 是为 Vue3 适配的状态管理库。
-
API 和 Vuex3(Vue2 用的版本)基本一致,只是底层适配了 Vue3 的
createApp。 -
如果你熟悉 Vuex3,上手 Vuex4 几乎没有学习成本。
2. 使用步骤
(1)安装 Vuex4
npm install vuex@4
(2)创建 Store
在 store/index.js 中:
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
count: 0
}
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
asyncIncrement({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
},
getters: {
doubleCount(state) {
return state.count * 2
}
}
})
export default store
(3)在 Vue 应用中挂载 Store
在 main.js 中:
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
const app = createApp(App)
app.use(store)
app.mount('#app')
(4)在组件中使用
方式一:选项式 API
<template>
<div>
<p>count: {{ $store.state.count }}</p>
<button @click="$store.commit('increment')">+1</button>
</div>
</template>
方式二:组合式 API(推荐)
Vuex4 提供了 useStore(),适配 Vue3 的 setup():
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
const count = computed(() => store.state.count)
const doubleCount = computed(() => store.getters.doubleCount)
const increment = () => {
store.commit('increment')
}
</script>
<template>
<div>
<p>count: {{ count }}</p>
<p>double: {{ doubleCount }}</p>
<button @click="increment">+1</button>
</div>
</template>
总结
在 Vue3 中使用 Vuex4:
- 用
createStore创建 Store - 在
main.js里通过app.use(store)挂载 - 在组件中既能用
$store,也能在setup()里用useStore(),更贴合组合式 API。