vue.js 中双向绑定的实现---初级
1. 1 我们看到的变量,其实都不是独立的,它们都是windows对象上的属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
var a = 117;
console.log(a);
console.log(window.a);
</script>
</body>
</html>
运行结果:直接打印a 和 打印window下的a 结果一样
1.2 defineProperty(): 每个对象在对本身属性的设置或者调用时,都会调用set()或则get()函数;这个和php类的调用差不多,在php中,叫做魔术方法,在调用类时,会调用__construct()方法,在结束调用时,会调用__destruct() 方法;
<script>
var obj = {};
Object.defineProperty(obj, 'name1', {
set: function() {
console.log("set()方法被调用了");
},
get: function() {
console.log("get()方法被调用了");
}
});
var a = obj.name1; // 调用get()方法
obj.name1 = "huanying2015"; // 调用set()方法
</script>
运行结果:
2. 对obj对象的属性进行监控,当属性改变时,调用set方法:其实这里监控的也不是obj的属性,而是监控输入框value值的变换,通过keyup 事件来监控
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
window.onload = function() {
var OInput = document.querySelector("#aa");
var Odiv = document.querySelector("#bb");
var obj = {};
Object.defineProperty(obj, 'content', {
set: function(val) {
OInput.value = val;
Odiv.innerHTML = val;
},
});
// e.target.value获取的就是你选择接受事件的元素输入的或者选择的值。
// 参数e接收事件对象。
// 而事件对象也有很多属性和方法, 其中target属性是获取触发事件对象的目标, 也就是绑定事件的元素,
// e.target表示该DOM元素, 然后在获取其相应的属性值。
OInput.addEventListener('keyup', function(e) {
obj.content = e.target.value;
});
}
</script>
</head>
<body>
<input type="text" id="aa">
<div id="bb"></div>
</body>
运行结果:
3. 双向绑定预热之------碎片节点说明:创建一个碎片节点---->然后在id 为box的范围内查找节点,把所有的节点都插入到碎片节点中,然后在把碎片节点插入到id为box 的范围内,即先把 id = box 中的东西拿出来,然后玩一玩,检查一下,再把东西放回去
注意:DocumentFragment节点不属于文档树,继承的parentNode属性总是null。它有一个很实用的特点,当请求把一个DocumentFragment节点插入文档树时,插入的不是DocumentFragment自身,而是它的所有子孙节点。
<body>
<div id="box">
<input type="text" id="txt">
<span id="box2"></span>
</div>
<script>
function nodeToFragment(node) {
var frag = document.createDocumentFragment();
var childNode;
// 这里childNode 和node.firstChild 都同时指向node 的第一个节点,放在while 中,是作为一个判断语句使用,
// 如果node.firstChild 为true ,即有第一个节点,那么执行do的内容(while(true){do;}),如果没有第一个节点,那么终止循环
while (childNode = node.firstChild) {
// 1.把node的第一个子节点插入碎片节点中
// 2.注意注意:
// 3.有一个很重要的特性是,如果使用appendChid方法将原dom树中的节点添加到DocumentFragment中时,会删除原来的节点。
// 4.以上的第3点是while循环的基础:所以每次循环,node.firstChild 都指向不同的节点,直到循环完毕
frag.appendChild(childNode);
}
return frag;
}
var oBox = document.querySelector("#box");
var nodeList = nodeToFragment(oBox);
// 把原先被删除的节点重新一次性插入原处,当插入的节点比较多时,这可以调高效率,减少页面对DOM的渲染次数
oBox.appendChild(nodeList);
</script>
</body>
运行结果:
4. 绑定编译原理:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
window.onload = function() {
// 替换函数:对html 中的content 进行替换
function compile(node, ghost) {
// 当需要替换的节点为元素节点时(即节点类型 nodeType ==1),遍历元素中的属性节点,查找元素中有没有'v-model'属性,如果有,则进行替换
if (node.nodeType == 1) {
// 获取元素的所有属性,使用attributes属性获取
var attrs = node.attributes;
for (var i = 0, len = attrs.length; i < len; i++) {
// 当元素的属性名字为‘v-model’时
if (attrs[i].nodeName == "v-model") {
// 获取'v-model'属性的属性值,存在key中
var key = attrs[i].nodeValue;
// 设置元素节点的value 属性为 ghost.data.key // 所有的.都可以使用[]来替代//
node.value = ghost.data[key];
// 清除元素中的 ‘v-mdel’属性
node.removeAttribute('v-model');
}
}
};
// 设置正则匹配
var re = /\{\{(.*)?\}\}/;
// 如果为文本节点,即属性值为3时,
if (node.nodeType == 3) {
// 查找文本节点的值,如果匹配正则
if (re.test(node.nodeValue)) {
// 获取正则中的第一个匹配对象,即{{}}中的内容
var key = RegExp.$1;
// 清除两侧的空格
key = key.trim();
// 将文本内容替换为 ghost.data[key];
node.nodeValue = ghost.data[key];
}
}
};
// 替换函数
function nodeToFragment(node, ghost) {
var flag = document.createDocumentFragment();
var childNode;
while (childNode = node.firstChild) {
compile(childNode, ghost);
flag.appendChild(childNode);
}
return flag;
};
// 传值函数
function Ghost(opt) {
this.data = opt.data;
var id = opt.el;
var Obox = document.querySelector(id);
var node = nodeToFragment(Obox, this);
Obox.appendChild(node);
}
// 实例化函数,这里相当于在vue.js 中的 new Vue({});
new Ghost({
el: "#box",
data: {
content: 'hello,huanying2015,how are you?'
}
});
}
</script>
</head>
<body>
<div id="box">
<input type="text" id="txt" v-model="content">
<br> {{content}}
</div>
</body>
</html>
运行结果:
5.相应式绑定数据:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
window.onload = function() {
function responsive(obj, key, val) {
Object.defineProperty(obj, key, {
get: function() {
return val;
},
set: function(newVal) {
if (val == newVal) {
return;
};
val = newVal;
console.log('值改变了,新值为:' + val);
}
});
}; function observe(obj, ghost) {
Object.keys(obj).forEach(function(key) {
responsive(ghost, key, obj[key]);
});
};
// 替换函数:对html 中的content 进行替换
function compile(node, ghost) {
// 当需要替换的节点为元素节点时(即节点类型 nodeType ==1),遍历元素中的属性节点,查找元素中有没有'v-model'属性,如果有,则进行替换
if (node.nodeType == 1) {
// 获取元素的所有属性,使用attributes属性获取
var attrs = node.attributes;
for (var i = 0, len = attrs.length; i < len; i++) {
// 当元素的属性名字为‘v-model’时
if (attrs[i].nodeName == "v-model") {
var key = attrs[i].nodeValue;
// 这里 ghost[key] 只是一个中间变量
// 监控键盘松开事件,当键盘松开时,在ghost上增加一个属性key,然后获取监听目标的值,把它赋值给ghost[key]
node.addEventListener('keyup', function(ev) {
ghost[key] = ev.target.value;
});
// 然后把这个ghost[key]赋值给节点的value 属性
node.value = ghost[key];
// 删除属性节点‘v-model’
node.removeAttribute('v-model');
}
}
};
// 设置正则匹配
var re = /\{\{(.*)?\}\}/;
// 如果为文本节点,即属性值为3时,
if (node.nodeType == 3) {
// 查找文本节点的值,如果匹配正则
if (re.test(node.nodeValue)) {
// 获取正则中的第一个匹配对象,即{{}}中的内容
var key = RegExp.$1;
// 清除两侧的空格
key = key.trim();
// 将文本内容替换为 ghost.data[key];
node.nodeValue = ghost.data[key];
}
}
};
// 替换函数
function nodeToFragment(node, ghost) {
var flag = document.createDocumentFragment();
var childNode;
while (childNode = node.firstChild) {
compile(childNode, ghost);
flag.appendChild(childNode);
}
return flag;
};
// 传值函数
function Ghost(opt) {
this.data = opt.data;
var data = this.data;
var id = opt.el;
// 对当前对象及当前对象的data 数据对象进行监控
observe(data, this);
var Obox = document.querySelector(id);
var node = nodeToFragment(Obox, this);
Obox.appendChild(node);
}
// 实例化函数,这里相当于在vue.js 中的 new Vue({});
new Ghost({
el: "#box",
data: {
content: 'hello,huanying2015,how are you?'
}
});
}
</script>
</head>
<body>
<div id="box">
<input type="text" id="txt" v-model="content">
<br> {{content}}
</div>
</body>
</html>
运行结果:
6. 发布者,订阅者模式解析:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
// 发布者1 发布信息
sub1 = {
update: function() {
console.log("这是发布者1发出的信息");
}
}
// 发布者2 发布信息
sub2 = {
update: function() {
console.log("这是发布者2发出的信息");
}
}
// 发布者3 发布信息
sub3 = {
update: function() {
console.log("这是发布者3发出的信息");
}
}
//发布清单构造函数
function Dep() {
this.subs = [sub1, sub2, sub3];
}
// 发布清单原型对象上加nodify()方法
Dep.prototype.nodify = function() {
this.subs.forEach(function(sub) {
// 执行发布框架操作
sub.update();
});
}
// 实例化发布清单,即填写发布清单
var dep = new Dep();
// 一个实体指定出一套自己的发布方法
var pub = {
publish: function() {
// 发布方法中,执行发布清单的操作
dep.nodify();
}
}
// 发布执行
pub.publish();
</script>
</head>
<body>
</body>
</html>
运行结果:
7. 响应式数据绑定:即输入的同时,显示输出
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script>
window.onload = function() {
var Watcher = function(ghost, node, name) {
Dep.target = this;
this.name = name;
this.node = node;
this.ghost = ghost;
this.update();
Dep.target = null;
}
Watcher.prototype = {
update: function() {
this.get();
this.node.nodeValue = this.value;
},
get: function() {
this.value = this.ghost[this.name];
}
} function Dep() {
this.subs = [];
}
Dep.prototype = {
addsub: function(sub) {
this.subs.push(sub);
},
nodify: function() {
this.subs.forEach(function(sub) {
sub.update();
});
}
} function responsive(obj, key, val) {
var dep = new Dep();
Object.defineProperty(obj, key, {
get: function() {
if (Dep.target) {
dep.addsub(Dep.target);
}
console.log("access:" + val);
return val;
},
set: function(newVal) {
if (val == newVal) {
return;
}
val = newVal;
console.log("值改变了:" + val);
dep.nodify();
}
});
} function observe(obj, ghost) {
Object.keys(obj).forEach(function(key) {
responsive(ghost, key, obj[key]);
});
} function compile(node, ghost) {
var re = /\{\{(.*)\}\}/;
if (node.nodeType == 1) {
var attrs = node.attributes;
for (var i = 0, len = attrs.length; i < len; i++) {
if (attrs[i].nodeName == "v-model") {
var key = attrs[i].nodeValue;
node.addEventListener("keyup", function(ev) {
ghost[key] = ev.target.value;
});
node.value = ghost[key];
node.removeAttribute("v-model");
}
}
}
if (node.nodeType == 3) {
if (re.test(node.nodeValue)) {
var key = RegExp.$1;
key = key.trim();
new Watcher(ghost, node, key);
}
}
} function nodeToFragment(node, ghost) {
var flag = document.createDocumentFragment();
var childNode;
while (childNode = node.firstChild) {
compile(childNode, ghost);
flag.appendChild(childNode);
}
return flag;
} function Ghost(opt) {
this.data = opt.data;
var data = this.data;
observe(data, this);
var id = opt.el;
var obox = document.querySelector(id);
var node = nodeToFragment(obox, this);
obox.appendChild(node);
}
var ogt = new Ghost({
el: "#box",
data: {
content: "huanying2015",
msg: "ahifahha",
}
});
}
</script>
</head>
<body>
<div class="box" id="box">
<input type="text" id="tet" v-model="content"><br> {{content}}
</div>
</body>
</html>
运行结果:
vue.js 中双向绑定的实现---初级的更多相关文章
- Vue.js 关于双向绑定的一些实现细节
Vue.js 是采用 Object.defineProperty 的 getter 和 setter,并结合观察者模式来实现数据绑定的. 当把一个普通 Javascript 对象传给 Vue 实例来作 ...
- 原生js实现 vue的数据双向绑定
原生js实现一个简单的vue的数据双向绑定 vue是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时 ...
- vue.js中,input和textarea上的v-model指令到底做了什么?
v-model是 vue.js 中用于在表单表单元素上创建双向数据绑定,它的本质只是一个语法糖,在单向数据绑定的基础上,增加了监听用户输入事件并更新数据的功能: 对,它本质上只是一个语法糖,但到底是一 ...
- Vue框架之双向绑定事件
Vue框架之双向绑定事件 首先介绍下Vue框架的语法 vue通过 {{temp}} 来渲染变量 {{count+100}} # 求和 v-text # 为标签插入text文本 v-html # 为标签 ...
- 用ES6的class模仿Vue写一个双向绑定
原文地址:用ES6的class模仿Vue写一个双向绑定 点击在线尝试一下 最终效果如下: 构造器(constructor) 构造一个TinyVue对象,包含基本的el,data,methods cla ...
- 用原生 JS 实现双向绑定及应用实例
写在前面: 所谓的双向绑定,无非是从界面的操作能实时反映到数据,数据的变更也能实时展现到界面.angular封装了双向绑定的方法,使双向绑定变得十分简单.但是在有些场景下(比如下面那个场景),不能使用 ...
- vue.js 中 data, prop, computed, method,watch 介绍
vue.js 中 data, prop, computed, method,watch 介绍 data, prop, computed, method 的区别 类型 加载顺序 加载时间 写法 作用 备 ...
- Vue的数据双向绑定原理——Object-defineProperty
一.定义 ①方法会直接在一个对象上定义一个新属性,或者修改一个已经存在的属性, 并返回这个对象. ②vue.js的双向数据绑定就是通过Object.defineProperty方法实现的,俗称属性拦截 ...
- Vue的数据双向绑定和Object.defineProperty()
Vue是前端三大框架之一,也被很多人指责抄袭,说他的两个核心功能,一个数据双向绑定,一个组件化分别抄袭angular的数据双向绑定和react的组件化思想,咱们今天就不谈这种大是大非,当然我也没到达那 ...
随机推荐
- win7 没有权限使用网络资源
局域网下同一工作组电脑无法访问 提示"....没有权限使用网络资源...." 一.组策略 win + R 输入gpedit.msc并回车,打开本地组策略编辑器 按如下展开 计算机配 ...
- json--处理框架
1.Android 中的Json解析工具fastjson .序列化.反序列化 2.Android Gson的使用总结 可以处理含有内部类的类,或包含集合内部类的类: 3.Android-JSONToo ...
- Android中Handler.removeCallbacksAndMessages(null)的作用
众所周知,在Android开发中会大量使用到Handler.但是,由于与activity生命周期不一致,当一个Activity被销毁时,如果Handler对象有Message在排队,则无法释放,进而导 ...
- tf.nn.dropout
tf.nn.dropout(x, keep_prob, noise_shape=None, seed=None, name=None) 此函数是为了防止在训练中过拟合的操作,将训练输出按一定规则进行变 ...
- CentOS用户和用户组管理
groupadd grptest1 按照系统默认的gid创建组.根uid一样,gid也是从1000开始的. groupadd -g 1008 grptest2 创建gid=1008的用户组:g ...
- sbt 以及 IDEA sbt 插件安装配置教程(转)
1. 在Windows中安装sbt 下载 官网: http://www.scala-sbt.org/ github: https://github.com/sbt/sbt/releases/downl ...
- C# 委托在线程与UI界面之间的应用
前景:我们在使用线程的时候,经常会想要访问到Form窗体的控件,也就是线程与UI界面交互,但是他们隶属于连个不同的线程,所以是不能直接访问的,这个时候我们就可以通过委托来实现.打个比方,你想要给远方的 ...
- Elasticsearch的JavaAPI
获取客户端对象 public class App { private TransportClient client; //获取客户端对象 @Before public void getClinet() ...
- solr 请求参数过长报错,Solr配置maxBooleanClauses属性不生效原因分析
博客分类: 上次已经写过一篇关于solr中,查询条件过多的异常的文章,这次在总结扩展一下: 有时候我们的查询条件会非常多,由于solr的booleanquery默认设置的条件数为1024,所以超过 ...
- Django之用户认证auth模块
一 Django中的auth模块: 我们在使用模块之前要先导入模块: from django.contrib import auth django.contrib.auth中提供了许多方法,这里主要介 ...