简单的实现Javascript的MVC

文章来源于网络,如有侵权,请在评论里留言,我将立即删除!!!

http://imweb.io/topic/56eebf97d62bf338217b8c69

本文作者:imweb 谢华良 原文出处:imweb社区 未经同意,禁止转载

最近看了一篇文章,“30行代码实现Javascript中的MVC”,原文链接:http://www.jqsite.com/notes/1603205925.html ,受益良多,在此记录下学习的心得。 提到MVC,基本都会从一些框架开始,比如angularJs之类的,要在短时间内透过复杂的框架看到某一种设计模式并非是一件容易的事情。那么如何通过最简单的代码实现一个简单的MVC呢?

  1. MVC的基础是观察者模式,这是实现Model与View同步的关键。
    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 = value;
     });
     model.set('hello, this is a div');
    })();
    
  2. 实现bind方法,绑定model与view
    Model.prototype.bind = function (node) {
     // 将watch的逻辑和通用的回调函数放到这里
     this.watch(function (value) {
         node = value;
     });
    };
    

    // html代码:

    // 逻辑代码:

    (function () {
     var model = new Model();
     model.bind(document.getElementById('div1'));
     model.bind(document.getElementById('div2'));
     model.set('this is a div'); 
    })();
    
  3. 实现controller,将绑定从逻辑代码中解耦
    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:
    // 逻辑代码:
    new Controller(function (models) {
     var model1 = models.model1;
     model1.set('this is a div');
    });
    
    以下是根据我自己的理解,封装的代码,简单的实现了双向绑定和模仿了angularjs部分形式:
    var app = (function(){
     var Model = function(value){
         this._v = value;
         this._listeners = [];
     }
     Model.prototype.set = function(value){
         var self = this;
         self._v = value;
         setTimeout(function(){
             self._listeners.forEach(function(listener){
                 listener.call(this,value);
             })
         })
     }
     Model.prototype.watch = function(func){
         this._listeners.push(func);
     }
     Model.prototype.bind = function(node){
         var self = this;
         this.watch(function(value){
             if(node.tagName.toUpperCase()=="INPUT"  && !self.inputEvent){
                 node.addEventListener("keyup",function(){
                     var _v = this.value;
                     if(_v != value){
                         self.set(_v);
                     }
                     self.inputEvent = 1;
                 })
                 node.value = value;
             }else{
                 node = value;
             }
         })
     }
     function controller(controllername,callback){
         var models = {},
             search = typeof controllername=="string" ? "[controller=" + controllername + "]" : "[controller]",
             controller = document.querySelector(search),init = eval("("+controller.getAttribute("init")+")"),$scope = {};
         if(!controller) return;
         var views = Array.prototype.slice.call(controller.querySelectorAll("[bind]"),0);
         views.forEach(function(view){
             var modelname = view.getAttribute("bind");
             (models[modelname] = models[modelname] || new Model()).bind(view);
             $scope = createVisitors($scope,models[modelname],modelname);
         });
         for(var index in init){
             $scope[index] = init[index];
         }
         callback.call(this,$scope);
     }
     function createVisitors($scope,model,property){
         $scope.__defineSetter__(property,function(value){
             model.set(value);
         })
         return $scope;
     }
     return {
         controller : controller
     }
    })();
    

    一个使用的例子:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>MVC例子</title>
    <meta name="description" content="">
    <meta name="keywords" content="">
    <link href="" rel="stylesheet">
    </head>
    <body>
     <div>时钟</div>
     <div controller="clock">
         <span bind="hour"></span> : 
         <span bind="minutes"></span> :
         <span bind="seconds"></span> 
     </div>
     <br/> 
     <div>双向绑定</div>
     <div controller="myCtrl" init="{numb:2,num:'nihao'}">
         Input : <input type="text" bind="numb">
         <br/>Span : <span bind="numb"></span>
     </div>
     <script type="text/javascript">
      var app = (function(){
         var Model = function(value){
             this._v = value;
             this._listeners = [];
         }
         Model.prototype.set = function(value){
             var self = this;
             self._v = value;
             setTimeout(function(){
                 self._listeners.forEach(function(listener){
                     listener.call(this,value);
                 })
             })
         }
         Model.prototype.watch = function(func){
             this._listeners.push(func);
         }
         Model.prototype.bind = function(node){
             var self = this;
             this.watch(function(value){
                 if(node.tagName.toUpperCase()=="INPUT"  && !self.inputEvent){
                     node.addEventListener("keyup",function(){
                         var _v = this.value;
                         if(_v != value){
                             self.set(_v);
                         }
                         self.inputEvent = 1;
                     })
                     node.value = value;
                 }else{
                     node = value;
                 }
             })
         }
         function controller(controllername,callback){
             var models = {},
                 search = typeof controllername=="string" ? "[controller=" + controllername + "]" : "[controller]",
                 controller = document.querySelector(search),init = eval("("+controller.getAttribute("init")+")"),$scope = {};
             if(!controller) return;
             var views = Array.prototype.slice.call(controller.querySelectorAll("[bind]"),0);
             views.forEach(function(view){
                 var modelname = view.getAttribute("bind");
                 (models[modelname] = models[modelname] || new Model()).bind(view);
                 $scope = createVisitors($scope,models[modelname],modelname);
             });
             for(var index in init){
                 $scope[index] = init[index];
             }
             callback.call(this,$scope);
         }
         function createVisitors($scope,model,property){
             $scope.__defineSetter__(property,function(value){
                 model.set(value);
             })
             return $scope;
         }
         return {
             controller : controller
         }
      })();
      app.controller("myCtrl",function($scope){
         //code ...
      });
      app.controller("clock",function($scope){
         function setTime(){
             var date = new Date();
             $scope.hour = date.getHours();
             $scope.minutes = date.getMinutes();
             $scope.seconds = date.getSeconds();
             setTimeout(setTime,1000);
         }
         setTime();
      })
     </script>
    </body>
    </html>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值