//dom结构
<div id="app">
<input type="text" v-model="msg">
<p>{{msg}}</p>
<ul>
<li>1</li>
<li>{{msg}}</li>
<li>{{test}}</li>
</ul>
</div>
//one-way-binding.js
//判断每个dom节点是否拥有子节点,若有则返回该节点
function isChild(node){
//这里使用childNodes可以读取text文本节点,所以不用children
if(node.childNodes.length ===0){
return false;
}
else{
return node;
}
}
//利用文档碎片劫持dom结构及数据,进而进行dom的重构
function nodeToFragment(node,vm){
var frag = document.createDocumentFragment();
var child;
while(child = node.firstChild){
//一级dom节点数据绑定
compile(child,vm);
//判断每个一级dom节点是否有二级节点,若有则递归处理文档碎片
if(isChild(child)){
//递归实现二级dom节点的重构
nodeToFragment(isChild(child),vm);
}
frag.appendChild(child);
}
//将文档碎片添加至对应node中,最后为id为app的元素下
node.appendChild(frag);
}
//初始化绑定数据
function compile(node,vm){
//node节点为元素节点时
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;
//将data下对应属性名的值赋值给当前节点值
//这里因为node是input标签所以值为node.value
node.value = vm.data[name];
//最后标签中的v-model属性也可以功成身退了,删除它
node.removeAttribute(attr[i].nodeName);
}
}
}
//node节点为text文本节点#text时
if(node.nodeType === 3){
var reg = /\{\{(.*)\}\}/;
if(reg.test(node.nodeValue.trim())){
//将正则匹配到的{{}}中的字符串赋值给name
var name = RegExp.$1;
//利用name对应赋值相应的节点值
node.nodeValue = vm.data[name];
}
}
}
//MVVM构造函数,这里我就写成Vue了
function Vue(options){
this.id = options.el;
this.data = options.data;
//将根节点与实例化后的对象作为参数传入
nodeToFragment(document.getElementById(this.id),this);
}
//实例化
var vm = new Vue({
el:'app',
data:{
msg:'hello,two-ways-binding',
test:'test key'
}
})
//判断每个dom节点是否拥有子节点,若有则返回该节点
function isChild(node){
if(node.childNodes.length ===0){
return false;
}
else{
return node;
}
}
//利用文档碎片劫持dom结构及数据,进而进行dom的重构
function nodeToFragment(node,vm){
var frag = document.createDocumentFragment();
var child;
while(child = node.firstChild){
//一级dom节点数据绑定
compile(child,vm);
//判断每个一级dom节点是否有二级节点,若有则递归处理文档碎片
if(isChild(child)){
nodeToFragment(isChild(child),vm);
}
frag.appendChild(child);
}
node.appendChild(frag);
}
//初始化绑定数据
function compile(node,vm){
//node节点为元素节点时
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;
//特殊处理input标签
//------------------------
if(node.nodeName === 'INPUT'){
node.addEventListener('keyup',function(e){
vm[name] = e.target.value;
})
}
//由于数据已经由data劫持至vm下,所以直接赋值vm[name]即可触发getter访问器
node.value = vm[name];
//-------------------------
node.removeAttribute(attr[i].nodeName);
}
}
}
//node节点为text文本节点时
if(node.nodeType === 3){
var reg = /\{\{(.*)\}\}/;
if(reg.test(node.nodeValue.trim())){
var name = RegExp.$1;
//node.nodeValue = vm[name];
//----------------------
//为每个节点建立订阅者,通过订阅者watcher初始化及更新视图数据
new watcher(vm,node,name);
//-----------------------
}
}
}
//----------------------------------------------------------------
//订阅者(为每个节点的数据建立watcher队列,每次接受更改数据需求后,利用劫持数据执行对应节点的数据更新)
function watcher(vm,node,name){
//将每个挂载了数据的dom节点添加到通知者列表,要保证每次创建watcher时只有一个添加目标,否则后续会因为watcher是全局而被覆盖,所以每次要清空目标
Dep.target = this;
this.vm = vm;
this.node = node;
this.name = name;
//执行update的时候会调用监听者劫持的getter事件,从而添加到watcher队列,因为update中有访问this.vm[this.name]
this.update();
//为保证只有一个全局watcher,添加到队列后,清空全局watcher
Dep.target = null;
}
watcher.prototype = {
update(){
this.get();
//input标签特殊处理化
if(this.node.nodeName === 'INPUT'){
this.node.value = this.value;
}
else{
this.node.nodeValue = this.value;
}
},
get(){
//这里调用了数据劫持的getter
this.value = this.vm[this.name];
}
};
//通知者(将监听者的更改信息需求发送给订阅者,告诉订阅者哪些数据需要更改)
function Dep(){
this.subs = [];
}
Dep.prototype = {
addSub(watcher){
//添加用到数据的节点进入watcher队列
this.subs.push(watcher);
},
notify(){
//遍历watcher队列,令相应数据节点重新更新view层数据,model => view
this.subs.forEach(function(watcher){
watcher.update();
})
}
};
//监听者(利用setter监听view => model的数据变化,发出通知更改model数据后再从model => view更新视图所有用到该数据的地方)
function observer(data,vm){
//遍历劫持data下所有属性
Object.keys(data).forEach(function(key){
defineReactive(vm,key,data[key]);
})
}
function defineReactive(vm,key,val){
//新建通知者
var dep = new Dep();
//灵活利用setter与getter访问器
Object.defineProperty(vm,key,{
get(){
//初始化数据更新时将每个数据的watcher添加至队列栈中
if(Dep.target) dep.addSub(Dep.target);
return val;
},
set(newVal){
if(val === newVal) return ;
//初始化后,文档碎片中的虚拟dom已与model层数据绑定起来了
val = newVal;
//同步更新model中data属性下的数据
vm.data[key] = val;
//数据有改动时向通知者发送通知
dep.notify();
}
})
}
//---------------------------------------------------------------
function Vue(options){
this.id = options.el;
this.data = options.data;
observer(this.data,this);
nodeToFragment(document.getElementById(this.id),this);
}
var vm = new Vue({
el:'app',
data:{
msg:'hello,two-ways-binding',
test:'test key'
}
})
机械节能产品生产企业官网模板...
大气智能家居家具装修装饰类企业通用网站模板...
礼品公司网站模板
宽屏简约大气婚纱摄影影楼模板...
蓝白WAP手机综合医院类整站源码(独立后台)...苏ICP备2024110244号-2 苏公网安备32050702011978号 增值电信业务经营许可证编号:苏B2-20251499 | Copyright 2018 - 2025 源码网商城 (www.ymwmall.com) 版权所有