欢迎大家指导与讨论 : )
前言
关于MVVM的原理大家可以参考这篇文章。《【翻译】Object.observe()带来的数据绑定变革 》http://www.tuicool.com/articles/ZVVNBv。
文章中的代码为笔者实践时写的小demo,可以运行。
代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="app"> <h1>作用域A</h1> <input type="text" two-way-bind="test"> <h1 bind="test"></h1> </div> <hr /> <div id="apple"> <h1>作用域B</h1> <input type="text" two-way-bind="test"> <h1 bind="test"></h1> </div> <hr /> <script type="text/javascript"> // vm -> view model; v -> view // 自定义两个绑定“指令”一个是从vm->v的绑定,一个是双向绑定 var directive_bind = ['bind', 'two-way-bind']; // 常量 声明该类型仅仅是vm->v 的DOM渲染 var DOM = 'DOM' // 常量 声明该类型是双向绑定 var TWOWAY = 'TWOWAY' // 新增两个作用域,用的是元素ID var vm = new mvvm('app'); var vm2 = new mvvm('apple') // 这是一个构造函数 // 用来返回一个可以被监测变化、有固定范围的vm function mvvm(rootEle) { this.scope = rootEle // 监听vm observe(this) // 确定作用域 —— 同时为v -> vm做事件监听 fineScope(this) } function observe(vm) { // 监听vm对象模型的改变 Object.observe(vm, function(changes) { changes.forEach(function(change, i) { switch (change.type) { case 'add': fineScope(vm) break; case 'delete': fineScope(vm); break; case 'update': fineScope(vm); break; default: break; } }) }) } // vm的改变引起v的改变 此处是 vm -> v function updateView(ele, value, type) { if (type === DOM) { if (value) { ele.innerHTML = value } else { ele.innerHTML = '' } } else if (type == TWOWAY) { if (value) { ele.value = value } else { ele.value = '' } } } function fineScope(vm) { var vmRootEle = document.getElementById(vm.scope) // 递归遍历该作用域下的所有DOM findChild(vmRootEle, vm) } function findChild(ele, vm) { // 查找所有自定义属性:bind、two-way-bind for (var i = 0; i < directive_bind.length; i++) { // 如果DOM有我们的自定义绑定属性 if (ele.getAttribute(directive_bind[i]) && directive_bind[i] === 'bind') { // 则按 vm->v的方向进行渲染 updateView(ele, vm[ele.getAttribute('bind')], DOM) } else if (ele.getAttribute(directive_bind[i]) && directive_bind[i] === 'two-way-bind') { var attr = ele.getAttribute('two-way-bind') // 同理 updateView(ele, vm[attr], TWOWAY) // 有点不同,这里是双向绑定,需要为该DOM增加事件监听 twoWayBind(ele, attr, vm) } } // 递归遍历 if (ele.hasChildNodes()) { var sonNodes = ele.childNodes; for (var j = 0; j < sonNodes.length; j++) { if (sonNodes[j].nodeType === 1) { findChild(sonNodes[j], vm) } } } } function twoWayBind(ele, prop, vm) { // 局限性:本DOM只是用了input[text],如果要监听其他input[x]应该要用对应的监听方法 ele.oninput = function() { vm[prop] = ele.value } } </script> </body> </html>