欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【Vue】基础(虚拟DOM & 响应式原理)

程序员文章站 2022-03-25 16:59:50
Vue虚拟DOM是实现响应式的核心,弄清楚虚拟DOM和Vue响应式原理,对写代码有参考作用 ......

虚拟 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】基础(虚拟DOM & 响应式原理)

 

响应式原理

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)
    }
  }