技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTVUE

Vue 必备知识点

所属馆藏
Vue
文件格式
Markdown
原始路径
Vue/18-Vue 必备知识点
本文目录6 个章节
  1. Vue 基础
  2. 生命周期
  3. Vue 组件通信方式
  4. Vue 进阶
  5. 小结
  6. 官方参考

Vue 必备知识点

本文整理了 Vue 2 时代常见面试知识点。Vue 2 的 Options API、Vue Router 3、Vuex 和 beforeDestroy/destroyed 等内容保留并标注历史版本;Vue 3.5 的 Composition API、Router 4、Pinia 和新的生命周期写法在对应章节补充。

Vue 基础

Computed 和 Watch 的区别

  • computed 计算属性:根据响应式依赖派生值,默认具有缓存。只有依赖变化并且重新读取时才会重新计算;getter 应保持纯净,不应在其中发请求或修改其他状态。
  • watch 侦听器:显式监听一个或多个响应式来源,在来源变化后执行副作用,例如异步请求、日志、同步浏览器 API。它默认是惰性的,不是“任何数据变化都触发”。
  • watchEffect:自动追踪回调同步执行阶段读取的依赖并立即执行,适合依赖列表不需要单独声明的场景。
import { computed, ref, watch } from 'vue'

const price = ref(10)
const count = ref(2)
const total = computed(() => price.value * count.value)

watch(total, (newTotal, oldTotal) => {
  console.log('total changed:', oldTotal, '=>', newTotal)
})

当需要派生数据并在模板中重复使用时优先考虑 computed;当需要在变化后执行异步或有副作用的工作时使用 watch。深度监听和 flush 时机应按需要配置,不能把所有 watch 都写成 { deep: true }

常见的事件修饰符及其作用

  • .stop:调用 event.stopPropagation(),阻止事件继续传播。
  • .prevent:调用 event.preventDefault();只对可取消的默认行为有效,不等同于阻止冒泡。
  • .capture:使用捕获阶段监听,由外到内执行。
  • .self:只有事件目标正好是当前元素时才触发,子元素冒泡上来的事件不会满足条件。
  • .once:事件监听器只执行一次。

v-ifv-show 的区别

  • v-if 是真正的条件渲染,切换时会创建/卸载条件块中的 DOM、事件监听器和子组件;初始条件为假时具有惰性。
  • v-show 始终渲染元素,通过 CSS 的 display 控制显示和隐藏;切换开销小,但初始渲染和节点常驻开销更高。
  • v-if 适合条件很少改变或不需要初始渲染的内容;v-show 适合频繁切换且内容较稳定的区域。

data 为什么是一个函数而不是对象?

在组件选项中,data 使用函数是为了让每个组件实例获得一份独立的数据对象:

export default {
  data() {
    return {
      count: 0
    }
  }
}

如果多个组件实例共享同一个对象,某个实例修改数据会影响其他实例。data() 每次创建实例时返回新对象,可以避免这种共享引用问题。Vue 3 的 Options API 仍然遵循这一规则;Composition API 通常在 setup()<script setup> 中声明状态。

Vue 中封装的数组方法有哪些?

Vue 2 会包装以下七个数组变异方法,使它们可以通知响应式依赖:

push、pop、shift、unshift、splice、sort、reverse

这不是因为 JavaScript 数组本身不能修改,而是因为 Vue 2 主要使用 Object.defineProperty() 观察已有属性,无法直接拦截数组索引和 length 的赋值。Vue 2 需要通过重写这些方法补上通知逻辑:

// Vue 2 中推荐
this.items.splice(index, 1, value)
this.$set(this.items, index, value)

Vue 3 使用 Proxy,可以直接追踪数组索引和 length;但仍建议使用清晰的数组操作,不要把“能拦截”误解成“没有任何性能成本”。

谈谈 MVVM

