WEEX H5 Render 解读(11)之vdom

今天我们来阅读下weex源码的vdom. 阅读这一部分,你首先知道dom提供的接口是什么.源码所在位置是:

WEEX H5 Render 解读(11)之vdom_第1张图片

1.猛一看

猛一看这个文件里声明了这几个类,基本与html的DOM中的概念一致。其提供的方法(有些方法名都一样)也是后者的阉割。

  • Document,类似于html中document的概念
  • Node,类似于逻辑树中的一个节点,可以用来遍历和查找。
  • Element,继承自Node,其直接对应dom树中的一个节点
  • Comment,继承自Node,就是weex的注释类型。

vdom的作用可能也就是迎来送往的作用,原生更新了视图后通知vdom或者js更新vdom后通知原生的作用。

2.Document

2.1 初始化

在初始化的代码中,主要是设置了id,url,instanceMap,并且初始化了监听器,最后调用了createDocumentElement方法。

export function Document (id, url, handler) {
  id = id ? id.toString() : ''
  this.id = id
  this.URL = url

  instanceMap[id] = this
  this.nodeMap = {}
  this.listener = new Listener(id, handler || genCallTasks(id))
  this.createDocumentElement()
}

这里的nodeMap就类似下图中的索引,而下图中的树则是这个dom树


WEEX H5 Render 解读(11)之vdom_第2张图片

2.2 createDocumentElement

Document.prototype.createDocumentElement = function () {
  if (!this.documentElement) {
    const el = new Element('document')
    el.docId = this.id
    el.ownerDocument = this
    el.role = 'documentElement'
    el.depth = 0
    el.ref = '_documentElement'
    this.nodeMap._documentElement = el
    this.documentElement = el
    el.appendChild = (node) => {
      appendBody(this, node)
    }
    el.insertBefore = (node, before) => {
      appendBody(this, node, before)
    }
  }

  return this.documentElement
}

我们可以看到,document也是Element的一种。只是document的role被设为了documentElement。笔者对下面的代码极为佩服:

 el.appendChild = (node) => {
      appendBody(this, node)
    }
    el.insertBefore = (node, before) => {
      appendBody(this, node, before)
    }

这段代码重写了原有的element的方法。。

2.3 appendBody

在appendBody方法中,

  • 首先判断是否document是否已经有真子元素,如果有则不能使用这个方法。
  • 插入子元素
if (documentElement.pureChildren.length > 0 || node.parentNode) {
    return
  }
  const children = documentElement.children
  const beforeIndex = children.indexOf(before)
  if (beforeIndex < 0) {
    children.push(node)
  }
  else {
    children.splice(beforeIndex, 0, node)
  }

判断node的角色是否为body,如果是,则设置这个节点的docid,ownerDocument, parentNode

if (node.role === 'body') {
      node.docId = doc.id
      node.ownerDocument = doc
      node.parentNode = documentElement
    }

如果节点的角色不是body,则把这个节点的子节点的parentNode 指向这个节点,然后设置document的body体,并且把node和document连接在一起。

else {
      node.children.forEach(child => {
        child.parentNode = node
      })
      setBody(doc, node)
      node.docId = doc.id
      node.ownerDocument = doc
      linkParent(node, documentElement)
      delete doc.nodeMap[node.nodeId]
    }

delete doc.nodeMap[node.nodeId]用来删除原有索引的值,因为可以通过_root得到原来的值。

  • 最后设置nodeType!=1的情况,目前就是comment元素。
    node.parentNode = documentElement
    doc.nodeMap[node.ref] = node

2.4 fireEvent

fireEvent 用以激活元素的事件,以及此事件所带来的dom变化。

if (!el) {
  return
}
e = e || {}
e.type = type
e.target = el
e.timestamp = Date.now()
if (domChanges) {
  updateElement(el, domChanges)
}
return el.fireEvent(type, e)

主要是用来生成事件的一些参数,比如时间戳,然后更新节点,最后嗲用el的fireEvent方法。可以看到这个el是Element类型,domChanges是一个对象。type的值官方说是只有div,list和scroller,但是在这个dom中并没有这个值得定义。


再读读代码,发现特别像早期的从窗口处理机制-监听器模式。当dom有变动时,监听器会调用这个事件的处理器。

const handler = this.event[type]

if (handler) {

return handler.call(this, e)

}

下节将继续阅读vdom代码,并绘制vdom的示意图

你可能感兴趣的:(WEEX H5 Render 解读(11)之vdom)