这是@尚春实现的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>