简单vue2双向数据绑定代码
,支持解析{{}},v-bind,v-on指令
,支持数据监听,mounted钩子回调和methods方法
调用
let DataBing=function(dataoptions) {
let iswatch=false;
function DataBinginit(options) {
let self = this;
this.data = options.data.call(this);
this.methods = options.methods;
this.watchs = options.watchs;
Object.keys(this.data).forEach(function(key) {
self.proxyKeys(key);
});
observe(this.data);
new Compile(options.el, this);
iswatch=true;
options.mounted.call(this);
}
DataBinginit.prototype = {
proxyKeys: function(key) {
let self = this;
Object.defineProperty(this, key, {
enumerable: false,
configurable: true,
get: function getter() {
return self.data[key];
},
set: function setter(newVal) {
self.data[key] = newVal;
}
});
}
}
function Observer(data) {
this.data = data;
this.walk(data);
}
Observer.prototype = {
walk: function(data) {
let self = this;
Object.keys(data).forEach(function(key) {
self.defineReactive(data, key, data[key]);
});
},
defineReactive: function(data, key, val) {
let dep = new Dep();
let childObj = observe(val);
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function getter() {
if (Dep.target) {
dep.addSub(Dep.target);
}
return val;
},
set: function setter(newVal) {
if (newVal === val) {
return;
}
val = newVal;
dep.notify();
}
});
}
};
function observe(value, vm) {
if (!value || typeof value !== 'object') {
return;
}
return new Observer(value);
};
function Dep() {
this.subs = [];
}
Dep.prototype = {
addSub: function(sub) {
this.subs.push(sub);
},
notify: function() {
this.subs.forEach(function(sub) {
sub.update();
});
}
};
function Compile(el, vm) {
this.vm = vm;
this.el = document.querySelector(el);
this.fragment = null;
this.init();
}
Compile.prototype = {
init: function() {
if (this.el) {
this.fragment = this.nodeToFragment(this.el);
this.compileElement(this.fragment);
this.el.appendChild(this.fragment);
} else {
console.log('Dom元素不存在');
}
},
nodeToFragment: function(el) {
let fragment = document.createDocumentFragment();
let child = el.firstChild;
while (child) {
fragment.appendChild(child);
child = el.firstChild
}
return fragment;
},
compileElement: function(el) {
let childNodes = el.childNodes;
let self = this;
[].slice.call(childNodes).forEach(function(node) {
let reg = /\{\{(.*)\}\}/;
let text = node.textContent;
if (self.isElementNode(node)) {
self.compile(node);
} else if (self.isTextNode(node) && reg.test(text)) {
self.compileText(node, reg.exec(text)[1]);
}
if (node.childNodes && node.childNodes.length) {
self.compileElement(node);
}
});
},
compile: function(node) {
let nodeAttrs = node.attributes;
let self = this;
Array.prototype.forEach.call(nodeAttrs, function(attr) {
let attrName = attr.name;
if (self.isDirective(attrName)) {
let exp = attr.value;
let dir = attrName.substring(2);
if (self.isEventDirective(dir)) {
self.compileEvent(node, self.vm, exp, dir);
}else if(self.isattrDirective(dir)){
dir=dir.substring(6);
self.compileAttr(node, self.vm, exp, dir);
}else {
self.compileModel(node, self.vm, exp, dir);
}
node.removeAttribute(attrName);
}
});
},
compileText: function(node, exp) {
let self = this;
let initText = this.vm[exp];
this.updateText(node, initText);
new Watcher(this.vm, exp, function(value) {
self.updateText(node, value);
self.compilewatch(node, self.vm, exp);
});
},
compileEvent: function(node, vm, exp, dir) {
let self = this;
let eventType = dir.split(':')[1];
let ev=self.getargs(exp);
let args='';
if(ev){
args=ev;
let zk=exp.indexOf('(');
exp=exp.substr(0,zk)
}
let cb = vm.methods && vm.methods[exp];
if (eventType && cb) {
node.addEventListener(eventType,cb.bind(vm,args), true);
}
},
getargs:function(exp){
let reg = /\((.*)\)/;
let args;
if(reg.test(exp)){
args=reg.exec(exp)[1].split(",");
let reg1 = /\"(.*)\"/,
reg2 = /\'(.*)\'/;
for(let i in args){
if(reg1.test(args[i])){args[i]=reg1.exec(args[i])[1]}
if(reg2.test(args[i])){args[i]=reg2.exec(args[i])[1]}
}
}
return args;
},
compilewatch:function(node, vm, exp) {
let wt = vm.watchs && vm.watchs[exp];
if (wt&&iswatch) {
wt.call(vm);
}
},
compileModel: function(node, vm, exp, dir) {
let self = this;
let val = this.vm[exp];
this.modelUpdater(node, val,"value");
new Watcher(this.vm, exp, function(value) {
self.modelUpdater(node, value,"value");
self.compilewatch(node, self.vm, exp);
});
node.addEventListener('input', function(e) {
let newValue = e.target.value;
if (val === newValue) {
return;
}
self.vm[exp] = newValue;
val = newValue;
});
},
compileAttr: function(node, vm, exp, dir) {
let self = this;
let val = this.vm[exp];
this.modelUpdater(node, val,dir);
new Watcher(this.vm, exp, function(value) {
self.modelUpdater(node, value,dir);
self.compilewatch(node, self.vm, exp);
});
},
updateText: function(node, value) {
node.textContent = typeof value == 'undefined' ? '' : value;
},
modelUpdater: function(node, value, attr) {
node[attr] = typeof value == 'undefined' ? '' : value;
},
isDirective: function(attr) {
return attr.indexOf('v-') == 0;
},
isEventDirective: function(dir) {
return dir.indexOf('on:') === 0;
},
isattrDirective: function(dir) {
return dir.indexOf(':') === 4||dir.indexOf(':') === 5&&dir.indexOf('on:') === -1;
},
isElementNode: function(node) {
return node.nodeType == 1;
},
isTextNode: function(node) {
return node.nodeType == 3;
}
}
Dep.target = null;
function Watcher(vm, exp, cb) {
this.cb = cb;
this.vm = vm;
this.exp = exp;
this.value = this.get();
}
Watcher.prototype = {
update: function() {
this.run();
},
run: function() {
let value = this.vm.data[this.exp];
let oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal);
}
},
get: function() {
Dep.target = this;
let value = this.vm.data[this.exp]
Dep.target = null;
return value;
}
};
return new DataBinginit(dataoptions);
}
- 扩展
Date.prototype.format
支持Date.format格式化date
调用代码
function getNowDateStr() {
let me = this;
let date = new Date();
return date.format("当前时间为:YYYY-MM-DD,星期W,为第Q季度,时间为:hh:mm:ss:c");
}
Date.prototype.format = function (format) {
let o = {
"Y+": this.getFullYear() + '',
"M+": this.getMonth() + 1,
"D+": this.getDate(),
"h+": this.getHours(),
"m+": this.getMinutes(),
"s+": this.getSeconds(),
"Q+": Math.floor((this.getMonth() + 3) / 3),
"c+": this.getMilliseconds(),
"W": ['一', '二', '三', '四', '五', '六', '日'][this.getDay() - 1]
}
for (let k in o) {
if (new RegExp("(" + k + ")").test(format)) {
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr((("" + o[k]).length >= 2 ? 2 : ("" + o[k]).length)))
}
}
return format
}
<html lang="cn">
<head>
<meta charset="UTF-8">
<title>双向数据绑定原理</title>
</head>
<style>
#app {
text-align: center;
}
</style>
<body>
<div id="app">
<h2>{{title}}</h2>
<!--<input v-model="name">-->
<input id="input" v-model="name" placeholder="请输入" v-bind:name="attrname"></input>
<h1>{{name}}</h1>
<h1>{{name1}}</h1>
<h1>当前时间为<span>{{date}}</span></h1>
<button v-on:click="clickMe('1','2')">click me 来清空input输入框值!</button>
<button v-on:click="getval">获取input输入框值!</button>
<button v-on:click="clickto">click me try!</button>
</div>
</body>
<script src="index.js"></script>
<script type="text/javascript">
new DataBing({
el: '#app',
data(){
return{
title: 'hello world!',
name: '333',
name1: '哈哈哈',
attrname: "attrname",
date: getNowDateStr(),
}
},
methods: {
clickMe: function (c1,c2) {
var ev = window.event;
this.name ='';
this.attrname = 'attrname';
console.log(ev,c1,c2)
},
clickto:function () {
this.name = 'hello world';
this.attrname = '测试'+getNowDateStr();
},
getval:function(){
alert(this.name);
}
},
watchs: {
name:function(){
console.log(getNowDateStr()+'输入框的值为 : '+this.name);
},
attrname:function(){
console.log('输入框的name为:'+ this.attrname);
}
},
mounted: function () {
window.setInterval(() => {
this.date=getNowDateStr();
}, 1000);
}
});
</script>
</html>
- vue3
proxy
语法详解,可以借助proxy自行实现一个简单的双向数据绑定
var object = { proxy: new Proxy(target, handler) };
var handler = {
get: function(target, name) {
console.log("get")
if (name === 'prototype') {
return Object.prototype;
}
return 'Hello, ' + name;
},
set: function(target, thisBinding, args) {
console.log("set")
return 20;
},
apply: function(target, thisBinding, args) {
console.log("apply")
return args[0];
},
construct: function(target, args) {
console.log("construct")
return { value: args[1] };
}
};
var fproxy = new Proxy({}, handler);
var fproxy2 = new Proxy(function(x, y) {
return x + y;
}, handler);
let obj = Object.create(proxy);
fproxy2(1, 2)
const target = new Date('2015-01-01');
const handler = {
get(target, prop) {
if (prop === 'getDate') {
return target.getDate.bind(target);
}
return Reflect.get(target, prop);
}
};
const proxy = new Proxy(target, handler);
proxy.getDate()
var obj = {
value: ''
}
var value = '';
Object.defineProperty(obj, "value", {
get: function() {
return value
},
set: function(newVal) {
value = newVal
}
})
document.querySelector('#input').oninput = function() {
var value = this.value;
obj.value = value;
document.querySelector('#text').innerHTML = obj.value;
}
const data = {
name: ''
}
Object.keys(data).forEach(function(key) {
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function() {
console.log('get');
},
set: function(newVal) {
console.log(`我修改了成了${newVal}`);
},
})
})
data.name = 'gg'