话接上文,之前的data不够纯粹,需要用户写set和get,现在改成如下
在线访问
在线访问
数据
复制代码
1
2
3
4
5
6
7
8
9
10
11var 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,那我们就需要对他在封装下,例如:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45//双向绑定,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优化
新值和旧值一样时候,不做任何处理
复制代码
1
2
3
4
5
6
7
8
9set(newValue) { //如果设置的值一样,不做任何处理 if (state[`_${key}`] === newValue) { return; } state[`_${key}`] = newValue; //用下划线开头封装起来,不直接设置 notice(key); noticeDeps[key] && noticeDeps[key].forEach(item => notice(item)); //通知依赖本数据的对象更新 },
类vue的watch 函数:
复制代码
1
2
3
4
5
6
7
8
9
10
11//通知html更新 var notice = function(name) { let list = Deps[name]; if (Array.isArray(list) && list.length) { console.warn("set", name) list.forEach(item => { item(); }) } }
完成
完成所有内容,相比之前,数据变得更加简单,同时抽离数据包装函数和notice通知更新函数
所有代码
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132<!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========================
封装
为了让它更像一个库,封装如下:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96(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);
调用
复制代码
1
2
3
4
5
6
7var state = { wrapInView: false, labelInView: false }; var vm = MVVM(state);
修改数据
复制代码
1vm.wrapInView=false
最后
以上就是刻苦糖豆最近收集整理的关于简单双向绑定(二)的全部内容,更多相关简单双向绑定(二)内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复