Vue - 渲染函數render

1、render函數是什麼

簡單的說,在vue中咱們使用模板HTML語法來組建頁面的,使用render函數咱們能夠用js語言來構建DOM。由於vue是虛擬DOM,因此在拿到template模板時也要轉譯成VNode的函數,而用render函數構建DOM,vue就免去了轉譯的過程。javascript

  當使用render函數描述虛擬DOM時,vue提供一個函數,這個函數是就構建虛擬DOM所須要的工具。官網上給他起了個名字叫createElement。還有約定它的簡寫叫hhtml

示例: 分別使用html語法和render函數來實現根據傳入的 level (h1-h6)頁面渲染不一樣的標題格式

【1】使用組件的形式vue

// 父組件 <template> <div> <child1 :level='level'>我是標題</child1> </div> </template> <script> const child1 = () => import("./child1.vue"); export default { components: { child1 }, data() { return { level: 1 }; }, }; </script>複製代碼
// 子組件 child1.vue <template> <div> <h1 v-if="level == 1"> <slot></slot> </h1> <h2 v-if="level == 2"> <slot></slot> </h2> <h3 v-if="level == 3"> <slot></slot> </h3> <h4 v-if="level == 4"> <slot></slot> </h4> <h5 v-if="level == 5"> <slot></slot> </h5> <h6 v-if="level == 6"> <slot></slot> </h6> </div> </template> <script> export default { props: { level: { require: true, type: Number, } } }; </script>複製代碼

【2】使用render函數的形式(修改child1子組件)java

<script> export default { props: { level: { require: true, type: Number, } }, render(createElement) { return createElement('h' + this.level, this.$slots.default); } }; </script>複製代碼

對比兩種實現方式,咱們發現這裏用模板並非最好的選擇,不但代碼冗長,並且在每個級別的標題中重複書寫了<slot></slot>。 使用render函數實現看起來簡單多了,這樣代碼精簡不少,可是須要很是熟悉 Vue 的實例屬性。在這個例子中,你須要知道,向組件中傳遞不帶v-slot指令的子節點時,好比child1中的「我是標題」,這些子節點被存儲在組件實例中的this.$slots.default中。git

2、render函數的參數

render 函數即渲染函數,它是個函數,render 函數的返回值是VNode(即:虛擬節點,也就是咱們要渲染的節點)
createElement 是 render 函數的參數,它自己也是個函數,而且有三個參數。接來下咱們重點介紹這三個參數github

【1】createElement 第一個參數是必填的,能夠是String | Object | Function express

  1. String,表示的是HTML 標籤名
  2. Object ,一個含有數據的組件選項對象
  3. Function ,返回了一個含有標籤名或者組件選項對象的async 函數

示例:數組

render: function (createElement) {
      // String
      return createElement('h1');

      //Object
      return createElement({
        template: " <div>鋤禾日當午</div> "
      })

      // Function
      let domFun = function () {
        return {
          template: " <div> 鋤禾日當午</div> "

        }
      }
      return createElement(domFun())
    }複製代碼

【2】createElement 第二個參數是選填的,一個與模板中屬性對應的數據對象 經常使用的有class | style | attrs | domProps | onbash

  1. class:控制類名
  2. style :樣式
  3. attrs :用來寫正常的 html 屬性 id src 等等
  4. domProps :用來寫原生的dom 屬性
  5. on::用來寫原生方法
return createElement('div', {
        // 與 `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<VNode> }
        scopedSlots: {
          default: props => createElement('span', props.text)
        },

        // 若是組件是其它組件的子組件,需爲插槽指定名稱
        slot: 'name-of-slot',

        // 其它特殊頂層屬性
        key: 'myKey',
        ref: 'myRef',
        
        // 若是你在渲染函數中給多個元素都應用了相同的 ref 名,
        // 那麼 `$refs.myRef` 會變成一個數組。
        refInFor: true
      })複製代碼

【3】createElement 第三個參數是選填的,表明子級虛擬節點 (VNodes),由 `createElement()` 構建而成,正常來說接收的是一個字符串或者一個數組,通常數組用的是比較多的dom

return createElement('div', {
        attrs: {
          id: "content"
        }
      }, [
          createElement('h1', '我是H1標題'),
          createElement('h6', '我是H6標題')
        ]
      )複製代碼

3、v­-model在render函數中的使用

在render函數中,沒有提供v-model的實現,因此你必須本身實現相應的邏輯。這就是深刻底層的代價,但與v-model相比,這可讓你更好地控制交互細節。

<template> <div> <child1 :name='name' v-model="name"></child1> <p>{{name}}</p> </div> </template> <script> const child1 = () => import("./child1.vue"); export default { components: { child1 }, data() { return { name: 'Demi' }; }, }; </script>複製代碼
// child1子組件 <script> export default { props: { name: { require: true, } }, render(createElement) { let self = this return createElement('input', { domProps: { value: self.name }, on: { input(event) { self.$emit('input', event.target.value) } } }) } }; </script>複製代碼

結果以下:

render函數經過input方法實現數據雙向綁定,當子組件name改變,父組件也隨着更新

4、render函數中的事件修飾符

對於.passive,.capture, .once 這些事件修飾符, Vue 提供了相應的前綴能夠用於 on

修飾符 前綴
.passive &
.capture !
.once ~
.capture.once.once.capture ~!
on: {
    '!click': this.doThisInCapturingMode,
    '~keyup': this.doThisOnce,
    '~!mouseover': this.doThisOnceInCapturingMode
  }複製代碼

5、JSX語法糖

JSX就是Javascript和XML結合的一種格式。React發明了JSX,利用HTML語法來建立虛擬DOM。當遇到<,JSX就當HTML解析,遇到{就當JavaScript解析。

複雜的render函數書寫異常痛苦,這就是爲何會有一個Babel插件,用於在 Vue 中使用 JSX 語法,它可讓咱們回到更接近於模板的語法上。

render(h) {
    return (
      <Child1 level={1}>
        <span>Hello</span> world!
    </Child1>
    )
  }複製代碼

將h做爲createElement的別名是 Vue 生態系統中的一個通用慣例,實際上也是 JSX 所要求的。從 Vue 的 Babel 插件的3.4.0版本開始,咱們會在以 ES2015 語法聲明的含有 JSX 的任何方法和 getter 中 (不是函數或箭頭函數中) 自動注入const h = this.$createElement,這樣你就能夠去掉(h)參數了。對於更早版本的插件,若是h在當前做用域中不可用,應用會拋錯。

JSX語法學習文檔: vuejs/JSX JSX語法簡介

相關文章
相關標籤/搜索