原生js实现vue双向数据绑定
【代码】原生js实现vue双向数据绑定。
html:
在HTML中使用v-model的指令,并且引入自定义的vue.js
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head><body>
<div id="app">
<span>姓名:{{ name }}</span>
<input type="text" v-model="name"></input>
<span>更多:{{ more.like }}</span>
<input type="text" v-model="more.like">
</div><script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
name: '猿小野',
more: {
like: '打篮球'
}
}
})
console.log(vm);
</script>
</body></html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<span>姓名:{{ name }}</span>
<input type="text" v-model="name"></input>
<span>更多:{{ more.like }}</span>
<input type="text" v-model="more.like">
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
name: '猿小野',
more: {
like: '打篮球'
}
}
})
console.log(vm);
</script>
</body>
</html>
js:
class Vue {
constructor(obj_instance) {
this.$data = obj_instance.data;
// 数据监听函数
Observer(this.$data);
// 模版解析函数
Compile(obj_instance.el, this)
}
}
// 数据劫持 - 监听实例里的数据
function Observer(data_instance) {
console.log(Object.keys(data_instance));
// 递归出口
if (!data_instance || typeof data_instance !== 'object') return;
const dependency = new Dependency();
Object.keys(data_instance).forEach(key => {
let value = data_instance[key];
Observer(value) // 递归 - 子属性数据劫持
Object.defineProperty(data_instance, key, {
enumerable: true,// 是否可以遍历/枚举
configurable: true,// 是否可再次修改/删除配置项
get() {
console.log(`访问了属性:${key} -> 值:${ value }`);
if (Dependency.temp) {
console.log(Dependency.temp);
}
// 订阅者加入依赖实例的数组
Dependency.temp && dependency.addSub(Dependency.temp);
return value;
},
set(newValue) {
console.log(`属性${key}的值${value}修改为 -> ${newValue}`);
value = newValue;
Observer(newValue);
dependency.notify();
}
})
})
}
// HTML模版解析 - 替换DOM
function Compile(element, vm) {
vm.$el = document.querySelector(element);
const fragment = document.createDocumentFragment();
let child;
while (child = vm.$el.firstChild) {
fragment.append(child);
}
console.log(fragment);
console.log(fragment.childNodes);
fragment_compile(fragment);
// 替换文档碎片内容
function fragment_compile(node) {
const pattern = /\{\{\s*(\S+)\s*\}\}/;
if (node.nodeType === 3) {
console.log(node);
console.log(node.nodeValue);
const xxx = node.nodeValue;
const result_regex = pattern.exec(node.nodeValue);
if (result_regex) {
console.log(node.nodeValue);
console.log(result_regex);
console.log(vm.$data[result_regex[1]]);
const arr = result_regex[1].split('.');
const value = arr.reduce((total, current) => total[current], vm.$data);
console.log(value);
node.nodeValue = xxx.replace(pattern, value);
console.log(node.nodeValue);
// 创建订阅者
new Watcher(vm, result_regex[1], newValue => {
node.nodeValue = xxx.replace(pattern, newValue);
})
}
return
}
if (node.nodeType === 1 && node.nodeName === 'INPUT') {
const attr = Array.from(node.attributes);
console.log(attr);
attr.forEach(i => {
if (i.nodeName === 'v-model') {
console.log(i.nodeValue);
const value = i.nodeValue.split('.').reduce((total, current) => total[current], vm.$data);
node.value = value;
new Watcher(vm, i.nodeValue, newValue => {
node.value = newValue;
});
node.addEventListener('input', e => {
// ['more', 'like']
const arr1 = i.nodeValue.split('.');
// ['more']
const arr2 = arr1.slice(0, arr1.length - 1);
// vm.$data.more
const final = arr2.reduce((total, current) => total[current], vm.$data);
// vm.$data.more['like'] = e.target.value
final[arr1[arr1.length - 1]] = e.target.value;
})
}
})
}
node.childNodes.forEach(child => fragment_compile(child));
}
vm.$el.appendChild(fragment);
}
// 依赖 - 收集和通知订阅者
class Dependency {
constructor() {
this.subscribers = [];
}
addSub(sub) {
this.subscribers.push(sub);
}
notify() {
this.subscribers.forEach(sub => sub.update());
}
}
// 订阅者
class Watcher {
constructor(vm, key, callback) {
this.vm = vm;
this.key = key;
this.callback = callback;
// 临时属性 - 触发getter
Dependency.temp = this;
console.log(`用属性${key}创建订阅者`);
key.split('.').reduce((total, current) => total[current], vm.$data);
Dependency.temp = null;
}
update() {
const value = this.key.split('.').reduce((total, current) => total[current], this.vm.$data);
this.callback(value);
}
}
class Vue {
constructor(obj_instance) {
this.$data = obj_instance.data;
// 数据监听函数
Observer(this.$data);
// 模版解析函数
Compile(obj_instance.el, this)
}
}
// 数据劫持 - 监听实例里的数据
function Observer(data_instance) {
console.log(Object.keys(data_instance));
// 递归出口
if (!data_instance || typeof data_instance !== 'object') return;
const dependency = new Dependency();
Object.keys(data_instance).forEach(key => {
let value = data_instance[key];
Observer(value) // 递归 - 子属性数据劫持
Object.defineProperty(data_instance, key, {
enumerable: true,// 是否可以遍历/枚举
configurable: true,// 是否可再次修改/删除配置项
get() {
console.log(`访问了属性:${key} -> 值:${ value }`);
if (Dependency.temp) {
console.log(Dependency.temp);
}
// 订阅者加入依赖实例的数组
Dependency.temp && dependency.addSub(Dependency.temp);
return value;
},
set(newValue) {
console.log(`属性${key}的值${value}修改为 -> ${newValue}`);
value = newValue;
Observer(newValue);
dependency.notify();
}
})
})
}
// HTML模版解析 - 替换DOM
function Compile(element, vm) {
vm.$el = document.querySelector(element);
const fragment = document.createDocumentFragment();
let child;
while (child = vm.$el.firstChild) {
fragment.append(child);
}
console.log(fragment);
console.log(fragment.childNodes);
fragment_compile(fragment);
// 替换文档碎片内容
function fragment_compile(node) {
const pattern = /\{\{\s*(\S+)\s*\}\}/;
if (node.nodeType === 3) {
console.log(node);
console.log(node.nodeValue);
const xxx = node.nodeValue;
const result_regex = pattern.exec(node.nodeValue);
if (result_regex) {
console.log(node.nodeValue);
console.log(result_regex);
console.log(vm.$data[result_regex[1]]);
const arr = result_regex[1].split('.');
const value = arr.reduce((total, current) => total[current], vm.$data);
console.log(value);
node.nodeValue = xxx.replace(pattern, value);
console.log(node.nodeValue);
// 创建订阅者
new Watcher(vm, result_regex[1], newValue => {
node.nodeValue = xxx.replace(pattern, newValue);
})
}
return
}
if (node.nodeType === 1 && node.nodeName === 'INPUT') {
const attr = Array.from(node.attributes);
console.log(attr);
attr.forEach(i => {
if (i.nodeName === 'v-model') {
console.log(i.nodeValue);
const value = i.nodeValue.split('.').reduce((total, current) => total[current], vm.$data);
node.value = value;
new Watcher(vm, i.nodeValue, newValue => {
node.value = newValue;
});
node.addEventListener('input', e => {
// ['more', 'like']
const arr1 = i.nodeValue.split('.');
// ['more']
const arr2 = arr1.slice(0, arr1.length - 1);
// vm.$data.more
const final = arr2.reduce((total, current) => total[current], vm.$data);
// vm.$data.more['like'] = e.target.value
final[arr1[arr1.length - 1]] = e.target.value;
})
}
})
}
node.childNodes.forEach(child => fragment_compile(child));
}
vm.$el.appendChild(fragment);
}
// 依赖 - 收集和通知订阅者
class Dependency {
constructor() {
this.subscribers = [];
}
addSub(sub) {
this.subscribers.push(sub);
}
notify() {
this.subscribers.forEach(sub => sub.update());
}
}
// 订阅者
class Watcher {
constructor(vm, key, callback) {
this.vm = vm;
this.key = key;
this.callback = callback;
// 临时属性 - 触发getter
Dependency.temp = this;
console.log(`用属性${key}创建订阅者`);
key.split('.').reduce((total, current) => total[current], vm.$data);
Dependency.temp = null;
}
update() {
const value = this.key.split('.').reduce((total, current) => total[current], this.vm.$data);
this.callback(value);
}
}
如果帮助到您了,可以留下一个赞👍告诉我

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)