上周五跟着一个师姐面试一个三年工作教训的前端开发,我在一边审慎的观摩。想着已经我也被他人面试过,现在面试他人,感觉其实情绪是一样的。

前言

  • 工作三年的Vue使用者应该懂什么?
  • 为何工作几年的根底越来越弱?
  • 工作如何挤出工夫学习?

一道面试题

其实咱们并不是要你把答案都记下来,而是把其中的思维学习到。就像你接触一个新的畛域react,你也一样能够把根本思维提炼进去。

面试题: Vue是如何对数据进行监听的?

这其实是陈词滥调的问题,凡是你有一点基础知识,你也能答出一二。师姐跟我说,其实问题不只是问题自身,而是跟这个常识顺带进去的体系。

01 对象数据是怎么被监听的

在vue2.x版本中,数据监听是用过Object.defineProperty这个API来实现的,咱们能够来看一个例子

var text = 'vue';const data = {};Object.defineProperty(data, 'text', {    get() {        return text;    },    set(newVal) {        text = newVal;    }});data.text // 'vue'data.text = 'react' // 'react'

当咱们拜访或设置对象的属性的时候,都会触发绝对应的函数,而后在这个函数里返回或设置属性的值。咱们当然能够在触发函数的时候做咱们本人想做的事件,这也就是“劫持”操作。

在Vue中其实就是通过Object.defineProperty来劫持对象属性的setter和getter操作,并创立一个监听器,当数据发生变化的时候发出通知。

var data = {    name:'hello',    age:18}Object.keys(data).forEach(function(key){    Object.defineProperty(data,key,{        enumerable:true, // 是否能在for...in循环中遍历进去或在Object.keys中列举进去。        configurable:true, // false,不可批改、删除指标属性或批改属性性以下个性        get:function(){            console.log('获取数据');        },        set:function(){            console.log('监听到数据产生了变动');        }    })});data.name //控制台会打印出 “获取数据”data.name = 'world' //控制台会打印出 "监听到数据产生了变动"

02 数组数据是怎么被监听的

咱们晓得,下面是对对象的数据进行监听的,咱们不能对数组进行数据的“劫持”。那么Vue是怎么做的呢?

import { def } from '../util/index'const arrayProto = Array.prototypeexport const arrayMethods = Object.create(arrayProto)const methodsToPatch = [  'push',  'pop',  'shift',  'unshift',  'splice',  'sort',  'reverse']methodsToPatch.forEach(function (method) {  // 缓存原来的办法  const original = arrayProto[method]  def(arrayMethods, method, function mutator (...args) {    const result = original.apply(this, args)    const ob = this.__ob__    let inserted    switch (method) {      case 'push':      case 'unshift':        inserted = args        break      case 'splice':        inserted = args.slice(2)        break    }    if (inserted) ob.observeArray(inserted)    // notify change    ob.dep.notify()    return result  })})

看来Vue能对数组进行监听的起因是,把数组的办法重写了。总结起来就是这几步:

01先获取原生 Array 的原型办法,因为拦挡后还是须要原生的办法帮咱们实现数组的变动。

02对 Array 的原型办法应用 Object.defineProperty 做一些拦挡操作。

03把须要被拦挡的 Array 类型的数据原型指向革新后原型。

参考:前端vue面试题具体解答

Vue为什么不能检测数组变动

并不是说 JS 不能反对响应式数组,其实JS是没有这种限度的。

数组在 JS 中常被当作栈,队列,汇合等数据结构的实现形式,会有批量的数据以待遍历。并且 runtime 对对象与数组的优化也有所不同。

所以对数组的解决须要特化进去以进步性能。

Vue 中是通过对每个键设置 getter/setter 来实现响应式的,开发者应用数组,目标往往是遍历,此时调用 getter 开销太大了,所以 Vue 不在数组每个键上设置,而是在数组上定义 __ob__ ,并且替换了 push 等等可能影响原数组的原型办法。

为此也有人去GitHub问了尤大,他的答复也是说因为性能问题而没有采纳这种形式监听数组。github.com/vuejs/vue/i…

源码地位:src/core/observer/index.js

constructor (value: any) {    this.value = value    this.dep = new Dep()    this.vmCount = 0    def(value, '__ob__', this)    if (Array.isArray(value)) {      if (hasProto) {        protoAugment(value, arrayMethods)      } else {        copyAugment(value, arrayMethods, arrayKeys)      }      this.observeArray(value)    } else {      this.walk(value)    }  }  /**   * Walk through all properties and convert them into   * getter/setters. This method should only be called when   * value type is Object.   */  walk (obj: Object) {    const keys = Object.keys(obj)    for (let i = 0; i < keys.length; i++) {      defineReactive(obj, keys[i])    }  }

通过源码咱们能够晓得,Vue 没有对数组每个键设置响应式的过程,而是间接对值进行递归设置响应式。

[外链图片转存失败,源站可能有防盗链机制,倡议将图片保留下来间接上传(img-Qa1W9i2a-1665629236626)(https://imgkr2.cn-bj.ufileos....)]

$set为啥能检测数组变动

还是去源码瞅一眼,看vue是怎么对数组进行解决的。

源码地位:dist/vue.runtime.esm.js

function set (target, key, val) {  //...   if (Array.isArray(target) && isValidArrayIndex(key)) {    target.length = Math.max(target.length, key);    target.splice(key, 1, val);    return val  }  if (key in target && !(key in Object.prototype)) {    target[key] = val;    return val  }  //...   defineReactive$$1(ob.value, key, val);  ob.dep.notify();  return val}

  • 如果target是一个数组且索引无效,就设置length的属性。
  • 通过splice办法把value设置到target数组指定地位。
  • 设置的时候,vue会拦挡到target发生变化,而后把新增的value也变成响应式
  • 最初返回value

这就是vue重写数组办法的起因,利用数组这些办法触发使得每一项的value都是响应式的。

答复思维

正如之前所说,面试一道题目不在乎外表你答复如许精确,实际上在乎的是其中的前因后果。咱们由浅入深,一步步解密其中的原理,这才是学习的思维。