公開購読から双方向データバインディングへ
6135 ワード
前言
双方向データーバインディングはすでに話題になっています.原理について言えば、みんながデータハイジャックに言及できると思います.しかし、どのように完全に双方向データバインディングの疑似コードを実現するかについては、多くの人が深く研究していないと思います.そこで、本稿では整理したリリースによる購読モードを利用して、浅いものから深いものまで双方向データバインディングを実現する.
購読モードを公開
双方向データバインディングの下の設計モードは、購読モードのリリースです.による購読モードの発表は、観察者モード とも呼ばれる.観察者は、オブジェクトが変更された場合、各観察者に通知 を受信することができます.
通俗的に例を引く
一番ポピュラーなものを挙げると、紅ちゃん、明ちゃん、シロさんがたくさん並んでいるAJ 1に注目します.
EventEmitter
これにより、EventEmitterの疑似コードも実現できます.
下記の例
注釈しないでください.みんなが読めます.
1、極簡略版
まず整理して何をするべきですか?追加対象ハイジャック 購読者管理センターを追加し、新規購読者と購読者に通知して を更新します.は、
(未完は継続します.ProxyのMVM実現方法を更新します.)
双方向データーバインディングはすでに話題になっています.原理について言えば、みんながデータハイジャックに言及できると思います.しかし、どのように完全に双方向データバインディングの疑似コードを実現するかについては、多くの人が深く研究していないと思います.そこで、本稿では整理したリリースによる購読モードを利用して、浅いものから深いものまで双方向データバインディングを実現する.
購読モードを公開
双方向データバインディングの下の設計モードは、購読モードのリリースです.
通俗的に例を引く
一番ポピュラーなものを挙げると、紅ちゃん、明ちゃん、シロさんがたくさん並んでいるAJ 1に注目します.
class Pinduoduo {
constructor() {
//
this.subscribers = [];
}
//
subscribe({name, callback}) {
if (~this.subscribers.indexOf(name)) return;
this.subscribers.push({
name, callback
});
}
//
publish() {
this.subscribers.forEach(({name, callback}) => {
let prize = 666;
if (name === ' ') prize = 100;
callback && callback(name, prize);
})
}
}
const pinInstance = new Pinduoduo();
const commonFn = (name, prize) => {
console.log(`${name} ,AJ1 ${prize}`)
}
//
pinInstance.subscribe({
name: ' ',
callback: commonFn
});
pinInstance.subscribe({
name: ' ',
callback: commonFn
});
pinInstance.subscribe({
name: ' ',
callback: commonFn
});
//
pinInstance.publish();
//
// ,AJ1 666
// ,AJ1 100
// ,AJ1 666
したがって、リリース購読モードを実現するための2つのポイントを覚えてください.リリース(トリガ)&購読(傍受)EventEmitter
これにより、EventEmitterの疑似コードも実現できます.
function EventEmitter() {
this.events = Object.create(null);
}
//
EventEmitter.prototype.on = (type, event) => {
if (!this.events) this.events = Object.create(null);
if (!this.events[type]) this.events[type] = [];
this.events[type].push(event);
}
//
EventEmitter.prototype.emit = (type, ...args) => {
if (!this.events[type]) return;
this.events[type].forEach(event => {
event.call(this, ...args);
})
}
//
function Girl() {}
//
Girl.prototype = Object.create(EventEmitter.prototype);
const lisa = new Girl();
lisa.on(' ', () => {
console.log(' !');
});
lisa.emit(' ');
// console: !
双方向データバインディングを深入にする下記の例
defineProperty
は、以下のコードに基づいて構成されている.
最も簡単な実現注釈しないでください.みんなが読めます.
const inputDom = document.getElementsByTagName('input')[0];
const textDom = document.getElementsByTagName('p')[0];
inputDom.addEventListener('input', e => {
const val = e.target.value;
textDom.innerText = val;
});
defineProperty実現1、極簡略版
const vm = {
data: ''
};
const inputDom = document.getElementsByTagName('input')[0];
const textDom = document.getElementsByTagName('p')[0];
Object.defineProperty(vm, 'data', {
set(newVal) {
if (vm['data'] === newVal) return;
//
textDom.innerText = newVal;
}
});
inputDom.addEventListener('input', e => {
vm.data = e.target.value;
});
2、階段版は属性を変えたり、dom
を追加したりすれば、多重化できなくなります.私たちは反復して、複数のv-model
の状況に適応できる.まず整理して何をするべきですか?
v-model
Object.defineProperty
を巡回して、Dom Tree
およびv-model
を解析する.v-text
にイベントバインディングの変更を行い、v-model
に購読者を追加し、v-text
に購読してビューの更新を実現する./*
*
*/
const vm = {
data: ''
};
function observe(obj) {
Object.keys(obj).forEach(key => {
let val = obj[key];
Object.defineProperty(obj, key, {
get() {
return val;
},
set(newVal) {
if (newVal === val) return;
// vm
val = newVal;
}
})
})
}
/*
*
*/
const Dep = {
target: null,
subs: [],
addSubs(sub) {
this.subs.push(sub)
},
notify() {
this.subs.forEach(sub => {
sub.update();
});
}
}
getterにウォッチを追加し、setterでデータの変化を観測した時、すべての「購読者」の更新をトリガします.// ...
get() {
// target watcher
if (Dep.target) Dep.addSubs(Dep.target);
return val;
},
set(newVal) {
if (newVal === val) return;
// vm
val = newVal;
Dep.notify();
}
// ...
次に【購読者】vm
を定義し、この例では、各nodeノードとして理解することができる.function Watcher(node, vm, name) {
Dep.target = this;
this.node = node;
this.vm = vm;
// name key
this.name = name;
// watcher dep
this.update();
Dep.target = null;
}
// Watcher update get
Watcher.prototype = {
update() {
this.get();
this.node.innerText = this.value;
},
// getter Dep.addSub
get() {
this.value = this.vm[this.name];
}
}
その後、対応するノードに対して解析処理を行う.function complie(node, vm) {
if (node.nodeType === 1) {
[...node.attributes].forEach(attr => {
const name = attr.nodeValue;
if (attr.nodeName === 'v-model') {
node.addEventListener('input', e => {
vm[name] = e.target.value;
})
} else if (attr.nodeName === 'v-text') {
new Watcher(node, vm, name)
}
})
}
}
今はノードごとにバインディング処理ができます.function MVVM(id, vm) {
observe(vm);
const node = document.getElementById(id);
// fragment ,
const fragment = document.createDocumentFragment();
let child;
while(child = node.firstChild) {
fragment.appendChild(child)
}
}
呼び出しMVVM(vm);
全体のコードは最初はかなり遠回りに見えるが、watcher
、observe
、complie
、Dep
、Wacther
といういくつかの概念を理解すれば、MVVM
がほぼ理解できると信じている.(未完は継続します.ProxyのMVM実現方法を更新します.)