Appearance
Vue2
本章管什么:Vue2 怎么用——是什么、虚拟 DOM、双向绑定、生命周期、指令、传值、路由传参、Vuex 用法等。
本章不管什么:按场景怎么选型(官网 / H5 / 小程序…)、为什么新项目选 Vue3、Vuex vs Pinia 的取舍 → 架构。「Vue 是什么」里对比 jQuery/React 只作入门一句。
Vue 是什么?和 jQuery、React 有什么区别?
Vue 是一套渐进式前端框架,用来做用户界面。核心是数据驱动:你改数据,视图跟着更新,不用自己去抠 DOM。常见说法是 MVVM:数据是 Model,页面是 View,Vue 在中间把两边绑上。Vue2 里响应式靠 Object.defineProperty,更新视图走虚拟 DOM。
渐进式的意思是:可以先在一页里局部用,也可以上组件、路由、Vuex 做成完整单页应用,不是必须一上来全套。
和 jQuery
| jQuery | Vue | |
|---|---|---|
| 定位 | 库,方便选节点、改 DOM、发请求 | 框架,管界面怎么根据数据长出来 |
| 写法 | 命令式:$('xx').text('hi') 自己改 | 声明式:改 data,模板自己变 |
| 适合 | 小页面、改改已有 HTML | 有状态、要拆组件的中大型界面 |
jQuery 没有组件、响应式、生命周期这一套。页面一大、状态一多,自己维护 DOM 容易乱。Vue 把数据和界面拆开,适合往上长。
和 React
两边都是组件化 + 虚拟 DOM + 数据驱动,都是为了解决「页面复杂了不要手改 DOM」。差别主要在写法和生态:
| Vue2 | React | |
|---|---|---|
| 界面怎么写 | 模板,接近 HTML | JSX,界面就是 JS |
| 数据怎么流 | data + 模板自动绑;表单常用 v-model 双向 | 默认单向,改界面靠 setState / 后来的 hooks |
| 全家桶 | 路由、Vuex 官方齐 | 路由、状态库社区选,更自由也更要自己搭 |
| 上手 | 会 HTML 就能看模板,相对快 | JSX 和函数式思路要适应一下 |
没有绝对谁更好:国内业务、要快上手、官方全家桶,Vue 常更合适;生态、跨端、函数式组件,React 面更宽。
Vue 相对优势( 怎么说)
- 渐进式,能局部用也能撑整站
- 模板接近 HTML,上手快
- 官方路由 / Vuex,少选型成本
v-model写表单方便- 国内资料和岗位多
追问响应式原理见 双向绑定;虚拟 DOM 相对真实 DOM 见 架构 · 虚拟 DOM。
🎤 口述精简版
Vue 是渐进式前端框架,核心是数据驱动,改数据页面就变,不用自己操作 DOM。和 jQuery 比,jQuery 是你自己改节点,Vue 是声明式、有组件。和 React 比,都是组件加虚拟 DOM,Vue 用模板、官方全家桶齐、上手快;React 用 JSX、生态更自由。Vue 的优势就是渐进式、上手快、官方工具齐、国内用的人多。
Vue2 双向数据绑定怎么做的?
两层一起说:响应式把数据推到视图;v-model 再把用户输入写回数据。
数据 → 视图(响应式)
- 数据劫持:初始化时用
Object.defineProperty给data每个属性加 getter / setter - 依赖收集:模板渲染时 Watcher 去读这个属性,触发 getter,把 Watcher 收进 Dep
- 派发更新:你改数据走 setter,Dep 通知 Watcher,再走虚拟 DOM 更新页面
视图 → 数据(v-model)
v-model 不是另一套魔法,就是:绑值 + 听输入事件写回 data。输入框等价于 :value 加 @input。
Object.defineProperty 的缺点
- 后来新增 / 删除的属性侦测不到,要用
Vue.set/Vue.delete - 数组下标赋值、改
length不直观,靠重写push/splice等方法 - 只能劫持对象上已有属性,嵌套还要递归,初始化成本高
🎤 口述精简版
Vue2 双向绑定是数据劫持加发布订阅。劫持就是 Object.defineProperty,给 msg 加 getter / setter。发布订阅就是谁用了 msg 记到 msg 自己的名单里,变了再通知名单里的 Watcher 改页面。输入框 v-model 绑 msg:第一次渲染读 msg 触发 getter,把 Watcher 记进名单;改 msg 走 setter,Watcher 被通知去改输入框上的字。缺点见上。
Vue2 完整生命周期有哪些?每个钩子用在哪?
组件从创建到销毁,框架给你几个时间点插手。按四段记:创建 → 挂载 → 更新 → 销毁。
| 阶段 | 钩子 | 这时有什么 | 常见用途 |
|---|---|---|---|
| 创建 | beforeCreate | 实例刚有,data / methods 还没有 | 很少用 |
| 创建 | created | 已有 data、methods、事件,还没有真实 DOM | 发请求、初始化数据 |
| 挂载 | beforeMount | 模板编译完,马上要挂 | 少用 |
| 挂载 | mounted | 已挂到页面,能拿到 DOM | 量尺寸、第三方库、$refs |
| 更新 | beforeUpdate | 数据变了,DOM 还没改 | 少用 |
| 更新 | updated | DOM 已按新数据改完 | 依赖新 DOM 的逻辑(慎用,别再改数据造成循环) |
| 销毁 | beforeDestroy | 还在,马上要拆 | 清定时器、解绑事件、取消请求 |
| 销毁 | destroyed | 已经拆掉 | 收尾 |
created 和 mounted 都能发请求:不碰 DOM 放 created 更早;要量高度、初始化图表,必须 mounted。
被 keep-alive 缓存时切走不会走销毁,走 deactivated;再进来走 activated。见 keep-alive。
父子顺序(常追问):创建是父 created 先于子 created;挂载是子 mounted 先于父 mounted(孩子挂完,父才算挂完);销毁是子先拆。
🎤 口述精简版
生命周期按创建、挂载、更新、销毁说。created 有数据能请求,但没有 DOM;mounted 才能碰页面。销毁前清定时器和事件。keep-alive 走激活/失活,不销毁。
Vue2 常用指令有哪些?v-if 和 v-show?computed、watch、methods 怎么选?
常用指令
| 指令 | 干什么 |
|---|---|
v-bind / : | 绑属性,如 :src、:class |
v-on / @ | 绑事件,如 @click |
v-model | 表单双向绑定(值 + 输入事件) |
v-if / v-else-if / v-else | 条件渲染,假时不生成节点 |
v-show | 一直渲染,靠 CSS display 显隐 |
v-for | 列表循环,要配 key |
v-text / v-html | 插文本 / 插 HTML(html 注意 XSS) |
v-slot / # | 插槽 |
v-once | 只渲染一次,后面数据变也不更新 |
日常开口报:v-bind、v-on、v-model、v-for、v-if / v-show,再补 v-html、插槽。
v-if 和 v-show
v-if | v-show | |
|---|---|---|
| 原理 | 条件为假时不渲染(销毁/创建) | 一直在 DOM 里,靠 display 切换 |
| 切换成本 | 高 | 低 |
| 初始成本 | 假时更低 | 无论如何都渲染 |
切换少、或很少为真 → v-if;频繁切换 → v-show。v-if 可以配 v-else,v-show 不行。
computed、watch、methods
methods | computed | watch | |
|---|---|---|---|
| 定位 | 方法,调用才执行 | 由依赖算出一个值 | 盯数据变化做副作用 |
| 缓存 | 无,每次调用都跑 | 有,依赖不变不重算 | 无 |
| 返回值 | 随意 | 必须有,给模板/逻辑用 | 一般不求返回值 |
| 典型场景 | 点击、提交 | 全名、过滤后的列表、价格合计 | 一变就请求、同步到 storage |
能靠依赖算出来的展示,用 computed;要「变了去干一件事」,用 watch;用户点了才跑,用 methods。
🎤 口述精简版
常用指令:绑属性 v-bind,绑事件 v-on,双向 v-model,循环 v-for,显隐 v-if / v-show。v-if 假就不渲染,切换贵;v-show 一直在,靠 CSS 切,频繁切用 show。methods 点一次算一次;computed 有缓存,适合派生值;watch 盯变化做副作用。能 computed 就算,别滥用 watch。
key 的作用是什么?key 为什么不推荐用 index?
key 帮 diff 识别节点身份,复用/移动/销毁更准确。
用 index 的问题:列表中间插入删除时,index 变了但节点被错误复用,导致状态错乱(输入框内容串台等)。
应用列表项稳定唯一 id 做 key。
🎤 口述精简版
key 是虚拟 DOM 的身份证。用 index 在插入删除时容易复用错节点,所以尽量用业务唯一 id。
Vue 组件怎么传值?
按关系说,不要一上来报清单。
父子(最常用)
父 → 子:props。子 → 父:$emit 抛事件,父监听。单向数据流:子不要直接改 props,要改就 emit 让父改。组件上的 v-model 也是 props + emit 的糖。
兄弟
没有直达通道。找共同的父中转,或用 event bus(new Vue() 当总线)。bus 小型还行,大了难追,容易乱。
跨多层provide / inject:祖先提供,后代注入,不用一层层 props 往下递。
全局
Vuex:很多页都要的用户信息、权限走 store。
少用但常被追问
$refs:父拿到子实例调方法$attrs/$listeners(Vue2.4+):透传没声明的属性和事件,中间层当传声筒
🎤 口述精简版
组件传值按关系说。父子:父传子 props,子传父 emit。兄弟找共同父中转或用 bus。跨多层 provide/inject。全局用 Vuex。再补 refs 调子方法,attrs/listeners 透传。
Vuex 五要素是什么?怎么做持久化?
Vuex 是 Vue2 的全局状态管理。组件里用 this.$store 读写。五件套:
| 干什么 | |
|---|---|
state | 唯一状态树,数据放这儿 |
getter | 从 state 算出衍生值,像计算属性,有缓存 |
mutation | 同步改 state,唯一推荐改数据入口,用 commit |
action | 异步(请求等),里面再 commit mutation,用 dispatch |
module | 按业务拆仓库,大项目用 |
改数据路径:页面 dispatch action → 请求完 commit mutation → 改 state → 视图更新。别在 action 里直接改 state,也别在 mutation 里发请求。
mutation 必须同步:DevTools 时光旅行要拍每次 mutation 前后快照,异步顺序会乱。
持久化
Vuex 在内存里,一刷新就空。常见做法:
- 登录态、用户信息写入
localStorage/sessionStorage,main.js里创建 store 后(或created)再读出来commit回去 - 插件
vuex-persistedstate,选模块持久化,别把整个 store 都塞本地 - 列表、详情这类业务数据优先进页再请求,以后端为准
token 可以存本地;密码别存。storage 是明文,别当保险箱。
🎤 口述精简版
Vuex 五要素:state 存数据,getter 算衍生值,mutation 同步改,action 异步再 commit,大了用 module 拆。刷新内存会空,token 这类存 localStorage,启动时灌回 store;业务数据进页再请求。
mutation 和 action 的区别?为什么 mutation 必须同步?
- mutation:同步改 state
- action:可异步,里面再 commit
mutation 必须同步:DevTools 时光旅行依赖每次 mutation 前后状态快照;异步的话顺序和记录会乱,难以追踪。
🎤 口述精简版
改 state 用同步 mutation;异步放 action。mutation 同步是为了让调试工具能准确记录每一次状态变更。
keep-alive 的作用?activated 和 deactivated 生命周期?
keep-alive 缓存动态组件,避免反复销毁重建(常见包 router-view)。
activated:缓存组件被激活deactivated:组件被切走,进入缓存
注意:被缓存时不会走 destroyed,清理逻辑要放对钩子。
🎤 口述精简版
keep-alive 用来缓存页面或组件,切回来更快。激活走 activated,切走走 deactivated。
keep-alive 的 include、exclude、max 属性作用?
| 属性 | 作用 |
|---|---|
include | 只缓存匹配的组件名 |
exclude | 匹配的不缓存 |
max | 最多缓存几个,超出按策略淘汰 |
组件需设置 name 才能被 include/exclude 准确匹配。
🎤 口述精简版
include/exclude 控制谁进缓存,max 限制缓存数量,防止内存飙。
Vue 虚拟 DOM 是什么?对比直接操作真实 DOM 有什么优势?
虚拟 DOM(Virtual DOM):用 JS 对象描述真实 DOM 树长什么样(标签、属性、孩子)。数据变了,先在内存里算出新的一棵虚拟树,跟旧的做 diff,得到最小变更(patch),再去动真实 DOM。
和直接操作真实 DOM 比
| 直接操作真实 DOM(如 jQuery) | 虚拟 DOM | |
|---|---|---|
| 你怎么写 | 自己选节点、改属性、插删 | 改数据 / 声明式模板,框架算差异 |
| 改动发生在哪 | 每次改都可能立刻碰浏览器 DOM | 多数计算在 JS 内存里,再批量 patch |
| 真实 DOM 贵在哪 | 改节点常牵涉样式计算、布局(重排)、绘制(重绘) | 能合并、复用,少做无意义的整页翻修 |
| 跨端 | 绑死在浏览器 DOM API | 同一套描述可渲染到小程序、原生等(看实现) |
常说的优势(面试点这些名就行)
- 少瞎改真实 DOM:先 diff 再 patch,避免一改数据就整段重刷
- 批量 / 合并更新:同一轮里多次数据变化,可凑到一次真实更新(再配合
nextTick) - 跨平台抽象:描述和平台解耦,不只绑死浏览器
- 配合声明式:你管状态,框架管怎么落到 DOM,心智更简单
别说满:虚拟 DOM 不是「一定比手写最优 DOM 操作更快」。极简单、改一两处,手写可能更直接。复杂 UI、频繁局部更新时,框架这套更均衡、更好维护。
相关:key 帮 diff 认节点;流程见 diff。
🎤 口述精简版
虚拟 DOM 是用 JS 对象描述真实 DOM。数据变了先在内存里 diff,再批量改真实 DOM。比你自己处处操作真实 DOM:少重排重绘、好合并更新、还能跨端,也更适合声明式写法。不是绝对更快,复杂页面更划算。
简述 Vue2 diff 算法的执行流程?
同层比较(不跨层):
- 新旧节点同层对比
- 头头、尾尾、头尾、尾头比对,尽量复用
- 用 key 在剩余节点里找可复用节点
- 找不到就创建,多出来的销毁
- 递归比子节点
核心:双端比较 + key,提高复用率。
🎤 口述精简版
Vue2 diff 同层双端比,靠 key 找可复用节点,能挪就挪,不能再创建销毁,再递归孩子。
Vue 路由守卫有哪些?全局、独享、组件内守卫区别?
| 类型 | 例子 | 范围 |
|---|---|---|
| 全局 | beforeEach、beforeResolve、afterEach | 所有路由 |
| 独享 | 路由配置里 beforeEnter | 单条路由 |
| 组件内 | beforeRouteEnter / Update / Leave | 组件内 |
常用于登录校验、权限、标题、离开确认。
🎤 口述精简版
守卫分全局、路由独享、组件内三种。登录鉴权多用全局 beforeEach。
路由之间怎么传值?query 和 params 有什么区别?
页面跳转带数据,Vue2 里看 this.$route。常用两种:
query | params | |
|---|---|---|
| 长什么样 | URL 问号后 ?id=1 | 路径里 /user/1,路由要配 /user/:id |
| 怎么跳 | push({ path: '/user', query: { id: 1 } }) | push({ name: 'user', params: { id: 1 } }) |
| 怎么取 | this.$route.query.id | this.$route.params.id |
| 刷新 | 还在,写在地址栏里 | 写进 path 才还在;只靠 name 传、path 里没有 :id,刷新就丢 |
| 观感 | 参数明明白白挂在后面 | 更像路径的一部分 |
js
// query
this.$router.push({ path: '/user', query: { id: 1 } })
this.$route.query.id
// params,路由要配 path: '/user/:id'
this.$router.push({ name: 'user', params: { id: 1 } })
this.$route.params.id坑:敏感信息别放 URL;中文等要编码;params 没配进 path 只靠 name 传,一刷新就没了。重要 id 要么写进 path,要么自己存 storage / 进页再请求。
🎤 口述精简版
路由传值两种:query 挂问号后面,刷新还在;params 走路径动态段,得在 path 里配 :id,只靠 name 传刷新会丢。页面上用 $route.query / $route.params 取。敏感信息别放地址栏。
Vue 如何解决页面刷新数据丢失问题?
刷新会丢内存里的 Vuex / 组件状态。
方案:
- 持久化:token、用户信息放
localStorage/sessionStorage,启动时再灌回 store - 路由进页重新请求接口
vuex-persistedstate一类插件- 关键数据以后端为准,前端只做缓存优化
🎤 口述精简版
刷新内存状态会清空。把登录态存本地,进应用再恢复;业务数据优先重新请求,必要时再做 store 持久化。