在咱们讲MVVM的框架实现原理以前咱们要先讲一个方法Object.defineProperty(),这个方法是Vue这类MVVM原理实现很重要的一个方法,由于这是一个ES5的方法,因此不兼容IE8及一下,这也就是为何Vue不兼容IE8的缘由。git
从字面意思上来看咱们也知道,Object.defineProperty()是给对象定义属性的一个方法,在咱们平时赋属性常常会这样作:github
这样看似并无什么问题。数组
咱们能够对这个属性进行修改,删除,枚举。。。随意调戏bash
这样的定义方式看似很方便,数据能够随意咱们处理,可是实际上它有不少不便之处,举个🌰框架
若是像咱们刚才那样定义属性,length就是会被遍历出来,这样会给咱们的使用形成很大麻烦,这样的属性是如何被定义的?这就要用到Object.definePropertyide
语法函数
Object.defineProperty(obj,prop,descriptor)复制代码
obj
spa
要在其上定义属性的对象。3d
prop
代理
要定义或修改的属性的名称。
descriptor
将被定义或修改的属性描述符。
属性描述符总共有4个参数,咱们能够传入一个对象
configurable
当且仅当该属性的 configurable 为 true 时,该属性描述符
才可以被改变,同时该属性也能从对应的对象上被删除。默认为 false。
enumerable
当且仅当该属性的enumerable
为true
时,该属性才可以出如今对象的枚举属性中。默认为 false。
value
该属性对应的值。能够是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined
。
writable
当且仅当该属性的writable
为true
时,value
才能被赋值运算符改变。默认为 false。
接下来写个小🌰
这个时候description也是有4个参数
configurable
当且仅当该属性的 configurable 为 true 时,该属性描述符
才可以被改变,同时该属性也能从对应的对象上被删除。默认为 false。
enumerable
当且仅当该属性的enumerable
为true
时,该属性才可以出如今对象的枚举属性中。默认为 false。
get
一个给属性提供 getter 的方法,若是没有 getter 则为 undefined
。该方法返回值被用做属性值。默认为 undefined
。
set
一个给属性提供 setter 的方法,若是没有 setter 则为 undefined
。该方法将接受惟一参数,并将该参数的新值分配给该属性。默认为 undefined
。
接下来写个小🌰
模拟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复制代码