학습 노트 - 데이터 양방향 귀속
29339 단어 ***
이것은 제가 vue를 배운 노트입니다. 오류가 있으면 지적해 주십시오.
1 MVVM
양방향 데이터 귀속은 대상 속성 변화와 보기의 변화를 서로 귀속시키는 것을 말한다.다시 말하면name 속성을 가진user 대상이 있으면 요소의 내용과 연결되어 user에게 주어집니다.name에 새 값을 부여하면 페이지 요소 노드도 해당하는 데이터를 표시합니다.마찬가지로, 만약 페이지 요소 (보통 input) 의 데이터가 바뀌면, 새로운 값을 입력하면user 대상의name 속성에 변화가 발생할 수 있습니다.
MVVM은 마이크로소프트가 최초로 제기한 것으로 데스크톱 응용 프로그램의 MVC 사상을 참고했다. 앞쪽 페이지에서 모델을순수한JavaScript 대상으로 표시하고View가 디스플레이를 책임지며 둘을 최대한 분리했다.Model과 View를 연결하는 것이 ViewModel입니다.ViewModel은 모드의 데이터를 View에 동기화하고 View의 수정을 모드에 동기화합니다.
한 마디로 하면 데이터와 표현은 분리된다. 어떤 데이터가 바뀌면 페이지에서 이 데이터를 사용하는 모든 요소의 내용이 바뀐다.다음은 가장 간단한 데이터 귀속의 예이다. Vue2.0 원본 읽기 노트 - 양방향 귀속 실현 원리, 이 예는 매우 간단하고 거칠다. 세 가지 일을 했다.
<head>head>
<body>
<div id="app">
<input type="text" id="a">
<span id="b">span>
div>
<script type="text/javascript">
var obj = {};
Object.defineProperty(obj, 'hello', {
get: function() {
console.log('get val:'+ val);
return val;
},
set: function(newVal) {
val = newVal;
console.log('set val:'+ val);
document.getElementById('a').value = val;
document.getElementById('b').innerHTML = val;
}
});
document.addEventListener('keyup', function(e) {
obj.hello = e.target.value;
});
script>
body>
html>
1.1 데이터의 양방향 연결을 실현하는 방식
쌍방향 데이터가 밑바닥을 연결하는 사상은 매우 기본적이며 세 단계로 압축될 수 있다.
게시자 - 구독자 모드의 사상은 매우 간단하다. 사용자 정의 데이터 속성을 사용하여 HTML 코드에서 귀속을 가리킨다.바인딩된 모든 JavaScript 객체와 DOM 요소는 게시자 객체에 가입됩니다.언제든지 귀속된 내용 (예를 들어 자바스크립트 대상이나 HTML 입력 필드) 이 변화를 탐지하면, 게시자 - 구독자 모드로 에이전트를 전송합니다. 이것은 모든 귀속된 대상과 요소에 변화를 방송하고 전파합니다.다음은 자바스크립트의 양방향 데이터 연결을 이야기하는 예입니다. 주석에 제 이해를 추가했습니다.
function DataBinder(object_id){
// PubSub
var pubSub = { // pubSub , callbacks ,
callbacks: {}, // , ,
on: function(msg,callback){ // on , ( ),
this.callbacks[msg] = this.callbacks[msg] || []; // msg , ( , )
this.callbacks[msg].push(callback); //
},
publish: function(msg){ // publish
this.callbacks[msg] = this.callbacks[msg] || []; // msg , this.callbacks msg , ,
for(var i = 0, len = this.callbacks[msg].length; i// msg
this.callbacks[msg][i].apply(this,arguments); // , this publish , publish
}
}
},
data_attr = "data-bind-" + object_id, // , , “data-bind” , ,
message = object_id + ":change", // , , “:change” , ,
changeHandler = function(evt){ // ,
var target = evt.target || evt.srcElemnt, //IE8
prop_name = target.getAttribute(data_attr); // data_attr
if(prop_name && prop_name !== ""){ // ,
pubSub.publish(message,prop_name,target.value); // message , message , , this publish , publish (publish apply)
}
};
// PubSub
if(document.addEventListener){ // , changeHandler
document.addEventListener("change",changeHandler,false);
}else{
//IE8 attachEvent addEventListener
document.attachEvent("onchange",changeHandler);
}
//PubSub
pubSub.on(message,function(vet,prop_name,new_val){ // pubSub.on , message , ,message , ,
var elements = document.querySelectorAll("[" + data_attr + "=" + prop_name + "]"), // ,
tah_name;
for(var i = 0,len =elements.length; i < len; i++){ //
tag_name = elements[i].tagName.toLowerCase();
if(tag_name === "input" || tag_name === "textarea" || tag_name === "select"){ // ,
elements[i].value = new_val;
}else{
elements[i].innerHTML = new_val;
}
}
});
return pubSub;
}
// model
function User(uid){
var binder = new DataBinder(uid), // pubSub , uid
user = {
attributes: {}, //
set: function(attr_name,val){ // set , publish
this.attributes[attr_name] = val;
// “publish”
binder.publish(uid+ ":change", attr_name, val,this);
},
get: function(attr_name){
return this.attributes[attr_name];
}
}
return user; // , ,
}
var user = new User(123); // user , attributes ,
user.set("name","Wolfgang"); // data-bind-123="name" html , ,
그리고 더러운 검사, 더러운 검사는 당신이 어떻게 변화하는지, 언제 변화하는지에 무관심하고 특정한 검사 단계에서 데이터가 바뀌었는지에 대한 데이터 감청 기술이다.간단하게 말하자면, 더러운 검사는 데이터가 바뀌었는지 직접 검사하는 것이다. 만약 감청된 데이터가 바뀌면 이 값을 모든 피감청자에게 전달한다.
데이터 납치는 속성의 setget 방법을 개조하여 데이터의 변화를 감시하고 구독자에게 메시지를 발표하여 해당하는 감청 리셋을 촉발하는 것이다.
2 vue 데이터 양방향 바인딩
vue는 데이터 납치 방식을 통해 데이터 연결을 하는 것으로 알려졌는데 그 중에서 가장 핵심적인 방법은 Object를 통해 연결하는 것이다.defineProperty () 는 속성에 대한 납치를 실현하고 데이터의 변동을 감청하는 목적을 달성합니다.
mvvm의 양방향 귀속을 실현하려면 주로 다음과 같이 진행되었다.
이해하기 쉽도록 먼저 하나의 메시지의 저장 중전의 구조 함수를 실현한다.
var uid = 0; // uid Dep id,
function Dep() {
this.id = uid++; // Dep id, uid 1
this.subs = [];
}
Dep.prototype = {
addSub: function(sub) { // sub
this.subs.push(sub);
},
depend: function() {
Dep.target.addDep(this); // Dep target ( this) subs
},
removeSub: function(sub) { // sub
var index = this.subs.indexOf(sub);
if (index != -1) {
this.subs.splice(index, 1);
}
},
notify: function() { // subs
this.subs.forEach(function(sub) {
sub.update();
});
}
};
객체의 등록 정보를 수정하면 바인딩된 각 등록 정보마다 Dep 인스턴스가 있습니다.모든 Dep 실례에는 알림이 필요한 대상을 저장하기 위해 subs 속성이 있습니다. 대상의 속성이 바뀔 때 set 방법을 통해 이 속성의 Dep 실례의 원형인 notify 방법을 호출하고 subs 그룹에 저장된 내용에 따라 이 속성 값을 귀속시킨 수정된 내용을 알립니다.
function Observer(data) {
this.data = data;
this.walk(data); // ,
}
Observer.prototype = {
walk: function(data) {
var me = this;
Object.keys(data).forEach(function(key) { // data , get / set
me.convert(key, data[key]);
});
},
convert: function(key, val) {
this.defineReactive(this.data, key, val);
},
defineReactive: function(data, key, val) { //
var dep = new Dep();
var childObj = observe(val);
Object.defineProperty(data, key, {
enumerable: true, //
configurable: false, // define
get: function() {
if (Dep.target) {
dep.depend(); // Dep.target dep subs
}
return val;
},
set: function(newVal) {
if (newVal === val) {
return;
}
val = newVal;
// object ,
childObj = observe(newVal);
//
dep.notify();
}
});
}
};
function observe(value, vm) {
if (!value || typeof value !== 'object') {
return;
}
return new Observer(value);
};
그리고 html 템플릿을 컴파일하여 각 노드와 그 속성에 따라'{{}}}','v-','on'등 특수 문자열이 포함되어 있는지 판단하고 귀속되었는지 판단하며 귀속된 속성 개get set을 처리한다.
function Compile(el, vm) {
this.$vm = vm;
this.$el = this.isElementNode(el) ? el : document.querySelector(el);
if (this.$el) {
this.$fragment = this.node2Fragment(this.$el);
this.init();
this.$el.appendChild(this.$fragment);
}
}
Compile.prototype = {
node2Fragment: function(el) {
var fragment = document.createDocumentFragment(),
child;
// fragment
while (child = el.firstChild) { // el , child, true
fragment.appendChild(child); // child el fragment ,el ,
}
return fragment; // fragment
},
init: function() {
this.compileElement(this.$fragment); // fragment
},
compileElement: function(el) {
var childNodes = el.childNodes,
me = this;
[].slice.call(childNodes).forEach(function(node) { // ,
var text = node.textContent;
var reg = /\{\{(.*)\}\}/;
if (me.isElementNode(node)) {
me.compile(node); //
} else if (me.isTextNode(node) && reg.test(text)) { //
me.compileText(node, RegExp.$1);
}
if (node.childNodes && node.childNodes.length) {
me.compileElement(node); // ,
}
});
},
compile: function(node) {
var nodeAttrs = node.attributes, // dom html
me = this;
[].slice.call(nodeAttrs).forEach(function(attr) { // ,
var attrName = attr.name;
if (me.isDirective(attrName)) { // , ,
var exp = attr.value;
var dir = attrName.substring(2);
//
if (me.isEventDirective(dir)) {
compileUtil.eventHandler(node, me.$vm, exp, dir);
//
} else {
compileUtil[dir] && compileUtil[dir](node, me.$vm, exp);
}
node.removeAttribute(attrName); //
}
});
},
compileText: function(node, exp) {
compileUtil.text(node, this.$vm, exp);
},
isDirective: function(attr) {
return attr.indexOf('v-') == 0;
},
isEventDirective: function(dir) {
return dir.indexOf('on') === 0;
},
isElementNode: function(node) { //
return node.nodeType == 1;
},
isTextNode: function(node) { //
return node.nodeType == 3;
}
}
마지막으로watch를 실현하고 속성의 변화를 감시한다.watch의 모든 실례는 감청하고자 하는 속성의 dep.subs 그룹에 추가됩니다. 감청하는 데이터에 변화가 발생하면 notify 함수를 호출하고 함수 내부에서subs를 호출하기 때문에watch 실례의 updata 방법으로 이 데이터를 감청하는 대상을 알립니다.알림을 받은 후 대상은 값이 바뀌었는지 판단합니다. 바뀌면 리셋 함수를 호출하고 보기를 변경합니다.
function Watcher(vm, exp, cb) {
this.cb = cb;
this.vm = vm;
this.exp = exp;
// getter, dep , Observer
this.value = this.get();
}
Watcher.prototype = {
update: function() {
this.run(); //
},
run: function() {
var value = this.get(); //
var oldVal = this.value;
if (value !== oldVal) { //
this.value = value;
this.cb.call(this.vm, value, oldVal); // Compile ,
}
},
get: function() {
Dep.target = this; //
var value = this.vm[exp]; // getter,
Dep.target = null; // ,
return value;
}
};
마지막으로 MVVM 구조기를 통해 위와 부분을 통합하여 데이터 귀속을 실현한다.
function MVVM(options) {
this.$options = options;
var data = this._data = this.$options.data;
observe(data, this);
this.$compile = new Compile(options.el || document.body, this)
}
위의 내용은 데이터 귀속을 실현하는 대략적인 사고방식일 뿐이고 다른 내용은 내가 천천히 보완할 것이다.
3 vue 데이터 양방향 귀속 결함
3.1 vue 인스턴스가 생성된 후 속성을 추가하여 수신할 수 없음
Vue 인스턴스를 만들면 모든 DOM 객체가 스트리밍되고 각 데이터 속성에 get과 set이 추가됩니다.get과 set을 사용하면 Vue에서 데이터의 변경 사항을 살펴보고 업데이트를 트리거할 수 있습니다.단, Vue 실례화 후에 속성을 추가하거나 삭제하면, 이 속성은 vue에서 처리되지 않고 get과 set을 변경합니다.
새 대상을 만들고 싶지 않으면 Vue를 사용할 수 있습니다.set 새 대상 속성을 설정합니다.이 메서드는 속성을 응답 속성으로 만들고 뷰 업데이트를 트리거합니다.
function addToCart (id) {
var item = this.cart.findById(id);
if (item) {
item.qty++
} else {
// , item.qty = 1
// Vue.set
Vue.set(item, 'qty', 1)
this.cart.push(item)
}
}
addToCart(myProduct.id);
3.2 배열
Object.defineProperty의 결함 중 하나는 그룹의 변화를 감청할 수 없다는 것입니다.
인덱스(index)를 사용하여 배열 항목을 직접 설정할 때 vue에서 감지되지 않습니다.
app.myArray[index] = newVal;
그러나 Vue의 문서에서 Vue는 수조의 변화를 감지할 수 있지만 다음과 같은 8가지 방법만 있다
vm.items[indexOfItem] = newValue
이런 것은 감지할 수 없다.push();
pop();
shift();
unshift();
splice();
sort();
reverse();
마찬가지로 Vue를 사용할 수 있습니다.set을 사용하여 배열 항목을 설정합니다.
Vue.set(app.myArray, index, newVal);
3.3 proxy와 defineproperty
Proxy 객체는 ES2015 사양에 정식으로 게시되어 속성 검색, 값 지정, 열거, 함수 호출 등의 기본 작업에 대한 사용자 정의 동작을 정의하는 데 사용됩니다.
이것은 목표 대상에 앞서'차단'을 가설하고 외부에서 이 대상에 대한 방문을 반드시 이 층을 통해 차단해야 하기 때문에 외부의 방문을 필터하고 고칠 수 있는 메커니즘을 제공했다.
Proxy는 Object입니다.defineProperty의 전방위적인 강화판으로 구체적인 문서는 이곳을 볼 수 있습니다.
Proxy에는 13가지 차단 방법이 있는데
apply、ownKeys、deleteProperty、has
등에 국한되지 않는 Object입니다.defineProperty에 없는Proxy는 새로운 대상을 되돌려줍니다. 우리는 새로운 대상만 조작해서 목적을 달성할 수 있습니다. Object.defineProperty는 객체 속성만 직접 수정할 수 있습니다.Proxy는 새로운 표준으로서 브라우저 업체의 중점적인 지속적인 성능 최적화, 즉 전설의 새로운 표준의 성능 배당금을 받을 것이다.물론 Proxy의 열세는 호환성 문제이고polyfill로 닦을 수 없기 때문에 Vue의 저자는 다음 버전(3.0)을 기다려야 Proxy로 다시 쓸 수 있다고 밝혔다.참조 자료:
MVVM(류설봉의 공식 사이트)은 자바스크립트의 양방향 데이터 귀속 더러움 검사와 그 장점에 대해 이야기한다.0 원본 읽기 노트 - 양방향 귀속 실현 원리 분석 Vue 원리 & 양방향 귀속 MVVM mdn Object 실현.defineProperty()Vue 응답식 및 결함 면접관: 쌍방향 귀속Proxy를 실현하는 것이 defineproperty보다 우열이 어떻습니까?MDN Proxy
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
빠른 정렬python 구현: 귀속과 비귀속텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.