MVVM 通常分为 Model、View 和 ViewModel:

  • Model 代表数据和业务状态。
  • View 负责 UI 展示和用户交互。
  • ViewModel 连接 Model 与 View,负责响应式更新和事件处理。

Vue 并不是严格意义上把所有代码都分成三个独立层的 MVVM 实现,但它通过响应式系统、模板和组件把状态变化映射到视图,减少了手动 DOM 操作。v-model 等语法还会把表单输入事件和状态更新组合起来,形成双向绑定体验。

使用 Object.defineProperty() 进行数据劫持的缺点

Vue 2 的响应式系统会递归遍历初始化时已有的对象属性,并用 getter/setter 收集依赖和派发更新。它的主要边界包括:

  • 初始化后直接新增或删除对象属性,无法自动建立响应式。
  • 直接通过数组索引赋值和直接修改 length 无法被拦截。
  • 需要递归转换已有嵌套结构,初始化和更新存在额外成本。

Vue 2 用 Vue.setVue.deletesplice 和数组方法包装解决部分限制。Vue 3 使用 Proxy 代理对象级别的操作,可以覆盖新增、删除、索引和集合操作;但 Proxy 也有运行环境兼容性、原始对象绕过代理、解构断开连接等边界,不能简单说成“完美监听任何变化”。

生命周期

Vue 2 历史生命周期

Vue 2 的生命周期顺序可以概括为:创建、挂载、更新、销毁:

  • beforeCreate:Vue 2 的内部事件系统已经初始化,但数据观测、computed、watch 和 methods 代理尚未完成,通常不能访问已代理的 data、computed 和 methods。
  • created:实例选项已经处理,但 DOM 尚未挂载,不能依赖真实 $el
  • beforeMount:首次渲染即将开始,尚未完成 DOM 挂载。
  • mounted:实例 DOM 已挂载;不代表所有异步组件或异步数据都已经完成。
  • beforeUpdate:响应式数据已经变化,但对应 DOM 尚未完成更新。
  • updated:一次组件更新的 DOM patch 完成后调用,应避免在其中无条件修改状态,以免循环更新。
  • beforeDestroy:销毁前调用,实例仍然可用。
  • destroyed:销毁完成,Vue 2 的实例监听和子实例等已清理。
  • activated / deactivated:被 <keep-alive> 缓存的组件进入或离开激活状态。

Vue 2 的简化父子顺序通常是:

父 beforeCreate -> 父 created -> 父 beforeMount
-> 子 beforeCreate -> 子 created -> 子 beforeMount
-> 子 mounted -> 父 mounted

更新时通常是父 beforeUpdate、子 beforeUpdate、子 updated、父 updated;销毁时是父 beforeDestroy、子 beforeDestroy、子 destroyed、父 destroyed。具体还会受到组件树、异步组件和插槽的影响,不能把这份顺序当成所有边界场景的完整规范。

Vue 3 当前生命周期

Vue 3 将销毁阶段改名为卸载阶段:

  • beforeDestroybeforeUnmount
  • destroyedunmounted
  • Composition API 对应 onBeforeUnmount()onUnmounted()
<script setup>
import { onMounted, onUnmounted } from 'vue'

function handleResize() {
  // ...
}

onMounted(() => window.addEventListener('resize', handleResize))
onUnmounted(() => window.removeEventListener('resize', handleResize))
</script>

onMountedonUnmounted 等 DOM 生命周期不会在服务端渲染期间执行。需要服务端预取数据时,应结合 onServerPrefetch() 或使用 Nuxt 等框架提供的数据获取方案;不能把 Vue 2 中“在 created 请求数据”当作 Vue 3 SSR 的通用答案。

一般在哪个生命周期请求异步数据?

Vue 2 客户端项目常在 createdmounted 中请求数据,取决于是否需要 DOM。Vue 3 客户端可以在 setup() 中创建同步 watcher、在 onMounted() 中访问浏览器 API,或使用框架的 SSR 数据预取。异步请求还应考虑竞态、取消和错误状态:

