写点什么

「每日一题」抖音面试题:请阐述 vue 数据绑定的实现原理

发布于: 2021 年 01 月 04 日

vue 数据绑定的实现原理?

这个题目本身不是特别难,只能说是作为社招的基础面试题,但是如果想回答好这道题也不是很容易。


不信接着往下看


1、概括回答

vue.js 是一个非常优秀的前端开发框架,使用 vue 的版本是 v2.x


vue 几个核心的地方:vue 实例化,虚拟 DOM,模板编译过程,数据绑定。


我们开始回到正题,vue.js 的作者尤雨溪最初就是尝试实现一个类似 angular1 的东西,发现里面对于数据处理非常不优雅,于是创造性的尝试利用 ES5 中的 Object.defineProperty 来实现数据绑定,于是就有了最初的 vue。


vue 的数据绑定的实现原理离不开 vue 中响应式的数据处理方式。


我们可以回想一下官网的图:



vue 的响应式基本原理:


  • 1、vue 会遍历此 data 中对象所有的属性,

  • 2、并使用 Object.defineProperty 把这些属性全部转为 getter/setter,

  • 3、而每个组件实例都有 watcher 对象,

  • 4、它会在组件渲染的过程中把属性记录为依赖,

  • 5、之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。


2、亮点回答

概括回答我们只回答了使用 ES5 的方法 Object.defineProperty 实现数据的监听的,那么具体是如何实现还是没有讲的很清楚。


这时候我们需要问自己,如何找亮点?


vue 的响应式原理设计三个重要对象:Observer,Watcher,Dep。


  • Observer 对象:vue 中的数据对象在初始化过程中转换为 Observer 对象。

  • Watcher 对象:将模板和 Observer 对象结合在一起生成 Watcher 实例,Watcher 是订阅者中的订阅者。

  • Dep 对象:Watcher 对象和 Observer 对象之间纽带,每一个 Observer 都有一个 Dep 实例,用来存储订阅者 Watcher。


当属性变化会执行主题对象 Observer 的 dep.notify 方法, 这个方法会遍历订阅者 Watcher 列表向其发送消息, Watcher 会执行 run 方法去更新视图。


依赖关系图如下,更能方面我们的理解



接着我们需要补充的是:模板编译过程中的指令和数据绑定都会生成 Watcher 实例,实例中的 watch 属性也会生成 Watcher 实例。


说的这些有没有觉得有点乱,那我们总结一下如何亮点回答


  • 1、在生命周期的 initState 方法中将 data,prop,method,computed,watch 中的数据劫持, 通过 observe 方法与 Object.defineProperty 方法将相关对象转为换 Observer 对象。

  • 2、然后在 initRender 方法中解析模板,通过 Watcher 对象,Dep 对象与观察者模式将模板中的 指令与对象的数据建立依赖关系,使用全局对象 Dep.target 实现依赖收集。

  • 3、当数据变化时,setter 被调用,触发 Object.defineProperty 方法中的 dep.notify 方法, 遍历该数据依赖列表,执行器 update 方法通知 Watcher 进行视图更新。

  • vue 是无法检测到对象属性的添加和删除,但是可以使用全局 Vue.set 方法(或 vm.$set 实例方法)。

  • vue 无法检测利用索引设置数组,但是可以使用全局 Vue.set 方法(或 vm.$set 实例方法)。

  • 无法检测直接修改数组长度,但是可以使用 splice


然后写一个使用 Object.defineProperty 实现监听变量


var obj = {};var a;Object.defineProperty(obj, 'a', {  get: function() {    console.log('get val');     return a;  },  set: function(newVal) {    console.log('set val:' + newVal);    a = newVal;  }});obj.a;     // get val obj.a = 'saucxs'    //set val
复制代码


如果上面代码格式出现问题,可以查看下面代码图片



3、进阶回答

因为现在 vue 已经到 3 了,不再是停留在 2 的时候,这个时候,可以把 3 的原理简单说一下。


这个时候不应该是 ES6 的 proxy 特性上场了,proxy 是 ES6 的新增的功能,可以用来定义对象中的操作。


let p = new Proxy(target, handler);// `target` 代表需要添加代理的对象// `handler` 用来自定义对象中的操作
复制代码


如果上面代码格式出现问题,可以查看下面代码图片



可以很方便的使用 Proxy 来实现一个数据绑定和监听.


let onWatch = (obj, setBind, getLogger) => {  let handler = {    get(target, property, receiver) {      getLogger(target, property)      return Reflect.get(target, property, receiver);    },    set(target, property, value, receiver) {      setBind(value);      return Reflect.set(target, property, value);    }  };  return new Proxy(obj, handler);};
let obj = { saucxs: 1 }let valuelet p = onWatch(obj, (v) => { value = v}, (target, property) => { console.log(`Get '${property}' = ${target[property]}`);})p.saucxs = songEagle // bind `value` to `songEagle`p.saucxs // -> Get 'saucxs' = songEagle
复制代码


如果上面代码格式出现问题,可以查看下面代码图片



然后在对比 vue2 和 vue3 的区别是什么?


以及为啥在数据监听上做了升级?


vue 为什么对数组对象的深层监听无法实现,因为组件每次渲染都是将 data 里的数据通过 defineProperty 进行响应式或者双向绑定上,之前没有后加的属性是不会被绑定上,也就不会触发更新渲染。


区别:


1、语法层面上


  • defineProperty 只能响应首次渲染时候的属性,

  • Proxy 需要的是整体监听,不需要关心里面有什么属性,而且 Proxy 的配置项有 13 种,可以做更细致的事情,这是之前的 defineProperty 无法达到的。


2、兼容层面上


  • vue2.x 之所以只能兼容到 IE8 就是因为 defineProperty 无法兼容 IE8,其他浏览器也会存在轻微兼容问题。

  • proxy 的话除了 IE,其他浏览器都兼容,这次 vue3 还是使用了它,说明 vue3 直接放弃了 IE 的兼容考虑。


写在最后

每日一题,带你一起学习新技术,总结学习过程,让你进阶到高级资深工程师,学习项目管理,思考职业发展,生活感悟,充实中成长起来。问题或建议,请在文末留言评论。


用户头像

还未添加个人签名 2020.09.07 加入

还未添加个人简介

评论

发布
暂无评论
「每日一题」抖音面试题:请阐述vue数据绑定的实现原理