1 Star 0 Fork 0

wangbo / learning

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
网上双向绑定demo.html 7.29 KB
一键复制 编辑 原始数据 按行查看 历史
wangbo 提交于 2020-12-29 13:12 . 牺牲午休写的,擦……
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Title</title>
<style>
#app {
width: 400px;
height: 400px;
margin: 50px auto 0;
}
#input {
outline: none;
border: 1px solid #ddd;
width: 280px;
display: inline-block;
line-height: 32px;
padding: 2px 20px;
border-radius: 8px;
margin-top: 30px;
color: #555;
}
</style>
</head>
<body>
<!--<h2>双向绑定示例</h2>-->
<div id="app">
<div>{{val}}</div>
<input type="text" id="input" v-model="val"></div>
<script>
// 发布
function Sub() {
this.subs = [];
}
Sub.prototype = {
add(sub) {
this.subs.push(sub);
},
trigger() {
//debugger
this.subs.forEach(sub => {
// 触发 Watcher实例 的方法
sub.update();
})
}
};
// Sub.target 的值为 一个 Watcher 实例
Sub.target = null;
// 数据劫持
function observe(data) {
//debugger
if (typeof data !== 'object' || !data) return;
// 遍历$data的key
Object.keys(data).forEach(item => {
let val = data[item];
let sub = new Sub();
Object.defineProperty(data, item, {
// 当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。
enumerable: true,
// 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除
configurable: false,
// get方法
get() {
//debugger
if (Sub.target) {
// 将 Watcher 实例添加到订阅列表
sub.add(Sub.target);
}
return val;
},
set(newVal) {
//debugger
val = newVal;
sub.trigger();
}
})
})
}
// 观察者
function Watcher(vm, prop, callback) {
//debugger
this.vm = vm;
this.prop = prop;
this.callback = callback;
Sub.target = this;
// 此处会触发 get 方法
let val = this.vm.$data[prop];
Sub.target = null;
// 这个 value 是 data对象里的 “旧” 值
this.vaule = val;
}
// 将在 data属性 的 set方法 中调用,input 事件触发后重新给 data属性赋值,并且会触发 set 方法
Watcher.prototype.update = function () {
//debugger
let newValue = this.vm.$data[this.prop];
if (this.value !== newValue) {
this.value = newValue;
// callback:val => { node.value = val }
// 将 新的值 赋给 input元素的 value(call函数的第一个参数可以不是 this.vm 不影响)
this.callback.call(this.vm, newValue);
}
}
// 编译构造函数
function Compile(vm) {
this.vm = vm;
this.el = vm.$el;
this.init();
}
// 编译初始化
Compile.prototype.init = function () {
//debugger
// 创建了一虚拟的节点对象,节点对象包含所有属性和方法
let fragment = document.createDocumentFragment();
// 取到有模版语法的div
let child = this.el.firstChild;
while (child) {
// 依次将 this.el 中的元素 append 到虚拟节点对象,append 一个少一个。当 this.el 没有子元素时,this.el.firstChild = null
fragment.append(child);
child = this.el.firstChild;
}
// 获取子元素 NodeList
let childNodes = fragment.childNodes;
Array.from(childNodes).forEach(node => {
//debugger
// nodeType 等于1 代表元素节点(避开一些空白节点之类的)
if (node.nodeType === 1) {
// 获取元素的所有属性节点,一个 ArrayLike
let attrs = node.attributes;
Array.from(attrs).forEach(attr => {
// 元素属性的名称
let name = attr.nodeName;
if (name === 'v-model') {
// 获取 v-model 上绑定的 key
let prop = attr.nodeValue;
// 获取 Vue data对象中的值
let value = this.vm.$data[prop];
// 此处的 node 是 input元素
node.value = value;
// 生成更改 input元素的 Watcher 实例(传入 Vue实例,data对象 key,更改 input元素 value 的回调函数)
new Watcher(this.vm, prop, val => {
node.value = val;
});
// 给 input元素 绑定 input事件,并将输入的值赋给 Vue data对象对应的属性
node.addEventListener('input', e => {
//debugger
let newVal = e.target.value;
if (value !== newVal) {
// 此处会触发该属性的 set 方法
this.vm.$data[prop] = newVal;
} else {
// 经测试,下面代码应该不会触发,上面判断多余
console.log(99999999999999999999)
}
})
}
})
}
let reg = /\{\{(.*)\}\}/;
// textContent 属性设置或者返回指定节点的文本内容 ==> {{val}}
let text = node.textContent;
if (reg.test(text)) {
// RegExp这个对象会在我们调用了正则表达式的方法后, 自动将最近一次的结果保存在里面, 所以如果我们在使用正则表达式时, 有用到分组, 那么就可以直接在调用完以后直接使用RegExp.$xx来使用捕获到的分组内容
let prop = RegExp.$1; // val
// 将 匹配到的 data属性值 赋给 div
node.textContent = this.vm.$data[prop];
// 生成 更改 div 文本的 Watcher 实例
new Watcher(this.vm, prop, val => {
// console.log(node);
node.textContent = val;
});
}
})
this.el.appendChild(fragment);
}
// Vue构造函数
function MyVue(options) {
this.$options = options;
this.$el = options.el;
this.$data = options.data;
this.init();
}
// Vue初始化操作
MyVue.prototype.init = function () {
observe(this.$data);
new Compile(this);
};
const vm = new MyVue({
el: document.getElementById('app'),
data: {
val: 123
}
})
window.onload = function () {
document.getElementById('input').focus();
}
</script>
</body>
</html>
JavaScript
1
https://gitee.com/dizuncainiao/learning.git
git@gitee.com:dizuncainiao/learning.git
dizuncainiao
learning
learning
master

搜索帮助