vue技巧

常见问题题的总结

v-show 与 v-if 区别

v-show是css切换,v-if是完整的销毁或重建.频繁切换用v-show.较少改变用v-if,或者需要销毁组件、应用完整的生命周期也用v-if.

computed和watch的区别

computed本质是一个惰性求值的观察者,其内部实现了一个惰性的watcher即computed watcher.它不会在依赖变化时立即计算求值,首先会看有没有地方读取这个计算属性,有的话才会计算,并且比较新旧值,只有值变了才会触发渲染.(内部有this.dirty标记是否重新计算).
computed看重的是结果,要获取某个动态值并且依赖于其它数据时用它.并且它有缓存性,不会每次获取值都重新计算.
watch监听器是监听某个数据的改变,是一个动作,发现改变,即可触发回调,处理相应逻辑.

组件中data为什么是函数

为什么new Vue实例中data能是对象?

组件是能复用的,而js对象是引用关系,复用的组件data属性值会互相污染,产生副作用.作为函数时,每个实例维护一份对象的独立拷贝.vue实例不存在复用,所以没问题.

怎样理解单向数据流

组件通信中,父组件通过props传递数据到子组件,子组件不能修改props值,否则会报错,想改需要通过$emit自定义事件,由父组件接受更改值.

组件通信

  • 父子:props/emit、$parent/$children、ref、provide/inject
  • 兄弟:bus、vuex
  • 跨级:bus、vuex、provide/inject

$nextTick原理

vue更新dom是异步执行的,当数据更新,vue会开启一个队列,并缓存在同一事件循环中发生的所有数据变更.
如果同一watcher多次触发,会进行去重操作保证只会被推入队列一次,避免不必要的计算和dom操作.
vue内部对队列使用原生的微任务(promise,mutationObserver)或宏任务(setImmediate,setTimeout),环境不支持微任务时,宏任务属于降级处理,nextTick就是用任务队列,保证在下一个事件循环队列(dom更新后)中执行.

keep alive原理

它是vue内置抽象组件,不会渲染成dom,也不会出现在父组件链中,能缓存组件,缓存的是v-node,分析其源码知道,当组件需要被缓存时会将v-node缓存,下次渲染时,有缓存则直接拿出渲染.不需要缓存的话就直接render并渲染.
当设置了max时,其用LRU缓存淘汰算法,置换缓存数据,保证最近被访问的数据在内存块头部,不被访问的会慢慢被挤到尾部,直至淘汰.保证数据范围在max值内.

$set原理

vue2中,vue不能检测对象属性的添加或删除,因为响应式数据是通过Object.defineProperty递归遍历,劫持属性的.当新增或删除时不能做到响应式,需要再次劫持新属性才行,$set做的就是再次劫持,将新属性变成响应式.

vue 是如何对数组方法进行变异的 ?

Vue通过原型拦截的方式重写了数组的7个方法,触发拦截动作时调用Observer劫持数据并手动触发更新.

Vue 中的 key 到底有什么用?

key是给每一个vnode的唯一id,在diff操作时能更准确,更快速.避免就地复用,状态错位的情况.

vue响应式原理

通过数据劫持和发布-订阅模式实现.
observer类通过defineProperty的getter和setter劫持数据收集依赖和派发更新.
dep订阅器管理watcher实例,并触发notify更新通知watcher.
watcher观察者分为renderWatcher(渲染),computedWatcher(计算),userWatcher(监听器)三种.收到更新通知后调用相应的update方法.

vue渲染过程

实例化vue

  1. 首先初始化一些东西:
    • 合并options
    • lifecycle生命周期初始化
    • events事件系统初始化
    • render相关属性方法初始化
    • beforeCreate周期调用
    • state数据、方法、props、watcher、computed等初始化
    • created周期调用
  2. $mount()调用,开始解析,编译,渲染更新
    • compile编译阶段,目的是将模板版已成render函数,核心方法是compileToFunctions
      • parse函数解析模板,生成ast抽象语法树
      • optimize标记静态节点,后续diff时能直接略过
      • generate将ast转为render函数字符串
    • beforeMount周期调用
    • new Watcher监听数据的变化,当数据发生变化时,Render 函数执行生成 vnode 对象
    • 调用 patch 方法,对比新旧 vnode 对象,通过 DOM diff 算法,添加、修改、删除真正的 DOM 元素,生成真实dom
    • mounted周期调用
  3. 当数据更新时
    • beforeUpdate周期调用
    • re-render&patch
    • updated
  4. 销毁组件
    • beforeDestroy
    • 卸载watcher和eventListener
    • destroyed

路由守卫

  • 全局前置守卫
    • router.beforeEach(async (to, from, next) => {})
  • 全局解析守卫
    • router.beforeResolve(async (to, from, next) => {})
    • 执行时机是before系里最后的,详见下方
  • 全局后置钩子
    • router.afterEach(async (to, from) => {})
  • 路由独享守卫
    • beforeEnter
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      function fn1 (to, from, next) {}
      function fn2 (to, from, next) {}
      const routes = [
      {
      path: '/users/:id',
      component: UserDetails,
      beforeEnter: (to, from, next) => {
      // reject the navigation
      return false
      },
      // beforeEnter: [fn1, fn2, ...] 也可传递函数数组,这样每个函数都会被执行
      },
      ]

  • 组件级守卫
    • beforeRouteEnter // 此时访问不到this,用next(vm => {})
    • beforeRouteUpdate
    • beforeRouteLeave

完整的导航解析流程

  • 导航被触发。
  • 在失活的组件里调用 beforeRouteLeave 守卫。
  • 调用全局的 beforeEach 守卫。
  • 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
  • 在路由配置里调用 beforeEnter。
  • 解析异步路由组件。
  • 在被激活的组件里调用 beforeRouteEnter。
  • 调用全局的 beforeResolve 守卫(2.5+)。
  • 导航被确认。
  • 调用全局的 afterEach 钩子。
  • 触发 DOM 更新。
  • 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例- 会作为回调函数的参数传入。

参考:

vue高频原理面试题
面试重点-vue原理
15个 Vue.js 高级面试题
很全面的vue面试题总结
vue常见面试题
Vue面试总结
Vue常见面试题总结
2021 Vue经典面试题总结
MVVM与MVC
MVVM和MVC的区别,以及MVVM的缺点
一个项目学会前端实现登录拦截