美文网首页weex社区web前端学习ing
WEEX H5 Render 解读(11)之vdom

WEEX H5 Render 解读(11)之vdom

作者: cpu_driver | 来源:发表于2016-08-17 21:52 被阅读302次

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


    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树


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

    <code>delete doc.nodeMap[node.nodeId]</code>用来删除原有索引的值,因为可以通过_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

        本文链接:https://www.haomeiwen.com/subject/gawmsttx.html