文档章节

30行代码构建javascript 的MVC模式

369yun
 369yun
发布于 2016/04/15 13:28
字数 1460
阅读 11
收藏 0

MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller);

很多讲解MVC的例子都从一个具体的框架的某个概念入手,比如Backbone的collection或AngularJS中model,这当然不失为一个好办法。但框架之所以是框架,而不是类库(jQuery)或者工具集(Underscore),就是因为它们的背后有着众多优秀的设计理念和最佳实践,这些设计精髓相辅相成,环环相扣,缺一不可,要想在短时间内透过复杂的框架而看到某一种设计模式的本质并非是一件容易的事。

这便是这篇随笔的由来——为了帮助大家理解概念而生的原型代码,应该越简单越好,简单到刚刚足以大家理解这个概念就够了。

1.MVC的基础是观察者模式,这是实现modelview同步的关键

为了简单起见,每个model实例中只包含一个primitive value值。

 function Model(value) {
    this._value = typeof value === 'undefined' ? '' : value;
    this._listeners = [];
}
Model.prototype.set = function (value) {
    var self = this;
    self._value = value;
    // model中的值改变时,应通知注册过的回调函数
    // 按照Javascript事件处理的一般机制,我们异步地调用回调函数
    // 如果觉得setTimeout影响性能,也可以采用requestAnimationFrame
    setTimeout(function () {
        self._listeners.forEach(function (listener) {
            listener.call(self, value);
        });
    });
};
Model.prototype.watch = function (listener) {
    // 注册监听的回调函数
    this._listeners.push(listener);
};
  
// html代码:
<div id="div1"></div>
// 逻辑代码:
(function () {
    var model = new Model();
    var div1 = document.getElementById('div1');
    model.watch(function (value) {
        div1.innerHTML = value;
    });
    model.set('hello, this is a div');
})();

 

借助观察者模式,我们已经实现了在调用model的set方法改变其值的时候,模板也同步更新,但这样的实现却很别扭,因为我们需要手动监听model值的改变(通过watch方法)并传入一个回调函数,有没有办法让view(一个或多个dom node)和model更简单的绑定呢?
2.  实现 bind 方法,绑定 model view
 Model.prototype.bind = function (node) {
    // 将watch的逻辑和通用的回调函数放到这里
    this.watch(function (value) {
        node.innerHTML = value;
    });
};
  
// html代码:
<div id="div1"></div>
<div id="div2"></div>
// 逻辑代码:
(function () {
    var model = new Model();
    model.bind(document.getElementById('div1'));
    model.bind(document.getElementById('div2'));
    model.set('this is a div');
})();
通过一个简单的封装,view和model之间的绑定已经初见雏形,即使需要在一个model上绑定多个view,实现起来也很轻松。注意bind是Function类prototype上的一个原生方法,不过它和MVC的关系并不紧密,笔者又实在太喜欢bind这个单词,一语中的,言简意赅,所以索性在这里把原生方法覆盖了,大家可以忽略。言归正传,虽然绑定的复杂度降低了,这一步依然要依赖我们手动完成,有没有可能把绑定的逻辑从业务代码中彻底解耦呢?
3.  实现 controller ,将绑定从逻辑代码中解耦
细心的朋友可能已经注意到,虽然讲的是MVC,但是上文中却只出现了Model类,View类不出现可以理解,毕竟HTML就是现成的View(事实上本文中从始至终也只是利用HTML作为View,javascript代码中并没有出现过View类),那Controller类为何也隐身了呢?别急,其实所谓的”逻辑代码”就是一个框架逻辑(姑且将本文的原型玩具称之为框架)和业务逻辑耦合度很高的代码段,现在我们就来将它分解一下。
如果要将绑定的逻辑交给框架完成,那么就需要告诉框架如何来完成绑定。由于JS中较难完成annotation(注解),我们可以在view中做这层标记——使用html的标签属性就是一个简单有效的办法。
 function Controller(callback) {
    var models = {};
    // 找到所有有bind属性的元素
    var views = document.querySelectorAll('[bind]');
    // 将views处理为普通数组
    views = Array.prototype.slice.call(views, 0);
    views.forEach(function (view) {
        var modelName = view.getAttribute('bind');
        // 取出或新建该元素所绑定的model
        models[modelName] = models[modelName] || new Model();
        // 完成该元素和指定model的绑定
        models[modelName].bind(view);
    });
    // 调用controller的具体逻辑,将models传入,方便业务处理
    callback.call(this, models);
}
  
// html:
<div id="div1" bind="model1"></div>
<div id="div2" bind="model1"></div>
// 逻辑代码:
new Controller(function (models) {
    var model1 = models.model1;
    model1.set('this is a div');
});
就这么简单吗?就这么简单:在Controller中完成业务逻辑并对Model进行修改,Model的变化触发View的自动更新,怎么样,算得上一个有模有样的MVC吧?当然,这样的”框架”还不足以用于生产环境,不过如果它能或多或少地帮助到大家对于MVC的理解的话,博主就非常满足了。
整理后去掉注释的”框架”代码:
 function Model(value) {
    this._value = typeof value === 'undefined' ? '' : value;
    this._listeners = [];
}
Model.prototype.set = function (value) {
    var self = this;
    self._value = value;
    setTimeout(function () {
        self._listeners.forEach(function (listener) {
            listener.call(self, value);
        });
    });
};
Model.prototype.watch = function (listener) {
    this._listeners.push(listener);
};
Model.prototype.bind = function (node) {
    this.watch(function (value) {
        node.innerHTML = value;
    });
};
function Controller(callback) {
    var models = {};
    var views = Array.prototype.slice.call(document.querySelectorAll('[bind]'), 0);
    views.forEach(function (view) {
        var modelName = view.getAttribute('bind');
        (models[modelName] = models[modelName] || new Model()).bind(view);
    });
    callback.call(this, models);[mw_shl_code=applescript,true]<span bind="hour"></span> : <span bind="minute"></span> : <span bind="second"></span>
// controller:
new Controller(function (models) {
    function setTime() {
        var date = new Date();
        models.hour.set(date.getHours());
        models.minute.set(date.getMinutes());
        models.second.set(date.getSeconds());
    }
    setTime();
    setInterval(setTime, 1000);
});
4.  一个简单的例子
下面请大家看一个简单例子,如何实现电子表
// html:

可以看出,controller中只负责更新model的逻辑,和view完全解耦;而view和model的绑定是通过view中的属性和框架中controller的初始化代码完成的,也没有出现在业务逻辑中;至于view的更新,也是通过框架中的观察者模式实现的。

原文链接:http://bbs.369cloud.com/thread-1767-1-1.html

© 著作权归作者所有

369yun
粉丝 2
博文 33
码字总数 7317
作品 0
海淀
产品经理
私信 提问
运用 Ext JS 4 的 MVC 架构

Ext JS 4 简介 Ext JS 4 目前是 Sencha 的产品,4.x 的正式版本号是 4.0.7。Ext JS 4 提供商业版本,但如果您的项目是开源的,则可以免费使用 Ext JS 4。Ext JS 的论坛目前非常活跃;Ext JS ...

IBMdW
2012/10/16
866
3
JavaScript MVC 开源框架

腾讯Web前端框架库 JX by Tencent AlloyTeam 简介 JX 是模块化的非侵入式Web前端框架,开发于2008年,并于2009年开源于GoogleCode - http://code.google.com/p/j-et/,于2012年切换到Github,...

wewelove
2014/06/04
345
0
Riot.js — 1Kb 大小的 JavaScript 的 MVP 框架

一个可以构建大型网络应用并令人难以置信快和强大但轻量级的客户端框架 Riot.js是一个客户端模型-视图-呈现(MVP)框架并且它非常轻量级甚至小于1kb.尽管他的大小令人难以置信,所有它能构建的...

oschina
2013/11/02
19.2K
52
JS文件由PHP动态生成,多少访问能把系统弄崩溃?

小弟公司最近在做个项目。 核心模块是由XML文件经由PHP模块生成javascript对象,在解析生成html标签。及该流程的反向过程。 阅读别人原代码的时候注意到以下js由PHP动态生成。 以上几个均为编...

代码成湿
2014/05/07
803
10
AngularJS - 下一个大框架

AngularJS AngularJS是web应用的下一个巨头。 AngularJS如果为创建web应用而设计,那它就是HTML的套路了。具有数据绑定, MVW, MVVM, MVC, 依赖注入的声明式模板和出色的可测试性都是用纯客户...

oschina
2014/07/29
16.8K
46

没有更多内容

加载失败,请刷新页面

加载更多

关于ThinkPHP5.1+的Log无法记录SQL调试记录的小经历

项目开发阶段,除了基本编码外,性能也需要实时关注与优化。之前我的大部分项目都是使用ThinkPHP5.0以及ThinkPHP3.2,对于框架提供的日志记录和日志配置都差不多,然后使用ThinkPHP5.1的时候...

北桥苏
11分钟前
0
0
TiDB Binlog 源码阅读系列文章(四)Pump server 介绍

作者: satoru 在 上篇文章 中,我们介绍了 TiDB 如何通过 Pump client 将 binlog 发往 Pump,本文将继续介绍 Pump server 的实现,对应的源码主要集中在 TiDB Binlog 仓库的 pump/server.go...

TiDB
15分钟前
0
0
OSChina 周五乱弹 ——不知道假装开心,装的像么

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @巴拉迪维 :天黑了 你很忧愁, 你说世界上, 找不到四块五的妞, 行走在凌晨两点的马路上, 你疲倦地拿着半盒黄鹤楼。#今日歌曲推荐# 《四块...

小小编辑
今天
2.4K
18
Windows下学习C语言有哪些集成开发软件?

前言 初学者学习C语言遇到的最大困难想必就是搭建环境了,相当多的初学者就是被搭建环境导致放弃了学习编程,就我自己的经验而言,初学编程不应该受限于环境,使用成熟好用的环境就可以了,之...

Allen5G
昨天
2
0
Hello,Servlet!

Servlet来源 上文说过了servlet是什么,我们从servlet是什么中也可以了解到servlet的来源:servlet是Java的一个类,并且能够运行在web容器上,所以servlet是按照web容器的规范和Java的规范写...

蒙尘
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部