这是@尚春实现的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雏形的更多相关文章

  1. 230行实现一个简单的MVVM

    作者:mirone链接:https://zhuanlan.zhihu.com/p/24451202来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. MVVM这两年在前端届 ...

  2. 如何实现一个简单的MVVM框架

    接触过web开发的同学想必都接触过MVVM,业界著名的MVVM框架就有AngelaJS.今天闲来无事,决定自己实现一个简单的MVVM框架玩一玩.所谓简单,就是仅仅实现一个骨架,仅表其意,不摹其形. 分 ...

  3. 一个简单的 MVVM 实现

    简介 一个简单的带有双向绑定的 MVVM 实现. 例子 使用 新建一个 ViewModel 对象, 参数分别为 DOM 元素以及绑定的数据即可. 指令 本 MVVM 的指令使用 data 数据, 即 ...

  4. 撸一个简单的MVVM例子

    我个人以为mvvm框架里面最重要的一点就是VM这部分,它要与Model层建立联系,将Model层转换成可以被View层识别的数据结构:其次也要同View建立联系,将数据及时更新到View层上,并且响应 ...

  5. JavaScript 实现一个简单的MVVM前端框架(ES6语法)

    前言 随着前端各大框架的崛起,为我们平时的开发带来了相当的便利,我们不能一直停留在应用层面,今天就自己动手实现一个乞丐版的MVVM小框架 完整代码github地址 效果 html代码 <div ...

  6. 基于vue实现一个简单的MVVM框架(源码分析)

    不知不觉接触前端的时间已经过去半年了,越来越发觉对知识的学习不应该只停留在会用的层面,这在我学jQuery的一段时间后便有这样的体会. 虽然jQuery只是一个JS的代码库,只要会一些JS的基本操作学 ...

  7. 用js实现一个简单的mvvm

    这里利用的object.defineproperty() 方法; <input     id='input'><p id='p'><p/>js: const dat ...

  8. MVVM之旅(1)创建一个最简单的MVVM程序

    这是MVVM之旅系列文章的第一篇,许多文章和书喜欢在开篇介绍某种技术的诞生背景和意义,但是我觉得对于程序员来说,一个能直接运行起来的程序或许能够更直观的让他们了解这种技术.在这篇文章里,我将带领大家一 ...

  9. 【UWP开发】一个简单的Toast实现

    Toast简介 在安卓里Toast是内置原生支持,它是Android中用来显示显示信息的一种机制.它主要用于向用户显示提示消息,没有焦点,显示的时间有限,过一定的时间就会自动消失.在UWP中虽然没有原 ...

随机推荐

  1. JavaScript---详解cookie

    1.什么是cookie? 在讨论cookie之前,需要知道“会话跟踪”这个概念. 1. 什么是会话  客户端打开与服务器的连接发出请求到服务器响应客户端请求的全过程称之为会话 2. 什么是会话跟踪  ...

  2. [例1.10]使用setw设置输出宽度的例子

    [例1.10]使用setw设置输出宽度的例子: #include <iostream> #include <iomanip> using namespace std; void ...

  3. 硬盘安装CentOS 6.0(超级详细图文教程)

    硬盘安装CentOS 6.0(超级详细图文教程) 来源:   引言: 电脑系统是Windows XP,电脑没有光驱.手头没有U盘.没有移动硬盘.电脑主板不支持U盘启动,在这种情况下想安装CentOS ...

  4. Sqlserver:datetime类型的精度(不确定性)问题

    转自http://www.xuebuyuan.com/212359.html 背景:近日进行大型数据表的迁移处理,遭遇创建 主键时 索引键值重复的错误.仔细检查原始表,并未有任何问题.分析后发现是迁移 ...

  5. .net 面试题总结

    1. DataSet和DataReader的区别? DataReader:和数据库处于一直连接状态.只读只能向前读取,一次只能读取一行信息.DataReader每次只在内存中加载一条数据,内存占用少, ...

  6. linux中~和/区别

    /是指根目录  就是所有目录最顶层的目录~指的是你当前用户的主目录  如果是root用户的话就是/root/目录    如果是其他用户的话就是/home/下以你用户名命名的用户 在linux里面,~/ ...

  7. JS字符串的问题

    首先,搞了好几个小时,头都大了,原来出在字符串问题上. 具体如下: 今天做Yii,遇到用JQuery 的AJAX方法做注册验证,把传回来的字符串与textField中的比较,发现相等,但是就是不出结果 ...

  8. 使用GitHub进行项目创建——初级,非指令版,纯软件操作

    主要步骤如下: 1.申请一个GitHub账号,官网按照步骤来就行 2.下载一个GitHub DeskTop(https://desktop.github.com/),命令什么的以后说不定会写把 3.创 ...

  9. 实例化Bean的方法(基于xml配置)-http://blog.csdn.net/shymi1991/article/details/48153293

    实例化Bean的方法(基于xml配置) 标签: spring framework 2015-09-01 13:43 918人阅读 评论(0) 收藏 举报  分类: Spring FrameWork(7 ...

  10. 使用VS2008进行WEB负载测试

    软件测试中使用VS2008进行WEB负载测试 最近接触关于多线程的软件测试,所以对vs2005里的测试开始感兴趣,今天学习试用了一下,记下所获得的. vs 测试分“WEB测试”,“单元测试”,“负载测 ...