【Vue】基礎(虛擬DOM & 響應式原理)


虛擬 DOM

Vue 通過建立一個虛擬 DOM 來追蹤自己要如何改變真實 DOM

在Vue中定義虛擬節點(VNode)描述節點信息

export default class VNode {
  tag: string | void;
  data: VNodeData | void;
  children: ?Array<VNode>;
  text: string | void;
  elm: Node | void;
  ns: string | void;
  context: Component | void; // rendered in this component's scope
  key: string | number | void;
  componentOptions: VNodeComponentOptions | void;
  componentInstance: Component | void; // component instance
  parent: VNode | void; // component placeholder node

這里描述節點文本,標簽信息(tag),真實Dom節點(elm),節點的data信息,子節點,父節點等信息

“虛擬 DOM”是我們對由 Vue 組件樹建立起來的整個 VNode 樹的稱呼

從結構可以看到根節點(parent為空)就可以表示整個樹

有了虛擬 DOM ,Vue就會比較差異,更新真實DOM
比較差異是在patch.js里面的patch方法(補丁)

 

響應式原理

Vue的響應式大概會經過下面幾個階段

1. 使用 Object.defineProperty 把屬性全部轉為getter/setter

2. 屬性變更時通知觀察者(watcher)變更

3. watcher觸發重新渲染生成虛擬 DOM

4. Vue框架遍歷計算新舊虛擬 DOM差異

  4.1 由於 JavaScript 的限制,Vue 不能檢測數組和對象的變化

5. 加載操作,將差異局部修改到真實 DOM

 

從源碼解讀Vue響應式(部分代碼有截取)

//截取部分代碼
Object.defineProperty(obj, key, {
    get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      return value
    },
    set: function reactiveSetter (newVal) {
      const value = getter ? getter.call(obj) : val
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      // #7981: for accessor properties without setter
      if (getter && !setter) return
      if (setter) {
        setter.call(obj, newVal)
      } else {
        val = newVal
      }
      childOb = !shallow && observe(newVal)
      dep.notify()
    }
  })

setter前面的都是賦值的判斷,

1. 值是否相等,

2. 是否自定義setter函數,

3. 是否只讀

4. 最后一句dep.notify(),dep是什么類型,這里看都猜到是通知,具體定義

 
         
const dep = new Dep()
export default class Dep {
  static target: ?Watcher;
  id: number;
  subs: Array<Watcher>;

  constructor () {
    this.id = uid++
    this.subs = []
  }

  addSub (sub: Watcher) {
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    remove(this.subs, sub)
  }

  depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
  }

  notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    if (process.env.NODE_ENV !== 'production' && !config.async) {
      // subs aren't sorted in scheduler if not running async
      // we need to sort them now to make sure they fire in correct
      // order
      subs.sort((a, b) => a.id - b.id)
    }
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }
}

可以看到,Dep類 提供一個訂閱,通知的功能

最后我們看一下訂閱的目標Watcher是做什么
Watcher最重要的一個方法update

 update () {
    /* istanbul ignore else */
    if (this.lazy) {
      this.dirty = true
    } else if (this.sync) {
      this.run()
    } else {
      queueWatcher(this)
    }
  }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM