前言
本文整理了高频出现的 Vue 相关面试题并且附带详解答案 难度分为简单
中等
困难
三种类型;
简单
1. 为什么 data 是一个函数
组件中的 data 写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的 data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份 data,就会造成一个变了全都会变的结果
2. Vue 组件通讯有哪几种方式
props 和$emit 父组件向子组件传递数据是通过 prop 传递的,子组件传递数据给父组件是通过$emit 触发事件来做到的
$parent,$children 获取当前组件的父组件和当前组件的子组件
$attrs 和$listeners A->B->C。Vue 2.4 开始提供了$attrs 和$listeners 来解决这个问题
父组件中通过 provide 来提供变量,然后在子组件中通过 inject 来注入变量。(官方不推荐在实际业务中使用,但是写组件库时很常用)
$refs 获取组件实例
eventBus 兄弟组件数据传递 这种情况下可以使用事件总线的方式
vuex 状态管理
3. 谈谈对vue生命周期的理解?
每个Vue
实例在创建时都会经过一系列的初始化过程,vue
的生命周期钩子,就是说在达到某一阶段或条件时去触发的函数,目的就是为了完成一些动作或者事件
beforeCreate
在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。在当前阶段 data、methods、computed 以及 watch 上的数据和方法都不能被访问created
实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。这里没有$el,如果非要想与 Dom 进行交互,可以通过 vm.$nextTick 来访问 DombeforeMount
在挂载开始之前被调用:相关的 render 函数首次被调用。mounted
在挂载完成后发生,在当前阶段,真实的 Dom 挂载完毕,数据完成双向绑定,可以访问到 Dom 节点beforeUpdate
数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁(patch)之前。可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程updated
发生在更新完成之后,当前阶段组件 Dom 已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新,该钩子在服务器端渲染期间不被调用。beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。我们可以在这时进行善后收尾工作,比如清除计时器。destroyed
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。activated
keep-alive 专属,组件被激活时调用deactivated
keep-alive 专属,组件被销毁时调用
常见的数据请求在哪一步发起?
可以在钩子函数 created、beforeMount、mounted 中进行异步请求,因为在这三个钩子函数中,data 已经创建,可以将服务端端返回的数据进行赋值。
如果异步请求不需要依赖 Dom 推荐在 created 钩子函数中调用异步请求,因为在 created 钩子函数中调用异步请求有以下优点:
能更快获取到服务端数据,减少页面 loading 时间;
SSR 不支持 beforeMount 、mounted 钩子函数,所以放在 created 中有助于一致性;
4. v-if 和 v-show 的区别?
v-if 在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。
v-show 会被编译成指令,条件不满足时控制样式将对应节点隐藏 (display:none)
扩展补充:display:none、visibility: hidden 和 opacity: 0 之间的区别?
三者都是隐藏元素,从四个角度说明不同点:
- 是否占据空间
display: none; 隐藏之后不占位置;visibility: hidden 和 opacity: 0 隐藏后会占据位置;
- 子元素是否继承
display: none 不会被子元素继承,父元素隐藏后是从页面视图消失,所以子元素也跟着隐藏了;
visibility: hidden 会被继承,可以通过子元素 visibility: visible 来显示资源数;
opacity: 0 会被继承,但不能设置子元素来重新显示;
- 事件绑定
display: none; 和 visibility:hidden 隐藏之后 不能触发它绑定的事件;
opacity: 0 元素上的事件是可以被触发的;
- 过滤动画
display: none; 和 visibility:hidden 使用transition 是无效的;
opacity:0 使用transition 是有效的,类似过度效果;
5. computed 和 watch 的区别和运用的场景
watch 属性监听
是一个对象,键是需要观察的属性,值是对应回调函数,主要用来监听某些特定数据的变化,从而进行某些具体的业务逻辑操作,监听属性的变化,需要在数据变化时执行异步或开销较大的操作时使用computed 计算属性
属性的结果会被缓存,当computed中的函数所依赖的属性没有发生改变的时候,那么调用当前函数的时候结果会从缓存中读取。除非依赖的响应式属性变化时才会重新计算,主要当做属性来使用computed中的函数必须用return返回最终的结果computed更高效,优先使用
使用场景
- computed:当一个属性受多个属性影响的时候使用,例:购物车商品结算功能
- watch:当一条数据影响多条数据的时候使用,例:搜索数据
6. v-if 与 v-for 为什么不建议一起使用
- key的作用主要是为了更高效的对比虚拟DOM中每个节点是否是相同节点;
- Vue在patch过程中判断两个节点是否是相同节点,key是一个必要条件,渲染一组列表时,key往往是唯一标识,所以如果不定义key的话,Vue只能认为比较的两个节点是同一个,哪怕它们实际上不是,这导致了频繁更新元素,使得整个patch过程比较低效,影响性能;
- 从源码中可以知道,Vue判断两个节点是否相同时主要判断两者的key和元素类型等,因此如果不设置key,它的值就是undefined,则可能永 远认为这是两个相同的节点,只能去做更新操作,这造成了大量的dom更新操作,明显是不可取的。
中等
7. Vue2.0 双向绑定实现原理
当一个Vue
实例创建时,Vue会遍历data选项的属性,用 Object.defineProperty
将它们转为 getter/setter并且在内部追踪相关依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher重新计算,从而致使它关联的组件得以更新。
相关代码如下
1 | class Observer { |
8. Vue3.0 和 2.0 的响应式原理区别
Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。
相关代码如下
1 | import { mutableHandlers } from "./baseHandlers"; // 代理相关逻辑 |
9. v-model的实现以及它的实现原理吗?
- vue中双向绑定是一个指令v-model,可以绑定一个动态值到视图,同时视图中变化能改变该值。v-model是语法糖,默认情况下相于:value和@input。
- 使用v-model可以减少大量繁琐的事件处理代码,提高开发效率,代码可读性也更好
- 通常在表单项上使用v-model
- 原生的表单项可以直接使用v-model,自定义组件上如果要使用它需要在组件内绑定value并处理输入事件
- 输出包含v-model模板的组件渲染函数,发现它会被转换为value属性的绑定以及一个事件监听,事件回调函数中会做相应变量更新操作,这说明神奇魔法实际上是vue的编译器完成的。
10.虚拟 DOM 是什么 有什么优缺点
由于在浏览器中操作 DOM 是很昂贵的。频繁的操作 DOM,会产生一定的性能问题。这就是虚拟 Dom 的产生原因。Vue2 的 Virtual DOM 借鉴了开源库 snabbdom 的实现。Virtual DOM 本质就是用一个原生的 JS 对象去描述一个 DOM 节点,是对真实 DOM 的一层抽象。
优点:
保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。
缺点:
无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,会比 innerHTML 插入慢。
11. 谈一下对 vuex 的个人理解
vuex 是专门为 vue 提供的全局状态管理系统,用于多个组件中数据共享、数据缓存等。(无法持久化、内部核心原理是通过创造一个全局实例 new Vue)
主要包括以下几个模块:
State
:定义了应用状态的数据结构,可以在这里设置默认的初始状态。Getter
:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。Mutation
:是唯一更改 store 中状态的方法,且必须是同步函数。Action
:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。Module
:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。
12. Vuex 页面刷新数据丢失怎么解决
需要做 vuex 数据持久化 一般使用本地存储的方案来保存数据 可以自己设计存储方案 也直接将状态保存至 cookie 或者 localStorage 中
13. Vuex 为什么要分模块并且加命名空间
模块
: 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。
命名空间
:默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
14. 路由原理 history 和 hash 两种路由方式的特点
hash 模式
:
location.hash 的值实际就是 URL 中#后面的东西 它的特点在于:hash 虽然出现 URL 中,但不会被包含在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
可以为 hash 的改变添加监听事件
1
window.addEventListener("hashchange", funcRef, false);
每一次改变 hash(window.location.hash),都会在浏览器的访问历史中增加一个记录利用 hash 的以上特点,就可以来实现前端路由“更新视图但不重新请求页面”的功能了
history 模式
:
利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法,来实现无需刷新页面就可以改变URL并且能够在浏览器历史记录中进行前进和后退操作, 在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前 URL 改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础, 但是history也是有缺点的,不怕前进后退跳转,就怕刷新(如果后端没有准备的话),因为刷新是实实在在地去请求服务器了, 需要正确的配置以避免出现404错误。。
abstract 模式
:
abstract 是vue路由中的第三种模式,本身是用来在不支持浏览器API的环境中,而不论是hash还是history模式都会对浏览器上的url产生作用,本文要实现的功能就是在已存在的路由页面中内嵌其他的路由页面,而保持在浏览器当中依旧显示当前页面的路由path,这就利用到了abstract这种与浏览器分离的路由模式。
15. Vue 修饰符有哪些 ?
事件修饰符
:用于控制事件的行为和触发条件
- .stop 阻止事件继续传播
- .prevent 阻止标签默认行为
- .capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
- .self 只当在 event.target 是当前元素自身时触发处理函数
- .once 事件将只会触发一次
- .passive 告诉浏览器你不想阻止事件的默认行为
v-model 的修饰符
: 用于增强其功能和控制其行为
- .lazy:将输入事件转换为 change 事件,即在元素失去焦点或按下回车键时更新绑定值。
- .number:自动将用户的输入转换为数字类型。
- .trim:自动过滤用户输入的首尾空白字符。
键盘事件的修饰符
:键盘事件的修饰符用于监听特定的键盘事件
- .enter:回车键。
- .tab:Tab 键。
- .delete:删除或后退键。
- .esc:Esc 键。
- .space:空格键。
- .up:上箭头键。
- .down:下箭头键。
- .left:左箭头键。
- .right:右箭头键。
系统修饰键
:用于监听按下特定的系统键盘组合键事件
- .ctrl:Ctrl 键。
- .alt:Alt 键。
- .shift:Shift 键。
- .meta:Meta 键(Windows 上是 Windows 键,Mac 上是 Command 键)。
鼠标按钮修饰符
: 鼠标按钮修饰符用于监听特定的鼠标按钮事件
- .left:左键(主键)。
- .right:右键。
- .middle:中键。
困难
16. Vue.nextTick()原理
在 Vue.js 中,Vue.nextTick() 方法用于在 DOM 更新之后执行回调函数。通常情况下,当更新DOM时,Vue.js 会将DOM操作异步化处理,并且会在下一次事件循环中批量处理,以提高性能和效率。这意味着,在更新DOM后立即访问DOM节点可能会失败或返回不正确的结果。为了解决这个问题,可以使用 Vue.nextTick() 方法来确保DOM已经更新并准备好操作。
实际上,Vue.nextTick() 方法内部是通过创建一个异步任务队列来实现的。每当需要更新DOM时,Vue.js 会将更新操作插入到异步任务队列中,并且会在下一次事件循环中批量处理这些更新操作。而 Vue.nextTick() 方法就是等待所有异步任务完成后才执行回调函数的方法。
更具体地说,Vue.nextTick() 方法会检查当前是否存在微任务(Promise 或 MutationObserver),如果存在,则使用微任务来延迟回调函数执行;否则,会创建一个新的 Promise 对象,并使用它来延迟回调函数执行。由于 Promise 的 then() 方法会在下一次微任务中执行,因此在 Promise 执行完毕后,回调函数就会在下一个事件循环中被调用。
总之,Vue.nextTick() 方法是Vue.js用于在DOM更新后执行回调函数的一种机制,其内部使用异步任务队列和微任务来实现。通过调用 Vue.nextTick() 方法,可以确保DOM已经更新并准备好操作,从而避免了访问DOM节点时出现的问题。
相关代码:
1 | // src/util/next-tick.js |
17. Vue.set 方法原理
在 Vue.js 中,Vue.set() 方法用于向响应式对象中添加新的属性或更新现有属性。这个方法可以确保响应式对象中新增或修改的属性也是响应式的,从而能够触发视图的更新。
实际上,Vue.set() 方法内部会检测目标对象是否为响应式对象,并且会使用 Vue.js 内置的响应式系统来添加或更新属性。如果目标对象不是响应式对象,则会将其转换为响应式对象,然后再进行添加或更新操作。
接下来,Vue.js 会在目标对象上调用 Object.defineProperty() 方法,以设置新的属性或更新现有属性。在这个过程中,Vue.js 会利用 Object.defineProperty() 方法提供的属性描述符,来确保新的属性也是响应式的。
总之,Vue.set() 方法是Vue.js用于向响应式对象中添加新的属性或更新现有属性的一种机制。通过检测目标对象是否为响应式对象,并利用 Vue.js 内置的响应式系统来添加或更新属性,Vue.set() 方法确保了新的属性也是响应式的,从而能够触发视图的更新。
相关代码:
1 | export function set(target: Array | Object, key: any, val: any): any { |
18. Vue 模板编译原理
Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步
1.将模板解析成 AST(抽象语法树)。AST 是一个树形结构,用于表示模板的语法结构和逻辑关系。
2.将 AST 转换成可执行的渲染函数。在这个过程中,会使用一些优化技巧来提高渲染函数的性能和效率。
3.将渲染函数和响应式数据关联起来,使得当响应式数据发生变化时,Vue.js 可以自动更新视图。
具体来说,模板编译的原理如下:
解析模板
:Vue.js 会使用正则表达式等方法来解析模板,并将其转换成 AST。在这个过程中,会识别出各种语法结构,例如指令、插值、属性绑定等。
静态分析
:Vue.js 会对 AST 进行静态分析,以确定每个节点的依赖关系和属性。同时,Vue.js 会检查模板是否存在语法错误或潜在的运行时错误,并生成相应的警告或错误信息。
代码生成
:根据 AST 生成可执行的渲染函数。在这个过程中,会使用一些优化技巧来提高渲染函数的性能和效率,例如缓存中间结果、避免不必要的计算等。
关联数据
:将渲染函数和响应式数据关联起来。在这个过程中,Vue.js 会使用响应式系统来监听数据的变化,并根据需要更新视图。