春节期间每天一道面试题,整理出20道。有些问题回答看似简单,但实际上也是查阅了官方文档,源码,并可以对问题相关知识点进行扩展。所以本文并非是标准,只作为参考。
1. Vue 中v-if和v-for哪个优先级高?如果两个同时出现,应该怎么优化得到更好的性能?
v-if 与 v-for 如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,浪费了性能。
v-if 和 v-for 在模版编译后会执行函数 _l(渲染列表,即renderList),
格式如下: _l((arr),function(e,i){return (arr.length)?_c(...):_e()})
- 第一个参数为数组arr,
- 第二个参数为函数,函数返回值即 v-if 产生的三目运算。参数为v-for遍历的参数
由此可以看出,v-for先于v-if执行,遍历数组项时进行判断。
v-if和v-for同时存在一般有两种情形,第一种是对列表项的值过滤,第二种是判断列表隐藏/显示
- 第一种情形可以使用计算属性对数组进行过滤,使用过滤后的数组直接遍历
- 第二种情形可以在外层加标签判断v-if,就能避免对每一项检查,代码逻辑也更清晰
2. Vue组件data选项为什么必须是个函数而Vue的根实例则没有此限制?
vue 组件可能存在多个实例,如果使用对象形式定义data,则会导致它们公用一个data对象,那么状态变更将会影响所有组件实例,这是不合理的;采用函数形式定义,在initData时会将其作为工厂函数返回全新data对象,有效规避多实例之间状态污染问题。
而在Vue根实例创建过程中则不存在该限制,也是因为根实例只能有一个,不需要担心这种情况。
3. 你知道vue中key的作用和工作原理吗?说说你对它的理解。
- key 的作用主要是为了高效的更新虚拟DOM,其原理是Vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少DOM操作量,提高性能。
- 另外,若不设置key还可能在列表更新时引发一些隐蔽的bug
- vue中在使用相同标签名过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果。
4. 你怎么理解vue中的diff算法?
- diff算法是虚拟DOM技术的必然产物:通过新旧虚拟DOM作对比(即diff),将变化的地方更新在真实DOM上;另外,也需要diff高效的执行对比过程,从而降低时间复杂度为O(n)
- vue2.x 中为了降低Watcher粒度, 每个组件只有一个Watcher与之对应,只有引入diff才能精确找到变化的地方
- vue中diff执行的时刻是组件实例执行其更新函数时,它会比对上一次渲染结果newVnode,此过程成为patch
- diff过程整体遵循深度优先、同级比较的策略;两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同操作;比较两组子节点是算法的重点,首先假设头尾节点可能相同做4次比对尝试,如果没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点,借助key通常可以非常精确的找到相同节点,因此整个patch过程非常高效。
5. 谈一谈对vue组件化的理解
题目较大,可以先从组件化定义、优点、使用场景和注意事项等方面展开陈述、同时强调vue中组件化的一些特点。
- 组件是独立和可复用的代码组织单元,组件系统是Vue核心特性之一,它使开发者使用小型、独立可复用的组件构建大型应用。
- 组件化开发能大幅提高应用开发效率、测试性、复用性等
- 组件使用按分类有:页面组件、业务组件、通用组件;
- vue的组件是基于配置的,我们通常便携的组件是组件配置而非组件,框架后续会生成其构造函数,基于VueComponent,扩展于Vue
- vue 中常见组件化技术有: 属性prop、 自定义事件、插槽等,他们主要用于组件通信、扩展等
- 合理的划分组件,有助于提升应用性能
组件的设计原则:
- 组件应该是高内聚,低耦合的;
- 遵循单向数据流的原则
6. 谈一谈对vue的设计原则的理解?
Vue.js的设计原则包括以下几点:
首先它是一个渐进式框架,可以像jQuery一样引入,完成单页面项目;也可以使用node+webpack启动,打包。至于vue-router,vuex,在需要的时候自然会派上用场;
第二点是vue的高效性,提供了响应式数据,diff算法,虚拟dom等机制
第三点是跨平台和兼容性
7. vue为什么要求组件模板只能有一个根元素?
- 在全局组件生命时,Vue 组件需要指定 el 元素的id为挂载对象, 这也就明确了根元素只能有一个
- 在单文件组件中, template 最终渲染时,不会出现在页面中,此时template 中的元素挂载时也需要唯一入口
- 在虚拟dom patch时, 新旧节点自上而下,逐级比较;有子节点则向下递归。所以树的根节点只能有一个
8. 谈谈你对MVC、MVP和MVVM的理解?
这三者都是框架模式, 它们设计的目标都是为了解决Model 和 View的耦合问题
- MVC 模式出现较早主要应用在后端, 如Spring MVC、ASP.NET MVC 等,在前端领域早期也有运用如 Backbone.js。 它的优点是分层清晰,缺点是数据流混乱,灵活性带来的维护性问题
- MVP 模式是MVC的进化形式,Presenter作为中间层负责MV通信, 解决了两者耦合问题,但P层过于臃肿则会导致维护问题。
- MVVM模式在前端领域有广泛应用, 它不仅解决MV耦合问题, 还同时解决了维护两者映射关系的大量复杂代码和DOM操作代码, 在提高开发效率、可读性同时还保持了优越的性能表现。
9. 谈谈你对vue组件之间通信的理解?
10. 你了解哪些vue性能优化方法
- 路由懒加载
- keep-alive 缓存页面
- 使用v-show复用DOM
- v-for 遍历避免同时使用v-if
- 长列表性能优化
- 如果列表为纯粹的数据展示,不会有任何改变,则不需要响应式
- 大数据长列表,可以借助第三方库(如vue-virtual-scroller)采用虚拟滚动
- 组件销毁时,将定时任务等事件也作销毁,避免内存泄露
- 图片懒加载
- 第三方插件按需引入
- 无状态组件标记为函数式组件
- 计算属性本地化 避免多次get影响效率
- SSR
11. 你知道vue3有哪些新特性吗?它们会带来什么影响?
- Object.defineProperty改为proxy, 效率会更高;
- diff算法由原本的整个组件遍历改为block tree(区块树),只关注动态节点变化,vdom更新性能由与模板大小有关提升为与动态节点数量有关;
- Class API弃用,改用
Function Base API(Composition API)
- 对TS支持友好,使用时编辑器层面的错误提示和警告会更细
- 逻辑复用能力灵活,代码易压缩。
- 高可维护性,代码模块化更强(看源码目录),拆分更明确。
12. vue如果想扩展某个现有的组件时应该怎么做?
- 可以使用混入 mixins 来扩展组件方法,混入后的优先级依次为: 全局混入-> 局部混入-> 组件
- 加 slot 扩展,slot 插槽有 匿名插槽,具名插槽,都可以用来扩展组件内容
- 可以用 extends , 在新组件中声明
let comB = {
extends: comA,
...
}
13. watch和computed的区别以及怎么选用?
computed 与 watch 都是对象:
- watch 的key为监听数据,值为变化时所执行的函数;computed 的key为新产生getter 数据,值为经过对原数据格式化后产生的新值,需要return
- 计算属性底层来自于watch,但增加了一些操作,例如缓存
使用场景:
数据改变,需要交互或一些复杂逻辑时使用 watcher
仅需要getter格式化后的数据,或者有缓存需求时使用 computed
14. 谈谈你对vue生命周期的理解?
- 生命周期钩子是vue实例化过程中执行的函数。
- 必要性:生命周期的出现,让开发者可以在数据初始化、渲染、更新、挂载、销毁各个阶段前后执行自定义操作;了解内置生命周期,也可以更好的获取当前阶段数据、属性、方法。
- 执行过程:
- 从new Vue() 开始
- 初始化事件与生命周期时执行 beforeCreate 钩子,
- 初始化注入校验后执行 created 钩子
- 渲染、模版编译后执行 beforeMount
- 挂载结束前执行 mounted钩子
- data 被修改时执行beforeUpdate钩子
- 虚拟DOM重新渲染并更新后执行 Update钩子
- 调用vm.$destroy() 时执行beforeDestroy 钩子
- 解除绑定、销毁子组件及事件监听器后执行 destroyed 钩子
15. 谈谈你对vuex使用及其理解?
vuex的出现是为了解决vue中状态管理的混乱,使组件跨层级传值和兄弟组件传值变得容易,
vuex与vue一样,数据单向流动。
- 首先在store中定义需要复用的数据state,
- 组件通过getState或者mapState扩展方法来获取store中定义的state,修改state只能通过store.commit 来触发mutation方法从而 state被替换为新值。
- 为了保证数据的单向流动,还提供了action,action中可执行多个mutation, store.dispatch执行action,实质上还是会提交mutation,来使state更新。
使用vuex优点是可以知道是哪个组件用到了state,哪些组件修改的state;state统一维护,更方便模块化管理。
16. 你知道nextTick的原理吗?
在源码nexttick.js文件中,nextTick方法中将传来的回调push入数组并执行,得到promise数组,所以本质上是微任务队列,在执行完任务以后就可以获取到最终的结果。
17. 你知道vue的双向数据绑定的原理吗?
vue 双向数据绑定使用是通过v-model指令监听事件,被监听元素值发生变化时,data中对应的值也会同步更新。
vue 实现双向数据绑定的原理是数据劫持加发布订阅者模式, 数据劫持即Object.defineProperty 修改data对象的get set方法。
MVVM 的双向绑定需要实现下面功能:
- Observer 对data 的每个值进行监听
- Watcher 用来接收属性变动的通知
- Dep 收集watcher依赖
- Compiler 指令解析
Vue中的双向绑定又作了优化,一个组件对应一个watcher。
18. vue-router导航钩子有哪些?
导航钩子按分类可以分为:
- 全局导航钩子
- beforeEach 路由改变前调用
- afterEach 路由改变后调用
- 路由配置中的导航钩子
- beforeEnter
- 组件内部导航钩子
- beforeRouteEnter 实例创建前
- beforeRouteUpdate 实例创建后
- beforeRouteLeave 组件路由跳转前
19. 什么是一个递归组件
递归组件就是在组件模版中调用自己
使用场景如 树状菜单、目录
需要注意的是 递归组件 需要指定终止条件,如v-if指令为false,否则会抛出错误
20. 谈一谈你对vue响应式原理的理解?
vue2x响应式是 由Object.defineProperty 进行数据劫持,为data对象中的每个key添加get 和set方法,并为每个组件添加watcher监听数据变更。