资讯专栏INFORMATION COLUMN

【教学向】150行代码教你实现一个低配版的MVVM库(1)- 原理篇

selfimpr / 562人阅读

摘要:模块则负责维护,以及各个模块间的调度思考题了解了的实现机制,你能否自己动手也试着用百来行代码实现一个库呢好了本教程第一部分设计篇就写到这里,具体请移步下一篇教学向行代码教你实现一个低配版的库代码篇我会用给出一版实现。

适读人群

本文适合对MVVM有一定了解(如有主流框架ng,vue等使用经验配合本文服用则效果更佳),虽然会用这类框架,但是对框架底层核心实现又不太清楚,或者能说出个所以然,但是让他自己动手写又没有头绪的码友。如果还没听说过MVVM,不妨先收藏着。。。

名词定义

先给低配版的库起一个响亮的名字,以便于开展教学,入乡随俗我们就叫ta -- SegmentFault.js 吧 (以下简称sf.js

设置在DOM Element上的自定义属性前缀统一以 sf- 开头 (如

为什么是低配版?
1. 没有sf-repeat
2. 不支持select,checkbox,radio等控件的双向绑定
3. 没有sf-if
4. 很多都没有

由于是教学向,力图用最简短易读的代码来实现MVVM最主要最基本的功能,故砍掉了部分实现。

先看演示图,图中就是使用sf.js写得DEMO

100多行的低配版不能要求太多,如果看不上低配版的库,请关闭本教程。

老生常谈 什么是双向绑定

首先明白一个概念,什么是双向绑定?在说双向绑定之前,我们先说说单向显示
单向显示 说白了就是view动态地显示变量。比如在ng或其它一些主流框架里类似这种写法

    //scope.message= "segmentfault"; 
    

segmentfault

为什么说是单向呢,因为都是 viewModel上某个变量(message) -> view (h3)的一个过程,viewModel上的变量被view所呈现。

再来看看 逆向修改
前面说了单向是viewMode->view的过程,那逆向就是 viewModel <- view的过程,换句话说就是viewModel被view修改的过程。例如angular中

一旦用户在input控件中输入值,便会实时地改变viewModel中message这个变量的值。这是一个view -> viewModel 的过程。

所谓的双向绑定就是一个 viewMode ->(显示) view ->(修改)viewModel 的过程。

双向绑定 = 单向显示 + 逆向修改
注意: 单向显示可能发生在所有的类型DOM节点上,而逆向修改只可能发生在INPUT,SELECT,TEXTAREA等交互型控件上。

如果整明白什么是双向绑定了,我们就来谈谈设计思路,没有整明白的同学请再阅读一遍.

单向显示的设计思路(viewModel -> view)

先看看API的设计


要实现这个功能,我们的sf库应该需要哪几步操作呢?(先自己想想,独立思考下)

1. 注册ViewModel,我们的库需要知道哪些object是viewModel
2. 扫描整个DOM Tree找到有哪些DOM节点上被配置了sf-xxxx这个attribute
3. 纪录这些被单向绑定的DOM节点和viewModel之间的映射关系
4. 使用DOM API, element.innerText = vm.prop, element.value = vm.prop, element.xxxx = vm.prop 来显示数据
思考题1

Q:如果我们要单向绑定不是innerText,value 而是作为样式的class,style呢?
A:没错,使用sf-class="vm.myClass" sf-style="vm.myStyle"就好了,其它原生属性也以此类推
"sf-" + native attribute is good!

逆向修改的设计思路(viewModel <- view)

主流的一些mvvm框架上一般这么设计API,还是拿angular举例子

这里我个人并不认同这种xx-model的命名方式来作为双向绑定的一种标识,比如angular的ng-model,或Vue的v-model,撇开前缀不说,这个model很让人困惑,我们知道input控件是本身就有value这个native的属性的,这个属性就是代表着input输入和输出的值,如果要给一个input进行双向绑定我们应该很自然而然地使用一个 *-value就可以了,完全没有必要弄出一个新的attribute叫做*-model的,从而增加学习成本。

所以,我们就设计一个叫做sf-value的attribute来做API

拍脑袋想想,view要改变数据只可能发生在可以和用户交互的一些html控件上,比如input家族(text, radio, checkbox), select, textarea上。 像h1~hn家族,这辈子是没有机会的。

要实现view改写viewModel,我们的库应该需要哪几步操作呢?(也先自己想想,千万不要丢掉独立思考能力)

1.扫描整个DOM Tree,找到哪些INPUT,SELECT,TEXTAREA节点上被配置了sf-value这个attribute
2.纪录这些被双向绑定的DOM节点和viewModel之间的映射关系
3.sf.js库自动给这个写DOM加上onchange或者oninput的事件监听
4.一旦监听到change/input事件,立即获取这个DOM的value值,把这个element.value赋给与之绑定的viewModel的变量上。
思考题2

Q:那么问题来了,vm.message被input修改了,谁去通知其它同样绑定了vm.message的view呢?
A:请看下一段

同步机制

脏检查大法 这三个字想必大家已经如雷贯耳,我2年多前出去面试的时候被问及最多的就是angular的脏检查,什么是脏检查?angular脏检查的时机是什么?

脏检查的原理就是,拷贝一份copy_viewModel在内存中,一旦有用户点击,输入操作,或ajax请求,setInterval,setTimeout等这些可能导致viewModel发生改变的行为,框架都会把copy_viewModel和最新的viewModel进行深度比较,一旦发现有属性(如vm.message)发生变化,则重新渲染与message绑定的DOM节点。

这也是为什么,一旦你没有使用ng自带的$http,$interval,$timeout,ng-click这些angular自己封装的API去操作viewModel,angular都不会自动去同步view,因为已经超出他的管辖范围了,你必须手动调用apply函数去强制执行一次脏检查,以同步view。

setter大法
听说VUE是使用的这种同步机制,其核心原理就是使用Object.defineProperty(obj, prop, descriptor)(不了解defineProperty的请戳)这个API,在setter中加点料,一旦有任何地方执行 vm.message = "new value"语句,则setter都会被调用,由setter去触发重新渲染view的逻辑。

相较这两种同步机制,似乎setter更加轻便,性能更好。所以本文使用了setter的方式来实现同步机制(关键是实现setter机制使用的代码较少)。

设计思路

给setter加点料

http://jsbin.com/gosigoh/edit...

总体设计图

所以归纳来说一个MVVM库主要由3块组成
MVVM库 = 单向显示 + 逆向修改 + 同步机制
下图为SegmentFault.js的实现机制
其中Renderer负责单向显示和逆向修改,Watcher负责监视viewModel为同步机制的核心模块,
Scanner负责sf.js初始化时扫描DOM Tree生成view和viewModel的映射关系。
SegmentFault模块则负责维护view-viewModel Map,以及各个模块间的调度

思考题3

Q:了解了MVVM的实现机制,你能否自己动手也试着用百来行代码实现一个MVVM库呢?

好了!本教程第一部分设计篇就写到这里,具体coding请移步(下一篇 【教学向】150行代码教你实现一个低配版的MVVM库(2)- 代码篇)
我会用Typescript给出一版实现。

写在最后 这篇文章的目的

2年前写了我受够了angular的笨重,学习曲线陡峭等缺点,自己一怒之下写下一个轻量的MVVM库,给她起名叫【Ukulele.js】(跟我一起念『尤克里里.杰爱死』,当然本文不是这个库的安利文,请安心服用),一开始写这个库出于好玩,后来也加入了越来越多的功能,诸如web component的支持,我渐渐发现,其实要写一个MVVM库也并不是很难,难的是你有没有决心敲下第一行代码。后来我把她和【精通angularjs】一起写在里简历里,然后就去找工作了。面试的时候被问及最多的问题就是:"说说MVVM的实现机制"。

我今天写下此文,1是希望有机会看到这篇文章的码友能真正掌握MVVM的核心机制,2是鼓励下大家能静下心来,自己动手写写库,写写框架,有些你现在觉得蛮高大上的东西,你仔细一分析,动动脑,真的没有那么高大上,普通的码农也能自己实现

相关阅读

【教学向】150行代码教你实现一个低配版的MVVM库(1)- 原理篇
【教学向】150行代码教你实现一个低配版的MVVM库(2)- 代码篇
【教学向】再加150行代码教你实现一个低配版的web component库(1) —设计篇
【教学向】再加150行代码教你实现一个低配版的web component库(2) —原理篇

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87348.html

相关文章

  • 教学】再加150代码教你实现一个低配版的web component1) —设计

    摘要:为的内置一个方法,用法和原生的事件机制一毛一样。 前言 上两篇Mvvm教程的热度超出我的预期,很多码友留言表扬同时希望我继续出下一篇教程,当时我也半开玩笑说只要点赞超10就兑现承诺,没想到还真破了10,所以就有了今天的文章。 准备工作 熟读 【教学向】150行代码教你实现一个低配版的MVVM库(1)- 原理篇【教学向】150行代码教你实现一个低配版的MVVM库(2)- 代码篇 本篇是在...

    Clect 评论0 收藏0
  • 教学150代码教你实现一个低配版的MVVM(2)- 代码

    摘要:也放出地址,上面有完整工程以及在线演示地址相关阅读教学向行代码教你实现一个低配版的库原理篇教学向行代码教你实现一个低配版的库代码篇教学向再加行代码教你实现一个低配版的库设计篇教学向再加行代码教你实现一个低配版的库原理篇 书接上一篇: 150行代码教你实现一个低配版的MVVM库(1)- 原理篇 写在前面 为了便于分模块,和阅读,我使用了Typescript来进行coding,总行数是正好...

    loonggg 评论0 收藏0
  • 2017-08-29 前端日报

    摘要:前端日报精选浏览器兼容性问题解决方案配置指南全新的模块化框架,知乎专栏现学现卖中文教学向再加行代码教你实现一个低配版的库原理篇我把最美的青春都献给了代码技术周刊开启浏览器全屏模式如何进行的操作掘金内存分配与垃圾回收写一 2017-08-29 前端日报 精选 浏览器兼容性问题解决方案AlloyTeam ESLint 配置指南全新的redux模块化框架,redux-arena - 知乎专栏...

    atinosun 评论0 收藏0
  • 个人分享--web前端学习资源分享

    摘要:前言月份开始出没社区,现在差不多月了,按照工作的说法,就是差不多过了三个月的试用期,准备转正了一般来说,差不多到了转正的时候,会进行总结或者分享会议那么今天我就把看过的一些学习资源主要是博客,博文推荐分享给大家。 1.前言 6月份开始出没社区,现在差不多9月了,按照工作的说法,就是差不多过了三个月的试用期,准备转正了!一般来说,差不多到了转正的时候,会进行总结或者分享会议!那么今天我就...

    sherlock221 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<