Vue

Vue 路由实现方式?

  • 路由:通过一定的机制,监听用户的行为动作,从而做出对应的变化。

  • 常见两种方式:

  1. hash 模式

    浏览器暴露一个现成的方法 hashchange,在 hash 改变的时候,触发该事件。有了监听事件,且改变 hash 页面并不刷新,这样我们就可以在监听事件的回调函数中,执行我们展示和隐藏不同 UI 显示的功能,从而实现前端路由。hash 路由的核心实现,主要就是监听 hash 的变化,渲染不同的组件代码。

    总结

    1. hash 模式所有的工作都是在前端完成的,不需要后端服务的配合
    2. hash 模式的实现方式就是通过监听 URLhash 部分的变化,从而做出对应的渲染逻辑
    3. hash 模式下,URL 中会带有#,看起来不太美观
  2. history 模式

    history 路由模式的实现,是要归功于 HTML5 提供的一个 history 全局对象,可以将它理解为其中包含了关于我们访问网页(历史会话)的一些信息。同时它还暴露了一些有用的方法,比如:

    1. window.history.go 可以跳转到浏览器会话历史中的指定的某一个记录页
    2. window.history.forward 指向浏览器会话历史中的下一页,跟浏览器的前进按钮相同
    3. window.history.back 返回浏览器会话历史中的上一页,跟浏览器的回退按钮功能相同
    4. window.history.pushState 可以将给定的数据压入到浏览器会话历史栈中
    5. window.history.replaceState 将当前的会话页面的 url 替换成指定的数据

    history 路由的实现,主要就是依靠于 pushStatereplaceState 实现的

    • 特点:
    1. 都会改变当前页面显示的 url,但都不会刷新页面
    2. pushState 是压入浏览器的会话历史栈中,会使得 history.length 加 1,而 replaceState 是替换当前的这条会话历史,因此不会增加 history.length
    3. history.pushStatehistory.replaceState 方法是不会触发 popstate 事件的
    4. 但是浏览器的某些行为会导致 popstate,比如 go、back、forward
    5. popstate 事件对象中的 state 属性,可以理解是我们在通过 history.pushStatehistory.replaceState 方法时,传入的指定的数据

    重点:重写pushStatereplaceState 方法

provide & inject

小型状态管理器

自定义指令

生命周期

过滤器

Vue.set

forceUpdate

Object.freeze

长列表渲染

常用指令

常用事件

图片懒加载

按需引入

语法糖

nextTick

key 的作用

常见问题

1、组件中 data 为什么是函数?

  因为当一个组件被引用多次时,相当于创建了多个实例。这些实例用的都是同一个构造函数。若data是对象的话,属于引用类型,修改其他组件属性时会影响到其他组件的实例。故,为了保证组件不同的实例之间 data 不冲突,data 必须是一个函数!

2、v-ifv-show的区别?

区别如下:

  • v-ifv-show 用于视图层进行条件判断视图展示
  • v-if 根据判断条件来动态的进行增删 DOM 元素,v-show 是判断条件来动态的进行显示和隐藏元素

总结

  1. 若需要非常频繁地切换,则使用 v-show 较好
  2. 若如果在运行时条件很少改变,则使用 v-if 较好

3、v-model 的原理?

v-model本质就是一个语法糖,可以看成是value + input方法的语法糖。 可以通过 model 属性的propevent属性来进行自定义。原生的 v-model,会根据标签的不同生成不同的事件和属性。

4、Vue模版编译原理?

本质:将template模板 转换为 render函数,经历以下阶段:

  • 生成 AST 树
  • 优化
  • codegen

第一、首先解析模版,生成 AST 语法树(一种用 JavaScript 对象的形式来描述整个模板)。 使用大量的正则表达式对模板进行解析,遇到标签、文本的时都会执行对应的钩子进行相关处理。

第二、Vue 的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的 DOM 也不会变化。那么优化过程就是深度遍历 AST 树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。

5、谈一谈keep-alive

keep-aliveVue的内置组件, 可以实现组件缓存,当组件切换时不会对当前组件进行卸载。

  • 常用属性:1)、include 2)、exclude, 允许组件有条件的进行缓存。

  • 生命周期:1)、activated 2)、deactivated, 用来得知当前组件是否处于活跃状态。

