面试官不想和你说话并向你扔了一个MVVM原理


先讲个方法

在咱们讲MVVM的框架实现原理以前咱们要先讲一个方法Object.defineProperty(),这个方法是Vue这类MVVM原理实现很重要的一个方法,由于这是一个ES5的方法,因此不兼容IE8及一下,这也就是为何Vue不兼容IE8的缘由。git

从字面意思上来看咱们也知道,Object.defineProperty()是给对象定义属性的一个方法,在咱们平时赋属性常常会这样作:github

这样看似并无什么问题。数组

咱们能够对这个属性进行修改,删除,枚举。。。随意调戏bash

这样的定义方式看似很方便,数据能够随意咱们处理,可是实际上它有不少不便之处,举个🌰框架

若是像咱们刚才那样定义属性,length就是会被遍历出来,这样会给咱们的使用形成很大麻烦,这样的属性是如何被定义的?这就要用到Object.definePropertyide


语法函数

Object.defineProperty(obj,prop,descriptor)复制代码

参数

  • objspa

  • 要在其上定义属性的对象。3d

  • prop代理

  • 要定义或修改的属性的名称。

  • descriptor

  • 将被定义或修改的属性描述符。


1.description是数据属性

属性描述符总共有4个参数,咱们能够传入一个对象

  • configurable

  • 当且仅当该属性的 configurable 为 true 时,该属性描述符才可以被改变,同时该属性也能从对应的对象上被删除。默认为 false

  • enumerable

  • 当且仅当该属性的enumerabletrue时,该属性才可以出如今对象的枚举属性中。默认为 false

  • value

  • 该属性对应的值。能够是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined

  • writable

  • 当且仅当该属性的writabletrue时,value才能被赋值运算符改变。默认为 false


接下来写个小🌰


2.description是访问器属性

这个时候description也是有4个参数

  • configurable

    当且仅当该属性的 configurable 为 true 时,该属性描述符才可以被改变,同时该属性也能从对应的对象上被删除。默认为 false

  • enumerable

    当且仅当该属性的enumerabletrue时,该属性才可以出如今对象的枚举属性中。默认为 false

  • get

    一个给属性提供 getter 的方法,若是没有 getter 则为 undefined。该方法返回值被用做属性值。默认为 undefined

  • set

    一个给属性提供 setter 的方法,若是没有 setter 则为 undefined。该方法将接受惟一参数,并将该参数的新值分配给该属性。默认为 undefined


当description属性有get或set访问器属性的时候,不能有writable和value属性,由于get和set的做用能够替代者两个属性

接下来写个小🌰

模拟Array.length属性调用的时候计算对象长度


要开始了

先看一下Vue的样子,关于Vue的使用大概就是这样婶儿的:

实现的最终结果就是这样的:

接下来咱们将一一讲解这些功能的实现

咱们将写一个新的myMVVM.js的框架,来实现这些功能

固然如今仍是这样的

数据劫持Observe

所谓数据劫持就是将咱们给对象内的数据(data)定义的属性从新用Object.defineProperty定义一遍


咱们成功给my对象绑定上了属性和get/set访问器

固然,若是咱们的data数据不止一层,只调用一次的话,深层的数据就不会被数据劫持,因此咱们就须要递归调用,循环赋值,这也就是为何observer()和Observer()分开写的缘由

到这里尚未完

好比咱们为name赋值,会走get方法,可是若是咱们改变name的值为一个对象,name就会变为这个对象,同时get和set并不存在,因此咱们必须也要为新改变的数据进行数据劫持


数据代理

在咱们平时Vue的使用习惯中,调取数据基本不会用到Vue.$data.name,而是直接使用Vue.name,这样咱们就须要用Vue来代理Vue.$data,咱们要把Vue.$data的数据从新绑定在Vue上



在Vue的官方文档中:

当这些数据改变时,视图会进行重渲染。值得注意的是只有当实例被建立时 data 中存在的属性才是响应式的。也就是说若是你添加一个新的属性,好比:

vm.b = 'hi'
复制代码

那么对 b 的改动将不会触发任何视图的更新。若是你知道你会在晚些时候须要一个属性,可是一开始它为空或不存在,那么你仅须要设置一些初始值。好比:

data: {
  newTodoText: '',
  visitCount: 0,
  hideCompletedTodos: false,
  todos: [],
  error: null
}复制代码

Vue中不能新增数据,由于新增的数据没有get和set方法,也就没法实现数据劫持,不能监控数据的变化


模板编译

在咱们将全部的数据都绑定到实例化对象以后就要开始模板编译,将{{data}}中的数据都替换为真实的数据

因此咱们须要一个编译函数Complie

在咱们将全部数据劫持以后调用模板编译

页面上就实现了数据替换的效果:

发布订阅模式

这个时候咱们能够看到数据被正确编译,可是当咱们更改这个数据的时候,实例上的数据改变了,可是模板上的数据并无跟着更新,因此接下来咱们要实现数据的刷新

看一下发布订阅模式的原理

订阅就是将想要监控的函数push进数组,发布就是notify让数组内对象的方法执行

发布订阅模式在咱们更新视图时候的应用,当为数据赋值时,将调用notify方法,让被监控的对象执行,而被监控的应该就是Compile内的模板编译的方法,因此咱们要将它Watcher一下

同时咱们要把刚刚粗糙的Watcher构造函数改写一下

当数据更改时,咱们在set中调用notify

这样就能够实现改变数据时,视图随之更新


双向数据绑定

在Vue中,最大的一个特色就是双向数据绑定,更改对象数据后,视图会随之跟新,一样的若是视图上的数据改变,对象上的数据也会随之更新,而且视图上其它用到此数据的部分也会更新

接下来咱们将实现这样的双向数据绑定的功能,首先咱们要先将name的数据赋值到input的value中,这一样要用到咱们的模板编译的方法,以前编译{{}}的时候断定的文本节点,而v-model须要断定的是元素节点

一样对象数据改变的时候咱们要更改input中的值,因此咱们也要在这里订阅一下

接下来咱们要实现当数据框输入的时候,对象的数据也要随之更新,这里咱们要监听input输入事件

这样咱们就实现了双向数据绑定


计算属性


就写到这吧,写太多了,实在没有力气了,随着。。。世界索然无味(下附源码)


这里附上源码,仍是但愿你们能本身敲一遍代码,清晰了解整个框架从无到有所经历的每个阶段,同时但愿这篇文章对你们能有所帮助

https://github.com/q869939686/myProject.git复制代码
相关文章
相关标签/搜索