import { onMounted, onUnmounted, ref } from 'vue'

const data = ref(null)
const controller = new AbortController()

onMounted(async () => {
  const response = await fetch('/api/data', {
    signal: controller.signal
  })
  data.value = await response.json()
})

onUnmounted(() => controller.abort())

Vue 组件通信方式

父子组件

  • props:父组件向子组件传递数据。
  • emits / $emit:子组件向父组件发出事件。
  • v-model:约定值和更新事件的双向绑定语法。
  • ref:父组件可以调用子组件公开的实例能力,但不应把它作为普通数据通信的首选。
  • provide/inject:跨越多层祖先后代组件传递依赖。

Vue 3 中如果希望 provide 的值保持响应式,应提供 refreactive 或 computed;直接 provide 一个当时读取到的普通值不会自动形成响应式连接。更安全的模式是由 provider 提供状态和修改函数,并对只读状态使用 readonly()

兄弟组件和任意组件

Vue 2 时代常见的 EventBus 是创建一个空 Vue 实例并使用 $on/$emit/$off。Vue 3 已移除实例事件 API $on$off$once,不能继续把 Vue 实例当事件总线。现在优先使用 props/emits、provide/inject、Pinia;确实需要独立事件总线时可使用 mitt 等库,并在组件卸载时移除监听。

Vue 2 的 $parent$children$refs 可以作为历史项目的逃生舱,但它们耦合组件层级,不适合替代清晰的 props/emits 契约。

Vue 进阶

Vue 路由模式有哪些?有什么区别?

Vue Router 常见的 URL 模式是 hash 和 HTML5 history:

  • hash 路由把路径放在 # 后面,hash 变化不会作为 HTTP 请求路径发送给服务器。
  • history 路由使用 pushStatereplaceStatepopstate,地址更自然,但服务器必须把前端路由回退到应用入口,否则刷新深层路径可能返回 404。
  • Vue Router 4 的 createMemoryHistory() 适合测试、SSR 或没有浏览器地址栏的环境;Vue Router 3 的对应历史模式名称是 mode: 'abstract'

Vue Router 3 历史写法:

const router = new VueRouter({
  mode: 'history', // 也可以是 'hash'
  routes
})

Vue Router 4 当前写法:

import {
  createRouter,
  createWebHashHistory,
  createWebHistory
} from 'vue-router'

const router = createRouter({
  history: createWebHistory(), // 或 createWebHashHistory()
  routes
})

说说 Vue Router 的 hash 模式

hash 模式的 URL 可能是 https://example.com/#/vue。修改 hash 时,浏览器通常不会向服务器请求新的页面资源,但会产生历史记录,因此前进和后退可以触发路由变化。Vue Router 通过 hashchange 及相关路由逻辑响应变化。

它兼容性较好、服务器配置简单,但 URL 包含 #,并且需要结合站点部署和搜索引擎策略评估 SEO。不能简单说 hash 是所有 Vue 项目的“默认模式”:Router 3 的默认模式是 hash,而 Router 4 要显式选择 createWebHashHistory()createWebHistory()

$route$router 的区别

在 Vue 2 / Router 3 或 Vue 3 Router 4 的 Options API 中:

  • $route 是当前路由信息对象,包含 pathparamsqueryhashfullPathmatchedname 等。
  • $router 是路由器实例,提供 pushreplacegoback 等导航方法以及全局守卫注册能力。

Vue Router 4 的 Composition API 中使用:

import { useRoute, useRouter } from 'vue-router'

const route = useRoute()
const router = useRouter()

router.push({ name: 'user', params: { id: 1 } })
console.log(route.params.id)

监听路由时,通常监听 () => route.params.id 等具体字段,而不是把整个 route 对象作为深度数据源。

Vuex 是什么?核心流程?

Vuex 是 Vue 2/3 时代的集中式状态管理方案。Vuex 3 通常对应 Vue 2,Vuex 4 对应 Vue 3。它的核心是响应式 store,状态通过约定的流程更新:

