这是@尚春实现的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. [vuex]——使用vuex解决模块间传值问题

    二月的第四个周末,在家.受寒流的影响,深圳天气持续冰冻了好几天,天冷人就变得懒动,迷迷糊糊睡到了快十点,终于在饥饿的催促下起床. 和妹子吃完粥后,百无聊赖.透过窗户,发现太阳依旧没有露头的打算,我们也 ...

  2. 记录Tomcat8.5文件上传,文件权限无法访问

    部署一个服务,文件上传本地可以,但是在Linux上通过docker发布到容器上,文件上传后,没有权限访问,查了好久才发现是Tomcat8.5的问题,低版本没有这个问题,现记录下. tomcat/bin ...

  3. CentOS6.5系统服务

    服务名称 功能 默认 建议 备注说明 NetworkManager 用于自动连接网络,常用在Laptop上 开启 关闭 对服务器无用 abrt-ccpp   开启 自定 对服务器无用 abrt-oop ...

  4. SharpNodeSettings项目,可配置的数据采集,统一的工业数据网关,OPC UA服务器开发,PLC数据发布到自身内存,redis,opc ua,以及数据可视化开发

    本项目隶属于 HslCommunication 项目的SDK套件,如果不清楚HslCommunication组件的话,可以先了解那个项目,源代码地址:https://github.com/dathli ...

  5. Linux解压rar、zip、war、tar文件

    在Linux上解压常见文件的命令: rar文件:rar e xxx.rar zip文件:unzip -xzvf xxx.zip war包:jar -xvf xxx.war tar包:tar -xzvf ...

  6. CNN中卷积层的计算细节

    原文链接: https://zhuanlan.zhihu.com/p/29119239 卷积层尺寸的计算原理 输入矩阵格式:四个维度,依次为:样本数.图像高度.图像宽度.图像通道数 输出矩阵格式:与输 ...

  7. 這是我既C語言作業寫博客後寫的第一篇博客

    這篇博客應該算是寫給我自己的博客吧,所以這裏我想用繁體字寫,因為我漸漸地發現我已經很少使用到繁體字了,日常QQ聊天都使用簡體字,繁體字都懶得切換了,但是為了不讓別人麻煩,在外界交流的時候我會使用簡體字 ...

  8. java重写toString方法

    在输出对象属性时,重写toString按照你希望的输出形式重写 object类里的toString只是把字符串的直接打印,数字的要转化成字符再打印,而对象,则直接打印该对象的hash码.所以当你要想按 ...

  9. MemSQL start[c]up Round 1 B题

    题目链接 http://codeforces.com/contest/325/problem/B 第一遍写了暴搜,果断TLE 然后上了二分,结果120组数据只有第40组过不了,我就写了奇怪的东西... ...

  10. mongodb,redis,mysql的区别和具体应用场景

    一.MySQL 关系型数据库. 在不同的引擎上有不同 的存储方式. 查询语句是使用传统的sql语句,拥有较为成熟的体系,成熟度很高. 开源数据库的份额在不断增加,mysql的份额页在持续增长. 缺点就 ...