【再学フロントエンドフレームワーク】初探Vueにおける仮想DOM


前言
React、Vueでは仮想DOMが使われていますが、Vueではどのように使われていますか?
Vueの中の模版は真実のDOMの思想に転化します
Vueでのテンプレートのコンパイルは、テンプレート--->ast(抽象ツリー)-->render関数->仮想dom->実際のdomです.
vueのテンプレートはcompilerによってast(テンプレートを表すjsオブジェクト、またはastはソースコードを表すjsオブジェクト)にコンパイルされ、astは対応するrender関数(ここではastの変換の詳細は後述せず)を生成し、render関数は仮想ノードvnode(ノードおよびそのサブノードの情報を記述するために使用される)を生成します.vnodeの集合はVirtual Dom(vueコンポーネントで構築されたvnodeツリー全体を仮想Domツリーと呼びます)を構成し、最後に実際のDomを生成します.
Vueのテンプレートを仮想DOM詳細に変換
astの構造
astは、ソースコードを表すjsオブジェクト(dom構造を記述するjsオブジェクト)である.
ast形式:
function add(a, b) {
    return a +
      //             
      b
  }

astに変換後:
FunctionDeclaration {
  type: 'FunctionDeclaration',
  id: Identifier {
    type: 'Identifier',
    name: 'add',
    loc: {
      start: [Object],
      end: [Object],
      lines: [Lines],
      tokens: [Array],
      indent: 2
    }
  },
  params: [
    Identifier { type: 'Identifier', name: 'a', loc: [Object] },
    Identifier { type: 'Identifier', name: 'b', loc: [Object] }
  ],
  body: BlockStatement {
    type: 'BlockStatement',
    body: [ [ReturnStatement] ],
    loc: {
      start: [Object],
      end: [Object],
      lines: [Lines],
      tokens: [Array],
      indent: 2
    }
  },
  generator: false,
  expression: false,
  async: false,
  loc: {
    start: { line: 2, column: 2, token: 0 },
    end: { line: 6, column: 3, token: 13 },
    lines: Lines {
      infos: [Array],
      mappings: [],
      cachedSourceMap: null,
      cachedTabWidth: undefined,
      length: 7,
      name: null
    },
    tokens: [
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object], [Object],
      [Object]
    ],
    indent: 2
  }
}

astをrender関数に変換
vueテンプレートは次のとおりです.

{{ blogTitle }}


最終的には次のrender関数に変換されます.
render: function (createElement) { 
  return createElement('h1', this.blogTitle) 
}

createElement関数パラメータ:

createElement(
  // {String | Object | Function}
  //    HTML    、      ,  
  // resolve            async   。   。
  'div',

  // {Object}
  //                。  。
  {
    // (      )
  },

  // {String | Array}
  //        (VNodes),  `createElement()`     ,
  //            “      ”。  。
  [
    '      ',
    createElement('h1', '    '),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

createElementの2番目のパラメータは、ノードのプロパティです.
{
  //   `v-bind:class`   API   ,
  //        、              
  'class': {
    foo: true,
    bar: false
  },
  //   `v-bind:style`   API   ,
  //        、  ,        
  style: {
    color: 'red',
    fontSize: '14px'
  },
  //     HTML   
  attrs: {
    id: 'foo'
  },
  //    prop
  props: {
    myProp: 'bar'
  },
  // DOM   
  domProps: {
    innerHTML: 'baz'
  },
  //        `on`    ,
  //        `v-on:keyup.enter`       。
  //              keyCode。
  on: {
    click: this.clickHandler
  },
  //      ,        ,         
  // `vm.$emit`      。
  nativeOn: {
    click: this.nativeClickHandler
  },
  //      。  ,     `binding`    `oldValue`
  //   ,   Vue            。
  directives: [
    {
      name: 'my-custom-directive',
      value: '2',
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],
  //          
  // { name: props => VNode | Array }
  scopedSlots: {
    default: props => createElement('span', props.text)
  },
  //              ,        
  slot: 'name-of-slot',
  //         
  key: 'myKey',
  ref: 'myRef',
  //                       ref  ,
  //    `$refs.myRef`        。
  refInFor: true
}

vueのrender関数の適用:
renderの作成柔軟性の高いコンポーネント
createElement( 
'anchored-heading',
{ props: { level: 1 } }, 
[ createElement('span', 'Hello'),' world!'] 
)
  
Hello world! 

Virtual Dom構造
render関数は最終的に仮想DOMを生成する.Virtual Domは単純なJSオブジェクトにすぎず、tag、props、childrenの3つのプロパティを最小限に抑えています.
Virtual Domのフォーマット:
{
    tag: "div",
    props: {},
    children: [
        "Hello World", 
        {
            tag: "ul",
            props: {},
            children: [{
                tag: "li",
                props: {
                    id: 1,
                    class: "li-1"
                },
                children: [" ", 1]
            }]
        }
    ]
}

Reactにおける仮想DOMの生成を比較する
Reactでのテンプレートレンダリングはrender関数で定義され、次のテンプレートです.
class Hello extends Component {
  render() {
    return 
Hello ConardLi
; } }

最終的にBabelはcreateElementを呼び出す形式に変換され、仮想DOMを生成します.この形式は、2番目のテンプレートを生成する方法としても使用できます.
class Hello extends Component {
  render() {
    return React.createElement('div', null, `Hello ConardLi`);
  }
}

VueではReactのようにJSXを使用してテンプレートを作成することもできます.対応するBableプラグインを導入する必要があります(https://github.com/vuejs/jsx):
import AnchoredHeading from  './AnchoredHeading.vue'
new Vue({ el: '#demo', 
render: function (h) { 
return ( 
     
    Hello 
    world!
     
)} 
})

参考資料:レンダー関数&JSX 【React深さ】仮想DOMのレンダリング原理と特性を深く分析する