keep-alive的中还运用了LRU(Least Recently Used)算法。

6、hash路由和history路由实现原理?

  • location.hash 的值实际就是 URL 中#后面的东西。
  • history实际采用了HTML5中提供的 API 来实现,主要有history.pushState()history.replaceState()

7、Vue2.xVue3.x渲染器的diff算法分别说一下?

diff算法有以下过程:

  • 同级比较,再比较子节点
  • 先判断一方有子节点一方没有子节点的情况(如果新的 children 没有子节点,将旧的子节点移除)
  • 比较都有子节点的情况(核心 diff)
  • 递归比较子节点

  正常 Diff 两个树的时间复杂度是 O(n^3),但实际情况下我们很少会进行跨层级的移动 DOM,所以 Vue 将 Diff 进行了优化,从 O(n^3) -> O(n),只有当新旧 children 都为多个子节点时才需要用核心的 Diff 算法进行同层级比较。

  Vue2 的核心 Diff 算法采用了双端比较的算法,同时从新旧 children 的两端开始进行比较,借助 key 值找到可复用的节点,再进行相关操作。相比 React 的 Diff 算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅。

Vue3.x 借鉴了 ivi算法inferno算法

  在创建 VNode 时就确定其类型,以及在 mount/patch 的过程中采用位运算来判断一个 VNode 的类型,在这个基础之上再配合核心的 Diff 算法,使得性能上较 Vue2.x 有了提升。

该算法中还运用了动态规划的思想求解最长递归子序列。

8、vue2.x中如何监测数组变化?

  使用了函数劫持的方式,重写了数组的(push、pop、shift、unshift、splice、sort、reverse)方法,Vue 将 data 中的数组进行了原型链重写,指向了自己定义的数组原型方法。这样当调用数组 api 时,可以通知依赖更新。如果数组中包含着引用类型,会对数组中的引用类型再次递归遍历进行监控。这样就实现了监测数组变化。

9、Vue2.x响应式数据原理?

  Vue 在初始化数据时,会使用 Object.defineProperty 重新定义 data 中的所有属性,当页面使用对应属性时,首先会进行依赖收集(收集当前组件的 watcher)如果属性发生变化会通知相关依赖进行更新操作(发布订阅)。

流程如下:

  1. 当一个 JavaScript 对象作为 Vue 的 data 项被传入的时候,Vue 会遍历该对象。并使用 Object.defineProperty 把每个属性转为 getter/setter。就是把 data 里的数据转为图上紫色的部分。
  2. Vue 使用 getter/setter,在内部追踪收集的依赖。在属性被访问或修改的时候,发出变更通知。
  3. 每个组件中都有一个 watcher,在组件渲染的时候,watcher 会把每个 touch 过的数据属性记录依赖,也就是会触发 getter 然后收集依赖(Collect as Dependency)。在属性被 setter 的时候,由依赖 dep 通知 watcher,重新渲染关联的组件。

10、Vue3.x 响应式数据原理?

  Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。

问:Proxy 只会代理对象的第一层,那么 Vue3 又是怎样处理这个问题的呢?

答:判断当前 Reflect.get 的返回值是否为 Object,如果是则再通过 reactive 方法做代理, 这样就实现了深度观测。

问:监测数组的时候可能触发多次 get/set,那么如何防止触发多次呢?

答:可以判断 key 是否为当前被代理对象 target 自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行 trigger。

Vue2.x 与 Vue3.0 对比

  • Vue2.x 采用 flow 进行编写,而 3.0 源码全部采用 TypeScript 进行开发,对 TypeScript 支持友好
  • 源码体积优化 移除部分 api 使用 tree-shaking
  • 数据劫持优化 Vue3 采用 Proxy,性能大大提升
  • 编译优化:Vue3 实现了静态模板分享,重新 diff 算法
  • CompositionAPI 整合业务代码的逻辑,提取公共逻辑(Vue2.0 采用-Mixin - 命名冲突,数据来源不清晰)
  • 自定义渲染器,可以用来创建自定义的渲染器,改写 Vue 底层渲染逻辑
  • 新增 Fragment Teleport Suspense 组件