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


先讲个方法

在我们讲MVVM的框架实现原理之前我们要先讲一个方法Object.defineProperty(),这个方法是Vue这类MVVM原理实现很重要的一个方法,因为这是一个ES5的方法,所以不兼容IE8及一下,这也就是为什么Vue不兼容IE8的原因。

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

这样看似并没有什么问题。

我们可以对这个属性进行修改,删除,枚举。。。随意调戏

这样的定义方式看似很方便,数据可以随意我们处理,但是实际上它有很多不便之处,举个?

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


语法

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

参数

  • obj

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

  • 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复制代码


转载于:https://juejin.im/post/5acdc82af265da238c3b07ac

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值