# 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