使用
調用data
onLoad(option) {
_self = this;
_self.$data.xxxx = "te";
}
綁定節點
元素~~~~
<input @click="categoryChange" :data-id="item.id" />
方法
methods: {
tabSelect(e) {
this.TabCur = e.currentTarget.dataset.id;
},
}
雙向綁定原理
https://www.cnblogs.com/wangjiachen666/p/9883916.html
原理
Vue內部通過Object.defineProperty方法屬性攔截的方式,把data對象里每個數據的讀寫轉化成getter/setter,當數據變化時通知視圖更新。
也就是說:
輸入框內容變化時,data 中的數據同步變化。即 view => model 的變化。
data 中的數據變化時,文本節點的內容同步變化。即 model => view 的變化。
使數據對象變得“可觀測”
Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。
依賴收集
完成了數據的'可觀測',即我們知道了數據在什么時候被讀或寫了,那么,我們就可以在數據被讀或寫的時候通知那些依賴該數據的視圖更新了,為了方便,我們需要先將所有依賴收集起來,一旦數據發生變化,就統一通知更新。其實,這就是典型的“發布訂閱者”模式,數據變化為“發布者”,依賴對象為“訂閱者”。
訂閱者Watcher
訂閱者Watcher 是一個 類,在它的構造函數中,定義了一些屬性:
vm:一個Vue的實例對象;
exp:是node節點的v-model或v-on:click等指令的屬性值。如v-model="name",exp就是name;
cb:是Watcher綁定的更新函數;
總結
實現數據的雙向綁定,首先要對數據進行劫持監聽,所以我們需要設置一個監聽器Observer,用來監聽所有屬性。如果屬性發上變化了,就需要告訴訂閱者Watcher看是否需要更新。因為訂閱者是有很多個,所以我們需要有一個消息訂閱器Dep來專門收集這些訂閱者,然后在監聽器Observer和訂閱者Watcher之間進行統一管理的。
代碼
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <h1 id="name"></h1> <input type="text"> <input type="button" value="改變data內容" onclick="changeInput()">
<script src="observer.js"></script>
<script src="watcher.js"></script>
<script>
function myVue (data, el, exp) {
this.data = data;
observable(data); //將數據變的可觀測
el.innerHTML = this.data[exp]; // 初始化模板數據的值
new Watcher(this, exp, function (value) {
el.innerHTML = value;
});
return this;
}var ele = document.querySelector('#name'); var input = document.querySelector('input'); var myVue = new myVue({ name: 'hello world' }, ele, 'name'); //改變輸入框內容 input.oninput = function (e) { myVue.data.name = e.target.value } //改變data內容 function changeInput(){ myVue.data.name = "難涼熱血" }
</script>
</body>
</html>
observer.js
/** * 把一個對象的每一項都轉化成可觀測對象 * @param { Object } obj 對象 */ function observable (obj) { if (!obj || typeof obj !== 'object') { return; } let keys = Object.keys(obj); keys.forEach((key) =>{ defineReactive(obj,key,obj[key]) }) return obj; } /** * 使一個對象轉化成可觀測對象 * @param { Object } obj 對象 * @param { String } key 對象的key * @param { Any } val 對象的某個key的值 */ function defineReactive (obj,key,val) { let dep = new Dep(); Object.defineProperty(obj, key, { get(){ dep.depend(); console.log(`${key}屬性被讀取了`); return val; }, set(newVal){ val = newVal; console.log(`${key}屬性被修改了`); dep.notify() //數據變化通知所有訂閱者 } }) } class Dep {
constructor(){ this.subs = [] } //增加訂閱者 addSub(sub){ this.subs.push(sub); } //判斷是否增加訂閱者 depend () { if (Dep.target) { this.addSub(Dep.target) } } //通知訂閱者更新 notify(){ this.subs.forEach((sub) =>{ sub.update() }) } } Dep.target = null;
watcher.js
class Watcher {
constructor(vm,exp,cb){
this.vm = vm;
this.exp = exp;
this.cb = cb;
this.value = this.get(); // 將自己添加到訂閱器的操作
}
get(){
Dep.target = this; // 緩存自己
let value = this.vm.data[this.exp] // 強制執行監聽器里的get函數
Dep.target = null; // 釋放自己
return value;
}
update(){
let value = this.vm.data[this.exp];
let oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.cb.call(this.vm, value, oldVal);
}
}
}