简单的实现Javascript的MVC
时间:2022-04-21
本文章向大家介绍简单的实现Javascript的MVC,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
最近看了一篇文章,“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与viewModel.prototype.bind = function (node) { // 将watch的逻辑和通用的回调函数放到这里
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>
- [C#3] 1-扩展方法
- 无插件仅代码实现 WordPress 分页导航(2)
- [C#3] 4-匿名类型
- jquery操作DOM 元素(3)
- 腾讯汤道生对话牛津大学互联网研究院Luciano:大数据将如何改变世界
- 分享本站右侧 “类Metro风格侧边栏” 的实现方法
- [C#1] 1-Hello World
- 代码审查工具StyleCop
- 实用工具特别推荐 Robocopy GUI
- 七牛镜像存储 WordPress 插件 的代码化
- WordPress 会自动压缩JPEG 格式的图像?
- jquery操作DOM 元素(2)
- WordPress 中部署真正的懒加载(Lazy Load)
- 图形化的2008R2 Server Core 配置管理工具
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- linux centos 安装mailx邮件服务器并测试发送一封邮件
- 深入分析Vue-Router原理,彻底看穿前端路由
- linux LVM 一键分区脚本自动扩容
- 再谈构造函数、原型、原型链之间的关系
- Java ConcurrentHashMap 高并发安全实现原理解析
- 第十二节:Activiti6.0——四种边界事件:定时器、错误、信号、补偿
- parted 磁盘分区-挂载-删除-shell脚本进行磁盘分区
- Ubuntu18.04——安装MySQL
- 八种 Vue 组件间通讯方式合集
- Sharding-JDBC 实现分库分表
- fastjson——使用 aop 打印入参,报错:getOutputStream() has already been called for this response
- webpack从零搭建开发环境
- 博客——使用 Redis 实现博客编辑的自动保存草稿功能
- linux下安装zabbix服务器shell脚本-添加主机-邮件监控报警zabbix-自动化运维
- Nginx——开启 GZIP 压缩