Vue
TODO
简介
前端框架发展历史关键点?
- 2004 年,Google Gmail 使用的 Ajax 技术使前端脱离模板的束缚。
- jQuery 解决了不同浏览器的兼容性问题。
- 2009 年,AngularJS 引领了 MVVM 的潮流,NodeJS 加速了前端工程化的诞生。
Vue 和 React 的计算性能瓶颈是什么?
Vue1 的每一个响应式数据都有一个 Watcher,极大的消耗内存,React 的问题发生在当页面 JS 执行 Diff 算法超过帧时间时带来的卡顿问题。
Vue2 引入虚拟 DOM,采用组件级别的响应式数据颗粒度,在避免了 Diff 算法超时问题的同时,降低内存消耗。同时,虚拟 DOM 还带来跨端能力。
React 引入 Fiber 架构,将 DOM Tree 变成链表,使得有能力随时启动或停止 Diff 算法(树的遍历使用递归所以相比链表更难记录状态),可以利用浏览器的空闲时间计算 Diff,避免了卡顿。
MVVM、MVC 的区别?
都是软件架构模式,主要区别是关注点不一样,Controller 用来承接交互操作并触发 Model 的修改,而 ViewModel 和 Model 层是双向绑定的,所以写 VueJS 时的思想不在如何操作 DOM 元素, 而主要是数据应该怎么变化。
Vue2 较明显的设计缺陷?
- 使用 Flow.js 来做类型校验,而不是社区所向的 TypeScript
- Vue2 内嵌了诸如 DOM API 之类的代码,使得跨端等二次开发变得困难,代码耦合也使得摇树优化变得困难
- Vue2 使用的 defineProperty 有缺陷,不支持 delete 等操作符
- Option API 在组件代码较多时不易维护
Option API 的一些缺陷?
- 数据挂载在 this 对象上,对类型系统以及摇树优化不友好
- 代码增多时,维护变得困难
- 不方便抽离通用逻辑
为什么要重写 Vue2?
有两个关键因素:主流浏览器逐渐兼容 Proxy,以及修复 Vue2 的设计缺陷。
切换到 Proxy 可以修复过去 setter、getter 无法拦截数组修改以及修改了对象本身带来的语义变化的问题,但由于它是一个原生特性,无法 polyfill,所以对 Vue 而言是一个重大的突破性变动。
过去的架构带来了模板很难正确的在 source-map 中追溯、渲染器(如 DOM 渲染器)不是渐进式的可选项的问题,再加上框架内部代码的耦合,使得摇树优化变得困难,代码也不便理解。
响应式
更多详细 API 见:Vue Reactivity API
ref 的原理?
ref 是在基础值的外面封装了一层对象,使用对象的 value setting、value getter 函数拦截并操作 value 属性,达到响应式的效果。
Vue 3 的自动脱 ref 机制?
自动脱 ref(Auto Unwrapping)是 Vue 3 响应式系统的特性,在特定场景下 ref 会被自动解包,无需手动写 .value。
触发场景:
- reactive 对象中的 ref:将 ref 放入 reactive 对象时,访问属性会自动解包
const count = ref(0) const state = reactive({ count }) state.count // 0,自动脱 ref state.count = 10 // 直接赋值,自动解包 - 模板中:模板渲染时自动解包 ref
<template> <div>{{ count }}</div> <!-- 不需要 .value --> </template> - watch 监听:直接监听 ref 时,回调参数是解包后的值
注意事项:
- 数组中的 ref 不会自动脱 ref,仍需
.value访问 - 替换整个 ref(如
state.count = ref(20))会导致属性指向新 ref 对象,原 ref 未被修改 - 自动脱 ref 只解包一层,嵌套对象内的 ref 仍然是 ref 对象
Vue 的设计初衷是减少模板中的 .value 繁琐写法,但这也带来常见陷阱:在 script 中需要 .value,在 reactive 对象里却不需要,新手容易混淆。
模板
Vue3 双向绑定的原理?
编译器
Vue3 做的静态优化有哪些?

