MVVM实现原理(数据变更的实现)
来源:互联网 发布:mac os支持office 2016 编辑:程序博客网 时间:2024/06/10 19:37
手动触发绑定
手动触发指令绑定是比较直接的实现方式,主要思路是通过在数据对象上定义get()方法和set()方法,调用时手动触发get()或set()函数来获取、修改数据,改变数据后会主动触发get()和set()函数中View层的重新渲染功能。根据View来驱动ViewModel变化的场景主要应用于<input>、<select>、<textarea>
等元素,当用户输入内容变化时,通过监听DOM的change,select、keyup等事件来触发操作改变ViewModel的数据。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title></head><body><input q-value="value" type="text" id="input"><span q-text="value" id="el"></span><script>let elems=[document.getElementById('input'),document.getElementById('el')];let data={value:'hello'};let directive={ text:function (text) { this.innerHTML=text; }, value:function (value) { this.setAttribute('value',value); }}function scan() { for(let elem of elems){ elem.directive=[]; for(let attr of elem.attributes){ if(attr.nodeName.indexOf('q-')>=0){ directive[attr.nodeName.slice(2)].call(elem,data[attr.nodeValue]); elem.directive.push(attr.nodeName.slice(2)); } } }}function ViewModelSet(key,value) { data[key]=value; scan();}scan();setTimeout(function () { ViewModelSet('value','helloouvenzhang');},1000);if(document.addEventListener){ elems[0].addEventListener('keyup',function (e) { ViewModelSet('value',e.target.value); },false);}</script></body></html>
前端数据对象挟持
其基本思路是使用Object.defineProperty和Object.defineProperties对ViewModel数据对象进行属性get()和set()的监听,当有数据读取和赋值操作时则扫描元素节点,运行对应节点的Directive指令。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title></head><body><input q-value="value" type="text" id="input"><div q-text="value" id="el"></div><script>let elems=[document.getElementById('el'),document.getElementById('input')];let data={value:'hello'};let directive={ text:function (text) { this.innerHTML=text; }, value:function (value) { this.setAttribute('value',value); }};let bValue;scan();defineGetAndSet(data,'value');if(document.addEventListener){ elems[1].addEventListener('keyup',function (e) { data.value=e.target.value; },false);} else { elems[1].attachEvent('onkeyup',function (e) { data.value=e.srcElement.value; },false);}setTimeout(function () { data.value='helloouvenzhang';},2000);function scan() { for(let elem of elems){ elem.directive=[]; for(let attr of elem.attributes){ if(attr.nodeName.indexOf('q-')>=0){ directive[attr.nodeName.slice(2)].call(elem,data[attr.nodeValue]); elem.directive.push(attr.nodeName.slice(2)); } } }}function defineGetAndSet(obj,propName) { Object.defineProperty(obj,propName,{ get:function () { return bValue; }, set:function (newValue) { bValue=newValue; scan(); }, enumerable:true, configurable:true })}</script></body></html>
ES6 Proxy
Proxy特性可以用于在已有的对象基础上重新定义一个对象,并重新定义对象原型上的方法,包括get()和set()方法。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title></head><body><input q-value="value" type="text" id="input"><span q-text="value" id="el"></span><script> let elems=[document.getElementById('el'),document.getElementById('input')]; let directive={ text:function (text) { this.innerHTML=text; }, value:function (value) { this.setAttribute('value',value); } }; let data = new Proxy({},{ get:function (target,key,receiver) { return target.value; }, set:function (target,key,value,receiver) { target.value=value; scan(); return target.value; } }); data['value']='hello'; scan(); if(document.addEventListener){ elems[1].addEventListener('keyup',function (e) { data.value=e.target.value; },false); } else { elems[1].attachEvent('onkeyup',function (e) { data.value=e.srcElement.value; },false); } setTimeout(function () { data.value='hello ouvenzhang'; },1000); function scan() { for(let elem of elems){ elem.direction=[]; for(let attr of elem.attributes){ if(attr.nodeName.indexOf('q-')>=0){ directive[attr.nodeName.slice(2)].call(elem,data[attr.nodeValue]); elem.directive.push(attr.nodeName.slice(2)); } } } }</script></body></html>
vue.js实现原理
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Two-way data-binding</title></head><body><div id="app"> <input type="text" v-model="text"> {{ text }}</div><script> function observe (obj, vm) { Object.keys(obj).forEach(function (key) { defineReactive(vm, key, obj[key]); }); } function defineReactive (obj, key, val) { var dep = new Dep(); Object.defineProperty(obj, key, { get: function () { if (Dep.target) dep.addSub(Dep.target); return val }, set: function (newVal) { if (newVal === val) return val = newVal; dep.notify(); } }); } function nodeToFragment (node, vm) { var flag = document.createDocumentFragment(); var child; while (child = node.firstChild) { compile(child, vm); flag.appendChild(child); } return flag; } function compile (node, vm) { var reg = /\{\{(.*)\}\}/; // 节点类型为元素 if (node.nodeType === 1) { var attr = node.attributes; // 解析属性 for (var i = 0; i < attr.length; i++) { if (attr[i].nodeName == 'v-model') { var name = attr[i].nodeValue; // 获取v-model绑定的属性名 node.addEventListener('input', function (e) { // 给相应的data属性赋值,进而触发该属性的set方法 vm[name] = e.target.value; }); node.value = vm[name]; // 将data的值赋给该node node.removeAttribute('v-model'); } } new Watcher(vm, node, name, 'input'); } // 节点类型为text if (node.nodeType === 3) { if (reg.test(node.nodeValue)) { var name = RegExp.$1; // 获取匹配到的字符串 name = name.trim(); new Watcher(vm, node, name, 'text'); } } } function Watcher (vm, node, name, nodeType) {// this为watcher函数 Dep.target = this;// console.log(this); this.name = name; this.node = node; this.vm = vm; this.nodeType = nodeType; this.update(); Dep.target = null; } Watcher.prototype = { update: function () { this.get(); if (this.nodeType == 'text') { this.node.nodeValue = this.value; } if (this.nodeType == 'input') { this.node.value = this.value; } }, // 获取daa中的属性值 get: function () { this.value = this.vm[this.name]; // 触发相应属性的get } } function Dep () { this.subs = [] } Dep.prototype = { addSub: function(sub) { this.subs.push(sub); }, notify: function() { this.subs.forEach(function(sub) { sub.update(); }); } }; function Vue (options) { this.data = options.data; var data = this.data; observe(data, this); var id = options.el; var dom = nodeToFragment(document.getElementById(id), this); // 编译完成后,将dom返回到app中 document.getElementById(id).appendChild(dom); } var vm = new Vue({ el: 'app', data: { text: 'hello world' } });</script></body></html>
阅读全文
0 0
- MVVM实现原理(数据变更的实现)
- WPF与MVVM的实现(二)数据绑定
- WPF与MVVM的实现(一)MVVM简介
- 对话框的MVVM实现
- 使用SQLServer 2008的CDC功能实现数据变更捕获
- 使用SQLServer 2008的CDC功能实现数据变更捕获
- 使用SQLServer 2008的CDC功能实现数据变更捕获
- 使用SQLServer 2008的CDC功能实现数据变更捕获
- 使用SQLServer 2008的CDC功能实现数据变更捕获
- 使用SQLServer 2008的CDC功能实现数据变更捕获
- 使用SQLServer 2008的CDC功能实现数据变更捕获
- 利用 JavaScript 数据绑定实现一个简单的 MVVM 库
- 剖析Vue原理&实现双向绑定MVVM
- 剖析Vue原理&实现双向绑定MVVM
- 剖析Vue原理&实现双向绑定MVVM
- 剖析Vue原理&实现双向绑定MVVM
- WPF与MVVM的实现(三)List的数据绑定
- SQLServer CDC实现数据变更捕获
- Tomcat 源码阅读(三)Catalina.start
- webservice客户端不生成JAXBElement类型
- Internet thin-stream 交互式应用 TCP延迟
- 大文件下载---之内存溢出问题解决
- [js高手之路]Node.js实现简易的爬虫-抓取博客所有文章列表信息
- MVVM实现原理(数据变更的实现)
- java注解
- Java处理线程同步/线程安全的方式概述
- Java基本数据类型总结
- Spark RDD 持久化
- Item组件
- jdk源码研究1-HashMap
- " " is not valid path to the gcc binary
- 用Tensorflow Object Detection API 训练自己的数据集