组件 -> dispatch Action -> commit Mutation -> State
  ^                                  |
  |-------------- 响应式渲染 ---------|
  • state:集中保存状态。
  • getter:从 state 派生数据。
  • mutation:同步修改 state,是 Vuex 追踪状态变化的约定入口。
  • action:可以执行异步工作,再提交 mutation。

Vuex 状态流转示意图(本地化历史图片)

Vuex 的规则有助于追踪状态变化,但并不代表任何项目都必须使用它。Vuex 当前处于维护状态,新项目通常优先考虑 Pinia。Vue 2.7 旧项目若使用 Pinia,应按 Pinia 官方兼容说明选择 v2;Pinia v3 面向 Vue 3。

Vuex 中 action 和 mutation 的区别

  • Mutation 专注于同步修改 state,提交方式是 store.commit()
  • Action 可以执行异步请求,提交 mutation 的方式是 context.commit(),也可以分发其他 action。
  • Action 的 context 不是 state 的父级,它是一个包含 stategetterscommitdispatch 等能力的上下文对象。
const store = new Vuex.Store({
  actions: {
    async fetchUsers({ commit, state, getters, dispatch }) {
      const users = await fetchUsersFromApi()
      commit('setUsers', users)
    }
  }
})

Redux 和 Vuex 的区别及共同思想

共同点包括:

  • 尽量保持单一、可追踪的状态来源。
  • 状态变化通过约定的流程发生,便于调试和回放。
  • 将共享状态从组件局部逻辑中抽离。

Vuex 的 mutation/action 是 Vue 生态的约定;Redux 通常通过 reducer 生成新 state,并由订阅机制驱动更新。Vuex 的组件更新可以直接利用 Vue 响应式系统,不需要开发者手动订阅 DOM。两者都不是 MVVM 的“实现本身”,而是状态管理方案。

Vuex 和 localStorage 的区别

  • Vuex 状态主要存在内存中,刷新页面后通常会丢失。
  • localStorage 是浏览器持久化存储,只能保存字符串,读写需要 JSON.stringify() / JSON.parse(),并且是同步 API。
  • Vuex/Pinia 能参与响应式更新;localStorage 本身不会自动让组件重渲染。
  • 持久化状态通常需要在 store 初始化时读取,并在变化时写回;要评估容量、隐私、XSS 和多标签页同步问题。

不能笼统地说“读取内存一定比读取硬盘快”,浏览器存储还有序列化、同步阻塞和实现差异;性能应以实际测量为准。

Vue 3 有什么更新?

Vue 3 的重要变化包括:

  • 使用 Proxy 为 reactive() 提供对象级响应式,能够处理属性新增/删除、数组索引和 length 等操作。
  • 支持更自然的 Composition API 和 <script setup>,也仍然支持 Options API。
  • 支持 Fragment、多根节点、Teleport 和 Suspense 等能力。
  • 提供更细粒度的 tree-shaking、静态提升、patch flags 和 block tree 优化。
  • 组件、指令、插件等全局 API 从 Vue 构造函数迁移到 app 实例。

原文中的“Vue 3 改成了类式组件”是错误说法。Vue 3 推荐 Composition API 或 Options API,并没有把组件统一改成 class 写法。

Portal 也不是 Vue 3 的内置名称;Vue 3 使用 <Teleport> 把内容渲染到 DOM 的其他位置。

Object.defineProperty 和 Proxy 的区别

Vue 2:

  • 初始化时遍历已有属性并安装 getter/setter。
  • 新增/删除属性、数组索引和 length 存在响应式边界。
  • 需要 Vue.setVue.delete 或包装数组方法补足限制。

Vue 3:

  • Proxy 代理整个对象,可以拦截读取、写入、删除、in、枚举等操作。
  • reactive() 访问嵌套对象时会继续提供深层代理。
  • 支持 Map、Set 等集合类型。
  • 原始对象绕过代理、直接解构 primitive 属性、使用 markRaw 等情况仍有使用边界。

