ele表格操作区根据数据_Vue数据绑定
這是一篇簡單的學習筆記。在學習一段時間Vue后,嘗試實現一下Vue的數據綁定。
相關源碼:https://github.com/buchuitoudegou/Data-Binding-demo
Vue的數據綁定機制利用了觀察者設計模式,利用偵聽器動態更新DOM元素中的值,以下是Vue在編譯時綁定數據的過程。
綁定setter和getter
index.mjs是編譯的入口。從SelfVue的構造函數我們可以看到,對于options中的data,我們會先對它進行一個observe的操作,即綁定getter和setter,這也是數據響應模式構造的開始。
// index.mjs export function SelfVue(options) {this.data = options.data;observe(this.data);Object.keys(this.data).forEach((key) => {Object.defineProperty(this, key, {enumerable: true,configurable: true,get: () => {return this.data[key];},set: (newVal) => {this.data[key] = newVal;// console.log('set index');}});});new Compile(options.el, this);return this; } // Observable.mjs export function observe(data) {if (!data || !(typeof(data) === 'object')) {return;}Object.keys(data).forEach((key) => {defineReactive(data, key, data[key]);}); }function defineReactive(data, key, val) {observe(val);const dep = new Dep;Object.defineProperty(data, key, {enumerable: true,configurable: true,get: () => {if (Dep.target) {dep.addSub(Dep.target);}return val;},set: (newVal) => {// console.log(newVal);val = newVal;dep.notify();}}); }defineReactive函數遞歸給data對象的每個屬性(如果該屬性也是對象,則遞歸執行)綁定getter和setter。
getter是這樣定義的:如果這個屬性被訂閱了,則將它的偵聽器加入到訂閱數組中,最后返回這個屬性的值。
// Dep.mjs export class Dep {constructor() {this.subs = [];}static target = null;addSub(sub) {this.subs.push(sub);}notify() {this.subs.forEach(sub => sub.update());} }這里提到了兩個概念偵聽器(Watcher)和訂閱(Dep)數組。偵聽器接下來會提到,先簡單介紹一下訂閱的概念。訂閱器(Dep)是一個實例,每個data中的對象(Object)都會有一個Dep實例(data自己也會有一個),它里面定義了訂閱數組,存儲了所有訂閱某個屬性的偵聽器。訂閱器有一個notify方法,用于通知訂閱數組里面所有的偵聽器,某個值發生了改變(并不會判斷該偵聽器是否偵聽了這個值)。另外,訂閱器類(Dep)有一個靜態屬性target,用于存儲當前正在進行訂閱操作的屬性(相當于訂閱器的辦事窗口,而且只有一個窗口)。
介紹完訂閱器的概念之后,繼續之前的內容,setter的定義應該就可以猜到了:setter在調用時,除了更新這個屬性的值,還會調用訂閱器的notify。
上面的步驟是Vue的root實例在定義的時候做的第一步工作。由于這個時候還不知道哪些屬性會綁定到DOM上,因此,所有訂閱器的訂閱數組都是空的。
編譯DOM節點
// Compile.mjs export class Compile {constructor(el, vm) {this.vm = vm;this.el = document.querySelector(el);this.fragment = null;this.init();}init() {if (this.el) {this.fragment = document.createDocumentFragment();let child = this.el.firstChild;while (child) {this.fragment.appendChild(child);child = this.el.firstChild;}[].slice.call(this.fragment.childNodes).forEach(node => {this.compileElement(node);});this.el.appendChild(this.fragment);}}compileElement(ele) {const reg = /{{(.*)}}/;if (ele.nodeType === 3) {if (reg.test(ele.nodeValue)) {let temp = RegExp.$1;temp = temp.trim();ele.nodeValue = getValue(this.vm, temp.split('.'));new Watcher(this.vm, temp.split('.'), (value) => {ele.nodeValue = value;});}} else if (ele.nodeType === 1) {const attr = ele.getAttribute('vmodel');if (attr) {ele.value = getValue(this.vm, attr.split('.'));new Watcher(this.vm, attr.split('.'), (value) => {ele.value = value;});ele.addEventListener('input', (e) => {this.vm[attr] = e.target.value;});}}} }這個步驟里,我們會知道哪些節點(HTMLElement或者文本節點)會和Vue的data有互動。給這些有互動的節點設置偵聽器(Watcher)。接下來介紹一下Watcher。
Watcher設置
// Watcher.mjs export function getValue(data, exp) {if (exp.length === 0) {return null;}let temp = data;exp.forEach((key) => {temp = temp[key];});return temp; }export class Watcher {constructor(vm, exp, cb) {this.vm = vm;this.exp = exp;this.cb = cb;this.value = this.get();}update() {const value = getValue(this.vm.data, this.exp);const oldVal = this.value;if (value !== oldVal) {this.value = value;this.cb.call(this.vm, value, oldVal);}}get() {Dep.target = this;const value = getValue(this.vm.data, this.exp);Dep.target = null;return value;} }偵聽器,顧名思義用于偵聽某個屬性的值是否發生了變化。我們在給某個屬性構造偵聽器的時候需要傳入一個回調函數,這個回調函數用于更新對應DOM節點的值。至于哪個節點對應哪個屬性值我們在編譯的時候就可以得知。
Watcher在構造的時候,會強行調用這個屬性值的getter。這是為什么呢?之前我們也提到過,getter中定義了,若某個值被訂閱,則它的偵聽器會加入到訂閱數組中。因此,給某個值構造Watcher時,先讓Dep的訂閱窗口(Dep.target)指向這個Watcher,說明這個值需要被訂閱,然后再調用這個值的getter,這樣子Dep的訂閱數組中就會把這個Watcher放進去。最后將訂閱窗口(Dep.target)指向null,表示訂閱完成,可以訂閱下一個屬性。
每個Watcher都會有一個update方法,用于調用之前提到過的回調函數。當Dep調用notify方法的時候,就會觸發每個在訂閱數組中的Watcher的update方法。update方法中確認自己的偵聽的值是否發生了變化,若發生了變化則調用構造的時候傳入的回調函數,更新DOM元素中的值。
當一個值發生變化時
當data中一個值發生了變化時,setter會調用對應Dep實例的notify函數,notify函數調用實例中存儲的訂閱數組中所有偵聽器的update方法。update方法中,偵聽器會判斷自己偵聽的值是否發生了變化。若發生了變化,則調用回調函數,更新DOM節點中的值。
參考
雙向綁定
vue 的雙向綁定原理及實現 - 前端 - 掘金
總結
以上是生活随笔為你收集整理的ele表格操作区根据数据_Vue数据绑定的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: g++ vscode 环境选择_Visu
- 下一篇: build vue 选哪个_分析vue-