첫째, 원칙
당신이 Object.defineProperty를 사용하는 경우, 간단한 양방향 바인딩, 실제로는 단지 다음과 같은 매우 간단 달성하기 위해 :
1
이
삼
4
5
6
(7)
8
9
|
이 뷰 = {};
Object.defineProperty (뷰, '$ 데이터', {
세트 (발) {
document.getElementById를 ( '충격 항목'). innerText와 = 브로
}
});
document.addEventListener ( '의 keyup', 기능 (E) {
뷰. $ 데이터 = e.target.value
})
|
이 데모는 VUE 간단한 방법 바인딩 원리 이상입니다.
둘째, 요소들을 대체
우리는 규칙 VUE를 사용할 때 생각해
1
이
삼
4
5
6
|
새로운 뷰 ({
'# 응용 프로그램'
데이터:{
텍스트 : '안녕하세요'
}
});
|
페이지 구조에 글 :
1
이
삼
4
|
<DIV의 ID = '응용 프로그램'>
<input 타입 = '문자'V 모델 = '문자'>
텍스트 (markedown는 텍스트를 가정 한하지 '대괄호'내부)
</ DIV>
|
우리 뷰는이 값을 전달, 생성자로 추상화
1
이
삼
4
5
|
{뷰 (옵션) 기능
this.data = options.data;
this.id = options.el;
getAllNode(document.getElementById(this.id), this);
};
|
替换掉节点中所有的双中括号里的内容:
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
|
function compile (node, vm){
var reg = /{{(.*)}}/;//匹配‘双中括号’
// 节点类型为元素
if (node.nodeType === 1) {
var attr = node.attributes;
// 解析属性
for (var i = 0; i < attr.length; i++){
if(attr[i].nodeName === 'v-model'){
var name = attr[i].nodeValue;// 获取绑定的属性的名字
node.value = vm.data[name];// 替换值
node.removeAttribute('v-model'); //移除v-model
}
};
}
// 节点类型为text
if(node.nodeType === 3) {
if(reg.test(node.nodeValue)) {
var name = RegExp.$1; //匹配到的第一个字符
name = name.trim();
node.nodeValue = vm.data[name]; // 将data的值赋值给node
}
}
};
function getAllNode(node, vm){
var length = node.childNodes.length;
for(var i = 0; i < length; i++){
compile(node.childNodes[i], vm)
}
};
|
这样就可以成功替换掉双中括号的内容:
三、绑定元素
上面我们只是替换了元素,但还没有实现绑定
实现数据绑定,就要用到definedProperty的set和get方法:
首先我们要给vue的所有属性都添加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
|
function Vue(){
// *****
observe(data,this)
}
// 遍历
function observe (obj, vm){
for(var key in obj){
active(vm, key, obj[key]);
}
}
// 添加set和get
function active (obj, key, val) {
Object.defineProperty(obj, key, {
get(){
return val;
},
set(newVal){
if (newVal === val){ return };
val = newVal;
}
});
}
|
再来明确我们要做的事,获取输入的值,改变Vue中相应的data的值,同时改变‘双中括号’中的值;
我们已经给data的每个属性都添加了get和set的方法,现在要做的就是如何触发它们。
触发它肯定是在赋值的时候,所以我们在有v-model属性的节点监听输入事件,同时赋值,触发set事件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
function compile (node, vm){
// ***********
if (node.nodeType === 1) {
var attr = node.attributes;
// 解析属性
for (var i = 0; i < attr.length; i++){
if(attr[i].nodeName === 'v-model'){
var name = attr[i].nodeValue;// 获取绑定的属性的名字
// 监听input事件
node.addEventListener('input', function(e){
// 给相应的data属性赋值,触发set
vm.data[name] = e.target.value
})
node.value = vm.data[name];// 替换输入框的值为data中的值
node.removeAttribute('v-model');
}
};
}
// ************
}
|
我们监听了input事件,接下来要获取输入的值并同步改变文本;
我们肯定希望只希望哪里改变了就对哪里做处理就行了,所以我们引入一个简单的发布——订阅组件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function pubsub(){
this.subs = []
}
pubsub.prototype = {
addSub: function(sub){
this.subs.push(sub);
},
pub: function(){
this.subs.forEach(function(sub){
sub.update();
})
}
}
|
在添加set和get的同时订阅事件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
function active (obj, key, val) {
var pubsub = new pubsub();
Object.defineProperty(obj.data, key, {
get(){
// 添加订阅
if(Pubsub.target){
pubsub.addSub(Pubsub.target);
}
return val;
},
set(newVal){
if (newVal === val){ return };
val = newVal;
// 发出通知
pubsub.pub();
}
});
}
|
添加一个方法,来在pubsub发出通知时处理事件,我们命名为watcher
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
|
function Watcher(vm, node, name){
Pubsub.target = this;
this.name = name;
this.node = node;
this.vm = vm;
this.update();
Pubsub.target = null
}
Watcher.prototype = {
update(){
this.get();
this.node.nodeValue = this.value
},
// 获取data中的属性值
get(){
this.value = this.vm[this.name] // 触发相应的get
}
}
function getAllNode(node, vm){
var length = node.childNodes.length;
for(var i = 0; i < length; i++){
compile(node.childNodes[i], vm)
}
};
|
这个watcher我们在什么时候添加呢?当然是在一开始的时候(compile里):
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
|
function compile (node, vm){
var reg = /{{(.*)}}/;// 匹配‘双中括号’内任意字符
// 节点类型为元素
if (node.nodeType === 1) {
var attr = node.attributes;
// 解析属性
for (var i = 0; i < attr.length; i++){
if(attr[i].nodeName === 'v-model'){
var name = attr[i].nodeValue;// 获取绑定的属性的名字
node.addEventListener('input', function(e){
// 给相应的data属性赋值,触发set
vm.data[name] = e.target.value
})
node.value = vm.data[name];// 替换输入框的值为data中的值
node.removeAttribute ( 'V 모델');
}
};
};
// 노드 유형은 텍스트입니다
경우 (node.nodeType의 === 3) {
경우 (reg.test (node.nodeValue)) {
첫 번째 문자에 일치하는 VAR 이름 = 정규식 $ (1) ;. //
이름 = name.trim ();
// node.nodeValue = VM [이름];
// 관측 값 입력 신규 관찰자 (VM, 노드 이름)
}
}
};
|
이 시점에서, 데이터 바인딩의 전체 프로세스를 모의.
IV 요약
마지막으로, 과정 전반에 걸쳐 아이디어를 명확히
뷰 만들기 :
입력 이벤트 :
GitHub의 저장소에서 내 소스 : https://github.com/lastnigtic/vue-bindData
원본 : 대형 열 바인딩 시뮬레이션 뷰 양방향 데이터