概述
话接上文,之前的data不够纯粹,需要用户写set和get,现在改成如下
在线访问
在线访问
数据
var data = {
age: "10",
name: "你好",
person: [function() {
console.warn("this.age", this.age)
return `${this.age} _ ${this.name}`
},
['age', 'name']
]
}
如果是computed这样的合成输入,让他接受一个两个参数的数组,
- 第一个是函数
- 第二个是它依赖的变化参数
这样的数据并不是特别干净,还好简洁明了,就这样吧
为什么vue的不需要手动写依赖呢?
因为vue是直接触发所有dep的回调,而不是只触发特定的,这会导致性能问题,
也是为什么它需要虚拟DOM的原因之一。
注意:person里面第一个参数【函数】不能使用箭头函数,因为里面的this需要指向执行环境的this
数据工厂函数
既然数据不再包含set和get,那我们就需要对他在封装下,例如:
//双向绑定,state封装,收集依赖
var stateFactory = function(obj) {
let state = {};
let keys = Object.keys(obj);
let noticeDeps = {}; //计算依赖收集,实现类似vue computed这样的方法
keys.forEach(key => {
//===============赋值【
let val = obj[key];
if (Array.isArray(obj[key]) && typeof obj[key][0] == "function") {
console.warn("obj[key][0]", obj[key][0])
val = obj[key][0];
}
state[`_${key}`] = val; //用下划线开头封装起来,而不是直接访问
//==============赋值】
//===============计算依赖收集【
//这里是反向操作,解析依赖其他数据的属性,给【被依赖】的属性添加依赖自己的‘属性名字’
//被依赖的数据更新时候,通知依赖本数据的数据触发更新dom操作
if (Array.isArray(obj[key]) && Array.isArray(obj[key][1])) {
obj[key][1].forEach(depKey => {
if (!noticeDeps[depKey]) {
noticeDeps[depKey] = [];
}
noticeDeps[depKey].push(key);
})
console.warn("通知队列", noticeDeps)
}
//======================计算依赖收集】
});
//拼凑数据,添加set和get
keys.forEach(key => {
Object.defineProperty(state, `${key}`, {
get() {
return state[`_${key}`];
},
set(newValue) {
state[`_${key}`] = newValue; //用下划线开头封装起来,不直接设置
notice(key);
noticeDeps[key] && noticeDeps[key].forEach(item => notice(item)); //通知依赖本数据的对象更新
},
enumerable: true,
configurable: true
});
})
return state;
}
set优化
新值和旧值一样时候,不做任何处理
set(newValue) {
//如果设置的值一样,不做任何处理
if (state[`_${key}`] === newValue) {
return;
}
state[`_${key}`] = newValue; //用下划线开头封装起来,不直接设置
notice(key);
noticeDeps[key] && noticeDeps[key].forEach(item => notice(item)); //通知依赖本数据的对象更新
},
类vue的watch 函数:
//通知html更新
var notice = function(name) {
let list = Deps[name];
if (Array.isArray(list) && list.length) {
console.warn("set", name)
list.forEach(item => {
item();
})
}
}
完成
完成所有内容,相比之前,数据变得更加简单,同时抽离数据包装函数和notice通知更新函数
所有代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>简单数据绑定</title>
</head>
<body>
<div>年龄:<span v-data="age">--</span></div>
<div>名字:<span v-data="name"></span></div>
<div>计算依赖:<span v-data="person">--</span></div>
<div>
<input v-data="age" type="number" placeholder="修改年龄" onchange="saveAge.bind(this)()">
</div>
<div>
<input type="text" v-data="name" placeholder="修改名字" oninput="saveName.bind(this)()">
</div>
<script>
//html对数据的依赖收集
var Deps = {}
//通知html更新
var notice = function(name) {
let list = Deps[name];
if (Array.isArray(list) && list.length) {
console.warn("set", name)
list.forEach(item => {
item();
})
}
}
//双向绑定,state封装,收集依赖
var stateFactory = function(obj) {
let state = {};
let keys = Object.keys(obj);
let noticeDeps = {}; //计算依赖收集,实现类似vue computed这样的方法
keys.forEach(key => {
//===============赋值【
let val = obj[key];
if (Array.isArray(obj[key]) && typeof obj[key][0] == "function") {
console.warn("obj[key][0]", obj[key][0])
val = obj[key][0];
}
state[`_${key}`] = val; //用下划线开头封装起来,而不是直接访问
//==============赋值】
//===============计算依赖收集【
//这里是反向操作,解析依赖其他数据的属性,给【被依赖】的属性添加依赖自己的‘属性名字’
//被依赖的数据更新时候,通知依赖本数据的数据触发更新dom操作
if (Array.isArray(obj[key]) && Array.isArray(obj[key][1])) {
obj[key][1].forEach(depKey => {
if (!noticeDeps[depKey]) {
noticeDeps[depKey] = [];
}
noticeDeps[depKey].push(key);
})
console.warn("通知队列", noticeDeps)
}
//======================计算依赖收集】
});
//拼凑数据,添加set和get
keys.forEach(key => {
Object.defineProperty(state, `${key}`, {
get() {
return state[`_${key}`];
},
set(newValue) {
//如果设置的值一样,不做任何处理
if (state[`_${key}`] === newValue) {
return;
}
state[`_${key}`] = newValue; //用下划线开头封装起来,不直接设置
notice(key);
noticeDeps[key] && noticeDeps[key].forEach(item => notice(item)); //通知依赖本数据的对象更新
},
enumerable: true,
configurable: true
});
})
return state;
}
var data = {
age: "10",
name: "你好",
person: [function() {
console.warn("this.age", this.age)
return `${this.age} _ ${this.name}`
},
['age', 'name']
]
}
var test = stateFactory(data);
console.warn("封装后的state", test)
console.warn("封装后的Dep", Deps)
function parse() {
var doms = document.querySelectorAll("[v-data]")
for (let i = 0; i <= doms.length - 1; i++) {
let tagName = doms[i].tagName.toUpperCase();
let data = doms[i].getAttribute("v-data");
if (!Deps[data]) {
Deps[data] = [];
}
var func = function() {
let val = test[data];
if (typeof test[data] == "function") {
val = test[data]();
console.log("person inner", val)
}
if (tagName == 'INPUT') {
doms[i].value = val;
} else {
doms[i].innerHTML = val;
}
};
Deps[data].push(func);
func()
}
console.warn("Deps", Deps)
}
//解析html,并收集依赖
parse();
//input的change事件回调函数
function saveAge() {
console.log("age this.value", this, this.value)
test.age = this.value;
}
function saveName() {
console.log("name this.value", this, this.value)
test.name = this.value;
}
</script>
</body>
</html>
更新 2022-2-25========================
封装
为了让它更像一个库,封装如下:
(function(window) {
//html对数据的依赖收集
var Deps = {};
//通知html更新
var notice = function(name) {
let list = Deps[name];
if (Array.isArray(list) && list.length) {
console.warn("set", name);
list.forEach((item) => {
item();
});
}
};
//双向绑定,state封装,收集依赖
var stateFactory = function(obj) {
let state = {};
let keys = Object.keys(obj);
let noticeDeps = {}; //计算依赖收集,实现类似vue computed这样的方法
keys.forEach((key) => {
//===============赋值【
let val = obj[key];
if (Array.isArray(obj[key]) && typeof obj[key][0] == "function") {
console.warn("obj[key][0]", obj[key][0]);
val = obj[key][0];
}
state[`_${key}`] = val; //用下划线开头封装起来,而不是直接访问
//==============赋值】
//===============计算依赖收集【
//这里是反向操作,解析依赖其他数据的属性,给【被依赖】的属性添加依赖自己的‘属性名字’
//被依赖的数据更新时候,通知依赖本数据的数据触发更新dom操作
if (Array.isArray(obj[key]) && Array.isArray(obj[key][1])) {
obj[key][1].forEach((depKey) => {
if (!noticeDeps[depKey]) {
noticeDeps[depKey] = [];
}
noticeDeps[depKey].push(key);
});
console.warn("通知队列", noticeDeps);
}
//======================计算依赖收集】
});
//拼凑数据,添加set和get
keys.forEach((key) => {
Object.defineProperty(state, `${key}`, {
get() {
return state[`_${key}`];
},
set(newValue) {
//如果设置的值一样,不做任何处理
if (state[`_${key}`] === newValue) {
return;
}
state[`_${key}`] = newValue; //用下划线开头封装起来,不直接设置
notice(key);
noticeDeps[key] && noticeDeps[key].forEach((item) => notice(item)); //通知依赖本数据的对象更新
},
enumerable: true,
configurable: true,
});
});
return state;
};
window.MVVM = function MVVM(data) {
var test = stateFactory(data);
console.warn("封装后的state", test);
console.warn("封装后的Dep", Deps);
function parse() {
var doms = document.querySelectorAll("[v-data]");
for (let i = 0; i <= doms.length - 1; i++) {
let tagName = doms[i].tagName.toUpperCase();
let data = doms[i].getAttribute("v-data");
if (!Deps[data]) {
Deps[data] = [];
}
var func = function() {
let val = test[data];
if (typeof test[data] == "function") {
val = test[data]();
console.log("person inner", val);
}
if (tagName == "INPUT") {
doms[i].value = val;
} else {
doms[i].innerHTML = val;
}
};
Deps[data].push(func);
func();
}
console.warn("Deps", Deps);
}
//解析html,并收集依赖
parse();
return test;
};
})(window);
调用
var state = {
wrapInView: false,
labelInView: false
};
var vm = MVVM(state);
修改数据
vm.wrapInView=false
最后
以上就是刻苦糖豆为你收集整理的简单双向绑定(二)的全部内容,希望文章能够帮你解决简单双向绑定(二)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复