一个简单的MVVM雏形
这是@尚春实现的MVVM,使用定时器轮询,只支持{{}}与input.value的修改。
这只能算是一个玩具,真正的MVVM需要有更复杂的扫描机制,JS解析器,双向绑定链什么的。
<!DOCTYPE html>
<html>
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title> </head>
<body>
<div data-component="input">
<template>
<input type="text" name="username" tb-model="username" value="" />
<span>{{ username }}</span>
</template>
</div>
<script>
var DIRECTIVE_ATTR_MODEL = 'tb-model',
regMustache = /\{\{\s*(\w+)\s*\}\}/g,
slice = Array.prototype.slice; function boot() {
var components = slice.call(document.querySelectorAll('[data-component]'), 0);
components.forEach(function (el) {
var component = el.getAttribute('data-component');
bootComponent(el, window[component + 'Controller']);
});
} function bootComponent(el, controller) {
var $scope = {},
elFrag = el.querySelector('template').content.cloneNode(true);
traverse(elFrag, $scope);
el.appendChild(elFrag);
controller($scope);
} function traverse(root, $scope) {
for (var el = root.firstChild; el; el = el.nextSibling) {
parseElement(el, $scope);
if (el) {
traverse(el, $scope);
}
}
} function parseElement(el, $scope) {
if (el.nodeType === 1) {
// element
if (el.hasAttribute(DIRECTIVE_ATTR_MODEL)) {
var model = el.getAttribute(DIRECTIVE_ATTR_MODEL);
el.removeAttribute(DIRECTIVE_ATTR_MODEL);
el.addEventListener('input', function () {
$scope[model] = this.value;
});
}
} else if (el.nodeType === 3) {
// text node
var text = el.textContent,
tpl = [],
lastIndex = 0,
match = regMustache.exec(text);
while (match) {
tpl.push(text.substring(lastIndex, regMustache.lastIndex - match[0].length));
tpl.push({
type: 'var',
content: match[1]
});
lastIndex = regMustache.lastIndex;
match = regMustache.exec(text);
}
watch($scope, function () {
text = '';
tpl.forEach(function (item) {
text += typeof item === 'string' ? item : $scope[item.content];
});
el.textContent = text;
});
}
} function watch($scope, cb) {
var old = _.cloneDeep($scope),
timer;
timer = setInterval(function () {
if (!_.isEqual($scope, old)) {
cb($scope, old);
old = _.cloneDeep($scope);
}
}, 50);
} function inputController($scope) {
$scope.username = 'spring';
} boot();
</script>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<script src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div data-component="input">
<template>
<input type="text" name="username" tb-model="username" value="" />
<span>{{ username }}</span>
</template>
</div>
<script>
var DIRECTIVE_ATTR_MODEL = 'tb-model',
regMustache = /\{\{\s*(\w+)\s*\}\}/g,
slice = Array.prototype.slice;
function boot() {
var components = slice.call(document.querySelectorAll('[data-component]'), 0);
components.forEach(function (el) {
var component = el.getAttribute('data-component');
bootComponent(el, window[component + 'Controller']);
});
}
function bootComponent(el, controller) {
var $scope = {},
elFrag = el.querySelector('template').content.cloneNode(true);
traverse(elFrag, $scope);
el.appendChild(elFrag);
controller($scope);
}
function traverse(root, $scope) {
for (var el = root.firstChild; el; el = el.nextSibling) {
parseElement(el, $scope);
if (el) {
traverse(el, $scope);
}
}
}
function parseElement(el, $scope) {
if (el.nodeType === 1) {
// element
if (el.hasAttribute(DIRECTIVE_ATTR_MODEL)) {
var model = el.getAttribute(DIRECTIVE_ATTR_MODEL);
el.removeAttribute(DIRECTIVE_ATTR_MODEL);
el.addEventListener('input', function () {
$scope[model] = this.value;
});
}
} else if (el.nodeType === 3) {
// text node
var text = el.textContent,
tpl = [],
lastIndex = 0,
match = regMustache.exec(text);
while (match) {
tpl.push(text.substring(lastIndex, regMustache.lastIndex - match[0].length));
tpl.push({
type: 'var',
content: match[1]
});
lastIndex = regMustache.lastIndex;
match = regMustache.exec(text);
}
watch($scope, function () {
text = '';
tpl.forEach(function (item) {
text += typeof item === 'string' ? item : $scope[item.content];
});
el.textContent = text;
});
}
}
function watch($scope, cb) {
var old = _.cloneDeep($scope),
timer;
timer = setInterval(function () {
if (!_.isEqual($scope, old)) {
cb($scope, old);
old = _.cloneDeep($scope);
}
}, 50);
}
function inputController($scope) {
$scope.username = 'spring';
}
boot();
</script>
</body>
</html>
运行代码
一个简单的MVVM雏形的更多相关文章
- 230行实现一个简单的MVVM
作者:mirone链接:https://zhuanlan.zhihu.com/p/24451202来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. MVVM这两年在前端届 ...
- 如何实现一个简单的MVVM框架
接触过web开发的同学想必都接触过MVVM,业界著名的MVVM框架就有AngelaJS.今天闲来无事,决定自己实现一个简单的MVVM框架玩一玩.所谓简单,就是仅仅实现一个骨架,仅表其意,不摹其形. 分 ...
- 一个简单的 MVVM 实现
简介 一个简单的带有双向绑定的 MVVM 实现. 例子 使用 新建一个 ViewModel 对象, 参数分别为 DOM 元素以及绑定的数据即可. 指令 本 MVVM 的指令使用 data 数据, 即 ...
- 撸一个简单的MVVM例子
我个人以为mvvm框架里面最重要的一点就是VM这部分,它要与Model层建立联系,将Model层转换成可以被View层识别的数据结构:其次也要同View建立联系,将数据及时更新到View层上,并且响应 ...
- JavaScript 实现一个简单的MVVM前端框架(ES6语法)
前言 随着前端各大框架的崛起,为我们平时的开发带来了相当的便利,我们不能一直停留在应用层面,今天就自己动手实现一个乞丐版的MVVM小框架 完整代码github地址 效果 html代码 <div ...
- 基于vue实现一个简单的MVVM框架(源码分析)
不知不觉接触前端的时间已经过去半年了,越来越发觉对知识的学习不应该只停留在会用的层面,这在我学jQuery的一段时间后便有这样的体会. 虽然jQuery只是一个JS的代码库,只要会一些JS的基本操作学 ...
- 用js实现一个简单的mvvm
这里利用的object.defineproperty() 方法; <input id='input'><p id='p'><p/>js: const dat ...
- MVVM之旅(1)创建一个最简单的MVVM程序
这是MVVM之旅系列文章的第一篇,许多文章和书喜欢在开篇介绍某种技术的诞生背景和意义,但是我觉得对于程序员来说,一个能直接运行起来的程序或许能够更直观的让他们了解这种技术.在这篇文章里,我将带领大家一 ...
- 【UWP开发】一个简单的Toast实现
Toast简介 在安卓里Toast是内置原生支持,它是Android中用来显示显示信息的一种机制.它主要用于向用户显示提示消息,没有焦点,显示的时间有限,过一定的时间就会自动消失.在UWP中虽然没有原 ...
随机推荐
- ExtJS 6 如何引入Dashboard模版
最近很多人问我在ext js 6+的版本中怎么引入官方的dashboard模版,正好我好久没写博客了,这里我写一篇博客来说明一下. 在这里以ext js 6.2.1版本为例(注:需要安装Sencha ...
- delphi向SQL Server2005中存取图片
SQL Server2005中,我用image类型来存取图片,首先把数据库表设置好 例如我的pic表有如下两列:时间,图片. delphi中,我用ADOQuery来连接数据库,但是数据库中有好几张表, ...
- Mysql04
mysql: dbs 数据库系统 bdms 数据库管理系统 bda 数据库管理员 db 数据库 dba通过dbms来操作db! 关系型数据库和非关系型数据库 登录mysql mysql -h主机地址 ...
- New Concept English Two 6 13
$课文11 礼尚往来 105. I was having dinner at a restaurant when Tony Steele came in. 我正在一家饭馆吃饭,托尼.斯蒂尔走了进来. ...
- CUDA Samples: matrix multiplication(C = A * B)
以下CUDA sample是分别用C++和CUDA实现的两矩阵相乘运算code即C= A*B,CUDA中包含了两种核函数的实现方法,第一种方法来自于CUDA Samples\v8.0\0_Simple ...
- LARC Caffe笔记(一) Setup
采用杂记的形式 从查找到知乎页面开始:https://www.zhihu.com/question/27982282 到mnist example页面:http://caffe.berkeleyvis ...
- asp.net core microservices 架构之分布式自动计算(三)-kafka日志同步至elasticsearch和kibana展示
一 kafka consumer准备 前面的章节进行了分布式job的自动计算的概念讲解以及实践.上次分布式日志说过日志写进kafka,是需要进行处理,以便合理的进行展示,分布式日志的量和我们对日志的重 ...
- OPENQUERY用法以及使用需要注意的地方
对给定的链接服务器执行指定的传递查询.该服务器是 OLE DB 数据源.OPENQUERY 可以在查询的 FROM 子句中引用,就好象它是一个表名.OPENQUERY 也可以作为 INSERT.UPD ...
- Oracle密码中含有特殊字符时exp,imp的使用
今天通过exp迁移Oracle 数据,由于密码含有很有很多特殊字符,弄了好久,都没成功,后发在网上找到方法. 1 exp用法Linux 下(密码用一对双引号, 整体userid用对单引号括住)exp ...
- Java多线程编程核心技术,第三章
1,notify的同步块完了,才会运行wait的同步块 2,interrupt()不是静态方法,用在wait的线程上会有InteruptException,锁也会被释放 3,notify()唤醒的线程 ...