1. v-if和v-for优先级
源码:compiler/codegen/index.js L64
- for优先级高于v-if
- 如果同时出现,每次渲染都会先执行循环再判断条件,无论如何循环都不可避免,浪费性能
- 要避免出现这种情况,就在外层嵌套template,做v-if判断,内部再进行循环
- 如要判断循环体内部,可以先用计算属性,过滤出可循环的list
2. vue组件data要函数形式 而vue根实例无此限制
源码:src/core/instance/state.js initData()
- vue组件存在多个实例,如果使用对象形式定义data,则会共用一个data,状态将会影响所有组件实例。不合理
- 采用函数形式定义,在initData时会将其作为工厂函数返回全新对象,避免多实例之间的状态污染问题
- vue根实例只能有一个,不会出现这种情况
3. vue中key的作用和原理
源码:src/core/vdom/patch.js updateChildren() L409
- key的主要作用是为了高效的更新虚拟dom,其原理是patch过程中通过key可以精准判断节点是否是同一个,从而避免频繁更新不同元素,使得整个patch过程更加高效,减少dom操作量,提高性能
- 不设key,可能会有隐藏bug
- 过渡时候,也会使用到key,目的是为了让vue可以区分它们,否则只会替换内部属性而不会触发过渡效果
4. 怎么理解vue中的diff算法
必要性:lifeCycle.js mountComponent()
组件中可能存在很多个data中的key使用
执行方式:patch.js patchVnode() L586
patchVnode是diff发生的地方,整体策略:深度优先,同层比较
高效性:patch.js updateChildren()
- 通过新旧虚拟dom做对比(即diff),降低时间复杂度为O(n)
- vue 2.x中为了降低Watcher粒度,每个组件只有一个Watcher与之对应,只有引入diff才能精确找到发生变化的地方
- vue中diff执行的时刻是组件实例执行其更新函数时,它会比对上一次渲染结果oldVnode和新的渲染结果newVnode,此过程称为patch(打补丁)
- diff过程整体遵循
深度优先,同层比较的策略;两个节点之间比较会根据它们是否拥有子节点或者文本节点做不同操作;比较两组子节点是算法的重点,首先假设头尾节点可能相同做4次比对尝试,如果 没有找到相同节点才按照通用方式遍历查找,查找结束再按情况处理剩下的节点;借助key通常可以非 常精确找到相同节点,因此整个patch过程非常高效
5. 谈谈对vue组件化的理解
组件化定义
优点
使用场景
注意事项
-
组件化定义
源码:src/core/global-api/assets.js
vue-loader会编译template为render函数,最终导出的依然是组件配置对象 -
优点
源码:lifeCycle.js - mountComponent()
组件、Watcher、渲染函数和更新函数之间的关系 -
组件化实现
构造函数, src/core/global-api/extend.js
实例化及挂载, src/core/vdom/patch.js - createElm() L149
总结
- 组件是独立和可复用的代码组织单元,组件系统是vue核心特性之一
- 提高应用开发效率、测试性、复用性等
- 分类:页面组件、业务组件、通用组件
- vue的组件是基于配置的,框架后续会生成其构造函数,他们基于VueComponent,扩展于Vue
- 常见组件化技术:属性prop,自定义事件,插槽等
- 合理划分组件,有助于提升应用性能
- 组件应该是高内聚低耦合的
- 遵循单向数据流的原则
6. 谈一谈对vue设计原则的理解
- 渐进式JavaScript框架
- vue的核心库只关心视图层,易于上手,便于与第三方库或既有项目整合
- vue也完全能够为复杂的单页面应用提供驱动
- 易用、灵活和高效
- 易用性:vue提供数据响应式、声明式模板语法和基于配置的组件系统等核心特性。
- 灵活性:如果应用足够小,我们可能仅需要vue核心特性即可完成功能,随着应用规模的不断扩大,我们才会逐渐引入路由、状态管理、vue-cli等库和工具,应用体积和学习难度都是一个逐渐增加的平和曲线。
- 高效性:超快的虚拟dom和diff算法使我们的拥有最佳的性能表现
7. 谈谈你对MVC、MVP和MVVM的理解?
前端这些年从无到有、从有到优
-
1.0
- 分层架构,职责清晰,代码易维护。 服务于后端,前后端形成了一定的分离,前端只完成了后端开发中的view层
存在的问题- 前端页面开发效率不高
- 前后端职责不清
-
2.0
- ajax风靡
- 前端MVC
-
MVC
- 不灵活
- 数据流混乱
-
MVP
P指的是Presenter 客户端开发 安卓 IOS常用 -
MVVM (Model-View-ViewModel)
ViewModel可以理解为Presenter基础上的进阶版- ViewModel通过实现一套数据响应机制自动响应Model中数据变化
- 同时ViewModel会实现一套更新策略自动将数据变化转换为试图更新
- 通过事件监听响应View中用户交互修改Model中数据
总结
- 都是框架模式 都是为了解决Model和View的耦合问题
- MVC模式出现较早主要应用于后端
- MVP模式是MVC的进化形式 解决了两者耦合问题 但P层过于臃肿会导致维护问题
- MVVM模式在前端领域有广泛应用 不仅解决了耦合问题 还同时解决了维护两者映射关系的大量繁杂代码和DOM操作代码 在提高开发效率、可读性同时还保持了优越的性能表现
8. vue优化
- 路由懒加载
按需加载 - keep-alive缓存页面
- 使用v-show复用DOM
- v-for遍历避免同时使用v-if
计算属性过滤 - 长列表性能优化
- 如果列表是纯粹的数据展示,不会有任何改变,就不需要做响应化
- 如果是大数据长列表,可采用虚拟滚动,只渲染少部分区域的内容
- 事件的销毁
- 图片懒加载
参考项目:vue-lazyload - 第三方插件按需引入
- 无状态的组件标记为函数式组件
- 子组件分割
- 变量本地化
使用computed替代data - SSR
9. Vue3.0新特性
-
更快
- 虚拟dom重写
- 优化slots的生成
- 静态树提升
- 静态属性提升
- 响应式系统的改进 基于proxy
-
更小
- 通过摇树优化 核心库体积
-
更易维护
- TypeScript + 模块化
-
更友好
- 跨平台
-
更容易使用
- 改进的
- 调试TypeScript支持
- 独立的响应化模块
- Composition API
10. vuex使用及其理解
- 定义
- 状态管理模式、 集中式(store的类)、 可预测
11. Vue中组件之间的通信方式
8种:props、on、parent、listeners、ref、$root、eventbus、vuex
-
父子组件
props$emit/$on$children/$parent源码中 有更高层次的封装 通用组件不能用vuex让用户强绑定ref$attrs/$listeners
-
兄弟组件
$parent$rooteventbusvuex
-
跨层级关系
eventbusvuexprovide / inject
12. vue-router中如何保护指定路由的安全
-
阐述vue-router中路由保护策略
导航守卫判断用户的登陆状态和权限
-
具体实现方式 (几个层级)
-
全局前置守卫beforeEach
每次导航都会执行该守卫、判断、通过next函数传递多参数- 禁止 next(false)
- 正常放行可以不传递参数 传递path字符串可以重定向到一个新的地址
-
路由独享守卫beforeEnter
-
组件内守卫beforeRouterEnter
-
-
简单说一下它们是怎么生效的
- 跟vue-router工作方式有关,像beforeEach只是注册一个hook,(next是高阶函数)
- router准备导航之前会批量执行这些hooks
- 并且把目标路由to,当前路由from,以及后续处理函数next传递给我们设置的hook\
-
能不能说说全局守卫、路由独享守卫和组件内守卫区别?
- 作用范围
- 组件实例的获取
// 只有组件内守卫可以获取 beforeRouteEnter(to, from, next){ next (vm => {}) } - 名称/数量/顺序
- 导航被触发
- 在失活的组件里调用离开守卫
- 调用全局的
beforeEach守卫 - 在重用的组件里调用
beforeRouteUpdate守卫(2.2+) - 在路由配置里调用
beforeEnter - 解析
异步组件 - 在被激活的组件里调用
beforeRouterEnter - 调用全局的
beforeResolve守卫(2.5+) - 导航被确认
- 调用全局的
afterEach钩子 - 触发DOM更新
- 用创建好的实例调用
beforeRouterEnter守卫中传给next的回调函数
-
你项目中的路由守卫是怎么做的?
-
前后端路由一样吗?
不太一样 -
前端路由是用什么方式实现的?
-
next方法是怎么实现的?
13. nextTick
考查对vue异步更新队列的理解
-
nextTick是啥?下定义
Vue.nextTick([callabck, context])- 在下次DOM更新循环结束之后执行延迟回调。修改数据之后立即使用这个方法,获取更新后的DOM。
-
为什么需要它?
Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作是非常重要的。nextTick方法会在队列中加入一个回调函数,确保该函数在前面的dom操作完成后才调用。 -
什么地方用?
修改数据后想要立即看到执行结果就需要用到nextTick方法 -
如何使用nextTick?
传递一个回调函数 在里面执行dom操作源码实现
14. 对vue响应式的理解
-
啥是响应式?
使数据变化可以被检测,并做出相应的机制 -
为什么vue需要响应式?
通过数据驱动视图更新 -
它能给我们带来什么好处?
- 通过数据响应式加上虚拟DOM和patch算法 只需操作数据 完全不用频繁操作DOM
- 提升开发效率 降低开发难度
-
vue的响应式是怎么实现的?有哪些优缺点?
- 优点:根据数据类型来做不同处理
对象采用Object.defineProperty()数组覆盖该数组原型的方法,扩展它的7个变更方法,使其可做额外更新通知
- 缺点
- 初始化的递归遍历会造成性能损失
- 新增或者删除属性的时候需要用
Vue.set/delete这样的api才能生效 - 对于es6中新产生的
Map、Set这些数据结构不支持
- 优点:根据数据类型来做不同处理
-
vue3中的响应式的新变化