VDOM
Vue2 和 Vue3 的 DOM Diff 算法有什么不同?
Vue2 使用双端 diff,Vue3 使用最长递增子序列。
TODO
为什么 React 的 Diff 算法不采用 Vue 的双端对比算法?https://juejin.cn/post/7116141318853623839
diff 算法深入一下?https://zhuanlan.zhihu.com/p/401340016
聊聊 Vue 的双端 diff 算法 https://zhuanlan.zhihu.com/p/534903909
diff 算法之双端对比 https://zhuanlan.zhihu.com/p/432927438
组件
如何处理中文输入法的 Enter 键误触发问题?
中文输入法(IME, Input Method Editor)在输入过程中会触发键盘事件,导致用户按 Enter 确认候选词时意外触发消息发送。
问题场景:
用户在 textarea 中输入中文拼音,输入法显示候选词,按 Enter 确认选择时,keydown 事件仍然触发,导致消息在拼音阶段就被发送。
解决方案:
使用 compositionstart / compositionend 事件检测 IME 输入状态,在输入法组合过程中忽略 Enter 键。
<script setup>
const isComposing = ref(false);
const input = ref('');
onMounted(() => {
const el = textarea.value;
el.addEventListener('compositionstart', () => {
isComposing.value = true;
});
el.addEventListener('compositionend', () => {
isComposing.value = false;
});
});
onKeyStroke('Enter', (e) => {
// 忽略 IME 输入法组合过程中的 Enter 键
if (isComposing.value) {
return;
}
// 处理发送逻辑...
});
</script>
为什么不使用 event.isComposing?
keydown 事件触发时 event.isComposing 可能还未更新。通过独立监听 composition 事件,确保状态是最新的。
keep-alive 的理解,它是如何实现的,具体缓存的是什么?
keep-alive 是一个内置抽象组件,会根据内部组件的 key,使用 LRU 策略来缓存组件实例。
Vue 的生命周期
keep-alive 独有 activated 和 deactivated 两个生命周期。
Vue 子组件和父组件执行顺序
父组件 mount 前,初始化子组件,所以父组件 beforeMount 后直到子组件 mounted 完成,再挂载。update 和 destroy 两个阶段类似,都是子组件先行。
Vue Router
$route VS $router
$route 是路由信息对象,包含 path、params、hash、query 等,$router 是 vue-router 实例,包含路由跳转方法和钩子函数等。
SSR
TODO
with TypeScript
Using Vue with TypeScript
"With a Vite-based setup, the dev server and the bundler are transpilation-only and do not perform any type-checking."
Vite-based 的项目不会在开发服务器启用类型检查。Vue 推荐开发者依赖 IDE 进行类型检查,或使用 vue-tsc watch mode、使用 vite-plugin-checker 另起线程插件。 Vue 认为类型检查降低开发环境的速度是不值得的。
两个 tsconfig.json 编译选项必须开启:isolatedModule 以便允许 esbuild 解析模块、noImplicitThis 以便启用 this 的类型。
"but for each project we are running two TS language service instances: one from Volar, one from VSCode' s built-in service"
禁用 @builtin TS 插件或打开 Volar 的 Takeover Mode,重启后便能使 Takeover Mode 生效。
"... with ts-loader. This, however, isn'
t a clean solution because the type system needs knowledge of the entire module graph to perform type checks"
使用 loader 用来类型检查的两个缺陷:检测缓慢阻塞编译、只能解析预解析后的文件所以报错不能直接跳转回源码。
"In version 3.2 and below, the generic type parameter for defineProps() were limited to a type literal or a reference to a local interface. This limitation has been resolved in 3.3."
TODO。为什么 Vue 3.2 不能使用外部引入的 Props 作为 type-based defineProps?Vue 3.3 是如何解决的? runtime-declare defineProps 和 runtime-declare emits 会不会对性能有影响?
"specify a generic type argument but omit the initial value,
the resulting type will be a union type that includes undefined"
如果不包含初始值,ref 的类型推断会自动包含 undefined。
// inferred type: Ref<number | undefined>
const n = ref<number>()
"Without type annotation, the event argument will implicitly have a type of any.
This will also result in a TS error if "strict": true or "noImplicitAny": true are used in tsconfig.json."
因为 noImplicitAny 需要打开,不然没法推断 options api 函数中 this 的类型,所以在组件中,接受事件的函数面对 Event 参数必须显式指定类型,不然就会报错。