对虚拟 DOM 的理解

Virtual DOM 是描述 UI 结构的 JavaScript 数据。状态变化后,框架会生成或更新 VNode,并把必要的差异应用到真实 DOM。它带来声明式渲染、跨平台抽象和编译优化的基础,但不应简单宣传为“任何场景都比手写 DOM 快”或“必然减少重排重绘”。

Vue 3 还会通过静态提升、patch flags、block tree 等编译和运行时优化减少不必要的工作;实际性能应结合 DOM 规模、组件结构和用户操作测量。

虚拟 DOM 和 Diff 的基本过程

简化过程如下:

  1. 根据状态生成新的 VNode 树。
  2. 将新旧 VNode 按节点类型、key 和位置进行比较。
  3. 对可复用节点更新属性和子节点。
  4. 创建新增节点、卸载删除节点,并将结果应用到真实 DOM。

Vue 2 的 sameVnodepatchVnodeupdateChildren 等源码是历史实现;Vue 3 还增加了编译器生成的静态节点和 patch flags。Diff 通常只比较同层级节点,使用启发式策略把复杂度从通用树编辑问题降低到可接受范围,但这不等于所有更新都是严格 O(n)。

为什么要设置 key

key 用于标识同一列表项或条件分支的身份,帮助 Vue 在更新时判断节点能否复用:

<input v-if="editing" key="edit" />
<input v-else key="view" />

v-for 中,建议为每一项提供稳定且唯一的业务 key:

<li v-for="user in users" :key="user.id">
  {{ user.name }}
</li>

key 不只是“让 Diff 更快”,更重要的是它影响组件实例、DOM 状态和输入值的复用身份。没有必要为了强制重建而随意使用随机 key,因为随机 key 会让节点每次都被卸载重建。

为什么不建议用 index 作为 key?

当列表会插入、删除、排序或包含输入框、组件内部状态时,index 会随着位置变化,导致 Vue 把旧位置的状态复用给另一条数据。使用稳定业务 id 更安全。

如果列表只会在末尾追加、顺序永远不变且没有内部状态,index 可能不会产生问题;因此不能绝对说 index “和没写 key 完全一样”,但它不应作为可变列表的默认选择。

编译过程

现代 Vue SFC 通常在构建阶段由 @vue/compiler-sfc 解析:

  1. 解析 <template><script><style>
  2. 将模板转换为 AST,并进行静态节点分析和优化。
  3. 生成 render 函数及 patch flags 等编译提示。
  4. 处理脚本、样式和资源,交给构建工具输出代码。

Vue 2 的模板编译实现和 Vue 3 的编译器细节不同,不能把模板编译概括成“主要依靠各种正则表达式替换”。

nextTick 原理和用途

Vue 会批量合并同一轮同步代码中的响应式更新,然后异步执行组件更新。nextTick() 返回一个 Promise(也支持回调),用于等待这轮 DOM 更新完成:

import { nextTick, ref } from 'vue'

const visible = ref(false)

visible.value = true
await nextTick()
// 此时可以读取本轮更新后的 DOM

Vue 2 的历史实现会根据环境选择 Promise、MutationObserver、setImmediate 或 setTimeout 等降级方案;这属于内部调度细节。Vue 3 应关注“等待批量 DOM 更新”这个公共语义,不要依赖具体宏任务/微任务实现。

小结

Vue 2 的知识点仍适合维护旧项目,但必须标注版本边界:Vue.setVue.usebeforeDestroy、实例 EventBus 和 Vue Router 3 都不是 Vue 3 的当前写法。新项目应优先使用 Vue 3、Composition API、Router 4、Pinia、useRoute/useRouteronBeforeUnmount/onUnmounted

官方参考

作者:牛客668579748号

原文链接:https://www.nowcoder.com/discuss/515602762281893888

来源:牛客网。

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS