乐趣区

关于javascript:关于vue的所有面试题

# 1 请说一下响应式数据的了解?
响应式数据分为两种,对象和数组。

  1. 对象

对象外部通过 defineReactive 办法,该办法是应用 Object.defineProperty 将属性进行劫持(只会劫持曾经存在的属性)。
比方多层对象是通过递归来实现劫持。(Vue3 是用 proxy 来实现的)

  1. 数组

数组则是通过重写数组办法来实现。创立一个新的原型对象,该对象继承自 Array.prototype,新对象在对数组的办法进行改写。

  • 引申

(1)对象层级过深,性能就会差(2)不须要响应数据的内容不要放到 data 中(3)Object.freeze() 能够解冻数据

2 Vue 如何检测数组变动?

Vue 在 observer 数据阶段会判断如果是数组的话,则批改数组的原型,这样的话,前面对数组的任何操作都能够在劫持的过程中管制。

const arrayProto = Array.prototype// 原生 Array 的原型
export const arrayMethods = Object.create(arrayProto);
[
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
].forEach(function (method) {const original = arrayProto[method]// 缓存元素数组原型
  // 这里重写了数组的几个原型办法
  def(arrayMethods, method, function mutator () {
    // 这里备份一份参数应该是从性能方面的思考
    let i = arguments.length
    const args = new Array(i)
    while (i--) {args[i] = arguments[i]
    }
    const result = original.apply(this, args)// 原始办法求值
    const ob = this.__ob__// 这里 this.__ob__指向的是数据的 Observer
    let inserted
    switch (method) {
      case 'push':
        inserted = args
        break
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
  })
})

// 定义属性
function def (obj, key, val, enumerable) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  });
}

* 引申
在 Vue 中批改数组的索引和长度是无奈监控到的。须要通过以上 7 种变异办法批改数组才会触发数组对应的 watcher 进行更新。数组中如果是对象数据类型也会进行递归劫持。那如果想更改索引更新数据怎么办?能够通过 Vue.$set()来进行解决 =》外围外部用的是 splice 办法

3 Vue 中模板编译原理?

1 先查找是否有 render 函数,没有的话看是否传入了 template, 如果传入了将 template 转化为 render 函数,如果仍没有,会回去 el 的 outerHtml 模板内容,将其转化成 render 函数。总之是转化成 render 函数。
2 字符串模板转化成为 render 函数的过程:用正则解析模板字符串生成 AST 语法树。将生成的 ast 语法树拼接成字符串,其中蕴含了 _c,_v,_s 等办法形容元素的节点,文本节点以及变量。而后把拼接好的字符串通过 new Function(with(this){return ${code}})转化成 render 函数

4 生命周期钩子是如何实现的?

1 通过 Vue.mixin 定义全局生命钩子,Vue.mixin 可调用屡次,会定义不同的合并策略,生命周期钩子的合并策略就是为每个钩子函数创立一个数组,每次定义的钩子函数保留在对应的数组中,并最终挂载到 Vue.$options 上
2 Vue 实例初始化会将 Vue.$options 上的全局钩子函数和实例的钩子函数合并,挂载到 options 上,最初在实例的不同阶段 通过 callHook 函数从 $options 中取出对应的钩子函数数组,遍历数组,顺次执行生命周期函数。

  • Vue 的生命周期钩子就是回调函数而已,外围是一个公布订阅模式

5 Vue.mixin 的应用场景和原理

Vue.mixin 的作用就是抽离公共的业务逻辑,原理相似“对象的继承”,当组件初始化时会调用 mergeOptions 办法进行合并,采纳策略模式针对不同的属性进行合并。如果混入的数据和自身组件中的数据抵触,会采纳“就近准则”以组件的数据为准。

6 nextTick 在哪里应用? 原理是?

nextTick 中的回调是在下次 DOM 更新循环完结之后执行的提早回调。在批改数据之后立刻应用这个办法,获取更新后的 DOM。原理就是异步办法 (promise,mutationObserver,setImmediate,setTimeout) 常常与事件环一起来问(宏工作和微工作)

7 Vue 为什么须要虚构 DOM?

Virtual DOM 就是用 js 对象来形容实在 DOM,是对实在 DOM 的形象,因为间接操作 DOM 性能低然而 js 层的操作效率高,能够将 DOM 操作转化成对象操作,最终通过 diff 算法比对差别进行更新 DOM(缩小了对实在 DOM 的操作)。虚构 DOM 不依赖实在平台环境从而也能够实现跨平台。实质上就是在 JS 和 DOM 之间的一个缓存。

8 Vue 中的 diff 原理

  • 1. 先比拟是否是雷同节点
  • 2. 雷同节点比拟属性, 并复用老节点
  • 3. 比拟儿子节点,思考老节点和新节点儿子的状况
  • 4. 优化比拟:头头、尾尾、头尾、尾头
  • 5. 比对查找进行复用

9 既然 Vue 通过数据劫持能够进准探测数据变动,为什么还须要虚构 DOM 进行 diff 检测差别?

响应式数据变动,Vue 的确能够在数据发生变化时, 响应式零碎能够立即得悉。然而如果给每个属性都增加 watcher 用于更新的话,会产生大量的 watcher 从而升高性能。而且粒度过细也会导致更新不精准的问题,所以 vue 采纳了组件级的 watcher 配合 diff 来检测差别。这里能够在讲一下 diff 的原理

10 Vue 中 computed 和 watch 的区别

computed 和 watch 都是基于 Watcher 来实现的,别离是计算属性 watcher 和用户 watcher。computed 属性是具备缓存的,依赖的值不发生变化,对其取值时计算属性办法不会从新执行(能够用模板渲染,取值的过程中不反对异步办法)watch 则是监控值的变动,当值发生变化时调用对应的回调函数。computed 不会立刻执行,外部通过 defineProperty 进行定义。并且通过 dirty 属性来检测依赖的数据是否发生变化。watch 则是立刻执行将老值保留在 watcher 上,当数据更新时从新计算新值,将新值和老值传递到回调函数中。

11 Vue.set 办法是如何实现的?

咱们给对象和数组自身都减少了 dep 属性。当给对象新增不存在的属性则触发对象依赖的 watcher 去更新,当批改数组索引时咱们调用数组自身的 splice 办法去更新数组

12 Vue 的生命周期办法有哪些?个别在哪一步发动申请及起因

  • beforeCreate 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
  • created 实例曾经创立实现之后被调用。在这一步,实例已实现以下的配置:数据观测(data observer),属性和办法的运算,watch/event 事件回调。这里没有 $el
  • beforeMount 在挂载开始之前被调用:相干的 render 函数首次被调用。
  • mounted el 被新创建的 vm.$el 替换,并挂载到实例下来之后调用该钩子。
  • beforeUpdate 数据更新时调用,产生在虚构 DOM 从新渲染和打补丁之前。
  • updated 因为数据更改导致的虚构 DOM 从新渲染和打补丁,在这之后会调用该钩子。
  • beforeDestroy 实例销毁之前调用。在这一步,实例依然齐全可用。
  • destroyed Vue 实例销毁后调用。调用后,Vue 实例批示的所有货色都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

    ♥钩子函数的作用

  • created 实例曾经创立实现,因为它是最早触发的起因能够进行一些数据,资源的申请。(服务端渲染反对 created 办法)
  • mounted 实例曾经挂载实现,能够进行一些 DOM 操作
  • beforeUpdate 能够在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
  • updated 能够执行依赖于 DOM 的操作。然而在大多数状况下,你应该防止在此期间更改状态,因为这可能会导致更新有限循环。该钩子在服务器端渲染期间不被调用。
  • destroyed 能够执行一些优化操作, 清空定时器,解除绑定事件

♥在哪发送申请都能够,次要看具体你要做什么事

13 vue-router 有几种钩子函数?别离用在什么中央

1 全局钩子函数

  • router.beforeEach
  • router.beforeResolve(这和 router.beforeEach 相似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
  • router.afterEach

2 路由独享的守卫

beforeEnter

const router = new VueRouter({
   routes: [
     {
       path: '/foo',
       component: Foo,
       beforeEnter: (to, from, next) => {// ...}
     }
   ]
 })
3 组件内的守卫
  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创立
  },
  beforeRouteUpdate (to, from, next) {
    // 在以后路由扭转,然而该组件被复用时调用
    // 举例来说,对于一个带有动静参数的门路 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,// 因为会渲染同样的 Foo 组件,因而组件实例会被复用。而这个钩子就会在这个状况下被调用。// 能够拜访组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航来到该组件的对应路由时调用
    // 能够拜访组件实例 `this`
  }
}

残缺的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创立好的组件实例会作为回调函数的参数传入。

14 vue-router 的两种模式的区别

hash 模式

url 地址前面 hash 值的变动,并 不会导致浏览器向服务器发出请求 ,浏览器不发出请求,也就不会刷新页面。每次 hash 值的变动,会 触发 hashchange 这个事件,通过这个事件咱们就能够晓得 hash 值产生了哪些变动。而后咱们便能够 监听 hashchange 来实现更新页面局部内容的操作

hash 模式背地的原理是 onhashchange 事件, 能够在 window 对象上监听这个事件

history 模式

因为 HTML5 规范公布,多了两个 API,pushState()replaceState()。通过这两个 API(1)能够扭转 url 地址且不会发送申请,(2)不仅能够读取历史记录栈,还能够对 浏览器历史记录栈进行批改。

除此之外,还有 popState(). 当浏览器跳转到新的状态时,将触发 popState 事件.
当刷新时,如果服务器中没有相应的响应或者资源,会呈现 404

退出移动版