Vue 2.0双方向バインド原理の実現
29208 ワード
Object.definePropertyメソッド
vue.jsはデータハイジャックとパブリッシャー-サブスクライバモードを組み合わせた方式を採用し、Object.defineProperty()は、各属性のsetter,getterをハイジャックし、データが変動したときに購読者にメッセージを発行し、対応するリスニングコールバックをトリガーします.
具体的な実装プロセスのコードは次のとおりです.定義コンストラクタ 定義命令解析器 観察者 を定義する.定義購読者 ビュー を更新する.
以上のコードは1つのVueに定義.jsファイルでは、双方向バインドを使用する必要がある場所に導入すればよい.使用してみます.
vue.jsはデータハイジャックとパブリッシャー-サブスクライバモードを組み合わせた方式を採用し、Object.defineProperty()は、各属性のsetter,getterをハイジャックし、データが変動したときに購読者にメッセージを発行し、対応するリスニングコールバックをトリガーします.
具体的な実装プロセスのコードは次のとおりです.
function Vue(option){
this.$el = document.querySelector(option.el); //
this.$data = option.data;
this.$methods = option.methods;
this.deps = {}; // ( ):{msg: [ 1, 2, 3], info: [ 1, 2]}
this.observer(this.$data); //
this.compile(this.$el); //
}
Vue.prototype.compile = function (el) {
let nodes = el.children; //
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node.children.length) {
this.compile(node) //
}
if (node.hasAttribute('l-model')) { // l-model
let attrVal = node.getAttribute('l-model'); //
node.addEventListener('input', (() => {
this.deps[attrVal].push(new Watcher(node, "value", this, attrVal)); //
let thisNode = node;
return () => {
this.$data[attrVal] = thisNode.value //
}
})())
}
if (node.hasAttribute('l-html')) {
let attrVal = node.getAttribute('l-html'); //
this.deps[attrVal].push(new Watcher(node, "innerHTML", this, attrVal)); //
}
if (node.innerHTML.match(/{{([^\{|\}]+)}}/)) {
let attrVal = node.innerHTML.replace(/[{{|}}]/g, ''); //
this.deps[attrVal].push(new Watcher(node, "innerHTML", this, attrVal)); //
}
if (node.hasAttribute('l-on:click')) {
let attrVal = node.getAttribute('l-on:click'); //
node.addEventListener('click', this.$methods[attrVal].bind(this.$data)); // this this.$data
}
}
}
Vue.prototype.observer = function(data){
for(var key in data){
(function(that){
let val = data[key]; //
that.deps[key] = []; // {msg: [ ], info: []}
var watchers = that.deps[key];
Object.defineProperty(data, key, { //
get: function(){
return val;
},
set: function(newVal){ // ( )
if(val !== newVal){
val = newVal;
}
//
watchers.forEach(watcher=>{
watcher.update()
})
}
})
})(this)
}
}
function Watcher(el, attr, vm, attrVal) {
this.el = el;
this.attr = attr;
this.vm = vm;
this.val = attrVal;
this.update(); //
}
Watcher.prototype.update = function () {
this.el[this.attr] = this.vm.$data[this.val]
}
以上のコードは1つのVueに定義.jsファイルでは、双方向バインドを使用する必要がある場所に導入すればよい.使用してみます.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Documenttitle>
<script src="./vue.js">script>
head>
<body>
<div id="app">
<input type="text" l-model="msg" >
<p l-html="msg">p>
<input type="text" l-model="info" >
<p l-html="info">p>
<button l-on:click="clickMe"> button>
<p>{{msg}}p>
div>
<script>
var vm = new Vue({
el: "#app",
data: {
msg: " ",
info: " , "
},
methods: {
clickMe(){
this.msg = " ";
}
}
})
script>
body>
html>