Skip to content

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 ​

jQueryVue
定位库,方便选节点、改 DOM、发请求框架,管界面怎么根据数据长出来
写法命令式:$('xx').text('hi') 自己改声明式:改 data,模板自己变
适合小页面、改改已有 HTML有状态、要拆组件的中大型界面

jQuery 没有组件、响应式、生命周期这一套。页面一大、状态一多,自己维护 DOM 容易乱。Vue 把数据和界面拆开,适合往上长。

和 React ​

两边都是组件化 + 虚拟 DOM + 数据驱动,都是为了解决「页面复杂了不要手改 DOM」。差别主要在写法和生态:

Vue2React
界面怎么写模板,接近 HTMLJSX,界面就是 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 再把用户输入写回数据。

数据 → 视图(响应式) ​

  1. 数据劫持:初始化时用 Object.defineProperty 给 data 每个属性加 getter / setter
  2. 依赖收集:模板渲染时 Watcher 去读这个属性,触发 getter,把 Watcher 收进 Dep
  3. 派发更新:你改数据走 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 还没改少用
更新updatedDOM 已按新数据改完依赖新 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-ifv-show
原理条件为假时不渲染(销毁/创建)一直在 DOM 里,靠 display 切换
切换成本高低
初始成本假时更低无论如何都渲染

切换少、或很少为真 → v-if;频繁切换 → v-show。v-if 可以配 v-else,v-show 不行。

computed、watch、methods ​

methodscomputedwatch
定位方法,调用才执行由依赖算出一个值盯数据变化做副作用
缓存无,每次调用都跑有,依赖不变不重算无
返回值随意必须有,给模板/逻辑用一般不求返回值
典型场景点击、提交全名、过滤后的列表、价格合计一变就请求、同步到 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 在内存里,一刷新就空。常见做法:

  1. 登录态、用户信息写入 localStorage / sessionStorage,main.js 里创建 store 后(或 created)再读出来 commit 回去
  2. 插件 vuex-persistedstate,选模块持久化,别把整个 store 都塞本地
  3. 列表、详情这类业务数据优先进页再请求,以后端为准

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同一套描述可渲染到小程序、原生等(看实现)

常说的优势(面试点这些名就行)

  1. 少瞎改真实 DOM:先 diff 再 patch,避免一改数据就整段重刷
  2. 批量 / 合并更新:同一轮里多次数据变化,可凑到一次真实更新(再配合 nextTick)
  3. 跨平台抽象:描述和平台解耦,不只绑死浏览器
  4. 配合声明式:你管状态,框架管怎么落到 DOM,心智更简单

别说满:虚拟 DOM 不是「一定比手写最优 DOM 操作更快」。极简单、改一两处,手写可能更直接。复杂 UI、频繁局部更新时,框架这套更均衡、更好维护。

相关:key 帮 diff 认节点;流程见 diff。

🎤 口述精简版 ​

虚拟 DOM 是用 JS 对象描述真实 DOM。数据变了先在内存里 diff,再批量改真实 DOM。比你自己处处操作真实 DOM:少重排重绘、好合并更新、还能跨端,也更适合声明式写法。不是绝对更快,复杂页面更划算。

简述 Vue2 diff 算法的执行流程? ​

同层比较(不跨层):

  1. 新旧节点同层对比
  2. 头头、尾尾、头尾、尾头比对,尽量复用
  3. 用 key 在剩余节点里找可复用节点
  4. 找不到就创建,多出来的销毁
  5. 递归比子节点

核心:双端比较 + key,提高复用率。

🎤 口述精简版 ​

Vue2 diff 同层双端比,靠 key 找可复用节点,能挪就挪,不能再创建销毁,再递归孩子。

Vue 路由守卫有哪些?全局、独享、组件内守卫区别? ​

类型例子范围
全局beforeEach、beforeResolve、afterEach所有路由
独享路由配置里 beforeEnter单条路由
组件内beforeRouteEnter / Update / Leave组件内

常用于登录校验、权限、标题、离开确认。

🎤 口述精简版 ​

守卫分全局、路由独享、组件内三种。登录鉴权多用全局 beforeEach。

路由之间怎么传值?query 和 params 有什么区别? ​

页面跳转带数据,Vue2 里看 this.$route。常用两种:

queryparams
长什么样URL 问号后 ?id=1路径里 /user/1,路由要配 /user/:id
怎么跳push({ path: '/user', query: { id: 1 } })push({ name: 'user', params: { id: 1 } })
怎么取this.$route.query.idthis.$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 / 组件状态。

方案:

  1. 持久化:token、用户信息放 localStorage / sessionStorage,启动时再灌回 store
  2. 路由进页重新请求接口
  3. vuex-persistedstate 一类插件
  4. 关键数据以后端为准,前端只做缓存优化

🎤 口述精简版 ​

刷新内存状态会清空。把登录态存本地,进应用再恢复;业务数据优先重新请求,必要时再做 store 持久化。