Vue - 渲染函數(shù)render

一、render函數(shù)是什么

簡(jiǎn)單的說(shuō),在vue中我們使用模板HTML語(yǔ)法來(lái)組建頁(yè)面的,使用render函數(shù)我們可以用js語(yǔ)言來(lái)構(gòu)建DOM。因?yàn)関ue是虛擬DOM,所以在拿到template模板時(shí)也要轉(zhuǎn)譯成VNode的函數(shù),而用render函數(shù)構(gòu)建DOM,vue就免去了轉(zhuǎn)譯的過(guò)程。

當(dāng)使用render函數(shù)描述虛擬DOM時(shí),vue提供一個(gè)函數(shù),這個(gè)函數(shù)是就構(gòu)建虛擬DOM所需要的工具。官網(wǎng)上給他起了個(gè)名字叫createElement。還有約定它的簡(jiǎn)寫(xiě)叫h

示例: 分別使用html語(yǔ)法和render函數(shù)來(lái)實(shí)現(xiàn)根據(jù)傳入的 level (h1-h6)頁(yè)面渲染不同的標(biāo)題格式

【1】使用組件的形式

// 父組件
<template>
  <div>
    <child1 :level='level'>我是標(biāo)題</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函數(shù)的形式(修改child1子組件)

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

對(duì)比兩種實(shí)現(xiàn)方式,我們發(fā)現(xiàn)這里用模板并不是最好的選擇,不但代碼冗長(zhǎng),而且在每一個(gè)級(jí)別的標(biāo)題中重復(fù)書(shū)寫(xiě)了<slot></slot>。 使用render函數(shù)實(shí)現(xiàn)看起來(lái)簡(jiǎn)單多了,這樣代碼精簡(jiǎn)很多,但是需要非常熟悉 Vue 的實(shí)例屬性。在這個(gè)例子中,你需要知道,向組件中傳遞不帶v-slot指令的子節(jié)點(diǎn)時(shí),比如child1中的“我是標(biāo)題”,這些子節(jié)點(diǎn)被存儲(chǔ)在組件實(shí)例中的this.$slots.default中。

二、render函數(shù)的參數(shù)

render 函數(shù)即渲染函數(shù),它是個(gè)函數(shù),render 函數(shù)的返回值是VNode(即:虛擬節(jié)點(diǎn),也就是我們要渲染的節(jié)點(diǎn))
createElement 是 render 函數(shù)的參數(shù),它本身也是個(gè)函數(shù),并且有三個(gè)參數(shù)。接來(lái)下我們重點(diǎn)介紹這三個(gè)參數(shù)

【1】createElement 第一個(gè)參數(shù)是必填的,可以是String | Object | Function

  1. String,表示的是HTML 標(biāo)簽名
  2. Object ,一個(gè)含有數(shù)據(jù)的組件選項(xiàng)對(duì)象
  3. Function ,返回了一個(gè)含有標(biāo)簽名或者組件選項(xiàng)對(duì)象的async 函數(shù)

示例:

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

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

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

        }
      }
      return createElement(domFun())
    }

【2】createElement 第二個(gè)參數(shù)是選填的,一個(gè)與模板中屬性對(duì)應(yīng)的數(shù)據(jù)對(duì)象 ****常用的有class | style | attrs | domProps | on

  1. class:控制類(lèi)名
  2. style :樣式
  3. attrs :用來(lái)寫(xiě)正常的 html 屬性 id src 等等
  4. domProps :用來(lái)寫(xiě)原生的dom 屬性
  5. on::用來(lái)寫(xiě)原生方法
return createElement('div', {
        // 與 `v-bind:class` 的 API 相同,
        // 接受一個(gè)字符串、對(duì)象或字符串和對(duì)象組成的數(shù)組
        'class': {
          foo: true,
          bar: false
        },

        // 與 `v-bind:style` 的 API 相同,
        // 接受一個(gè)字符串、對(duì)象,或?qū)ο蠼M成的數(shù)組
        style: {
          color: 'red',
          fontSize: '14px'
        },

        // 普通的 HTML 特性
        attrs: {
          id: 'foo'
        },

        // 組件 prop
        props: {
          myProp: 'bar'
        },

        // DOM 屬性
        domProps: {
          innerHTML: 'baz'
        },

        // 事件監(jiān)聽(tīng)器在 `on` 屬性?xún)?nèi),
        // 但不再支持如 `v-on:keyup.enter` 這樣的修飾器。
        // 需要在處理函數(shù)中手動(dòng)檢查 keyCode。
        on: {
          click: this.clickHandler
        },

        // 僅用于組件,用于監(jiān)聽(tīng)原生事件,而不是組件內(nèi)部使用
        // `vm.$emit` 觸發(fā)的事件。
        nativeOn: {
          click: this.nativeClickHandler
        },

        // 自定義指令。注意,你無(wú)法對(duì) `binding` 中的 `oldValue`
        // 賦值,因?yàn)?Vue 已經(jīng)自動(dòng)為你進(jìn)行了同步。
        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)
        },

        // 如果組件是其它組件的子組件,需為插槽指定名稱(chēng)
        slot: 'name-of-slot',

        // 其它特殊頂層屬性
        key: 'myKey',
        ref: 'myRef',

        // 如果你在渲染函數(shù)中給多個(gè)元素都應(yīng)用了相同的 ref 名,
        // 那么 `$refs.myRef` 會(huì)變成一個(gè)數(shù)組。
        refInFor: true
      })

【3】createElement 第三個(gè)參數(shù)是選填的,代表子級(jí)虛擬節(jié)點(diǎn) (VNodes),由 createElement() 構(gòu)建而成,正常來(lái)講接收的是一個(gè)字符串或者一個(gè)數(shù)組,一般數(shù)組用的是比較多的

      return createElement('div', {
        attrs: {
          id: "content"
        }
      }, [
          createElement('h1', '我是H1標(biāo)題'),
          createElement('h6', '我是H6標(biāo)題')
        ]
      )

三、v--model在render函數(shù)中的使用

在render函數(shù)中,沒(méi)有提供v-model的實(shí)現(xiàn),所以你必須自己實(shí)現(xiàn)相應(yīng)的邏輯。這就是深入底層的代價(jià),但與v-model相比,這可以讓你更好地控制交互細(xì)節(jié)。

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

結(jié)果如下:

render函數(shù)通過(guò)input方法實(shí)現(xiàn)數(shù)據(jù)雙向綁定,當(dāng)子組件name改變,父組件也隨著更新

四、render函數(shù)中的事件修飾符

對(duì)于.passive,.capture, .once 這些事件修飾符, Vue 提供了相應(yīng)的前綴可以用于 on

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

五、JSX語(yǔ)法糖

JSX就是Javascript和XML結(jié)合的一種格式。React發(fā)明了JSX,利用HTML語(yǔ)法來(lái)創(chuàng)建虛擬DOM。當(dāng)遇到<,JSX就當(dāng)HTML解析,遇到{就當(dāng)JavaScript解析。

復(fù)雜的render函數(shù)書(shū)寫(xiě)異常痛苦,這就是為什么會(huì)有一個(gè)Babel插件,用于在 Vue 中使用 JSX 語(yǔ)法,它可以讓我們回到更接近于模板的語(yǔ)法上。

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

將h作為createElement的別名是 Vue 生態(tài)系統(tǒng)中的一個(gè)通用慣例,實(shí)際上也是 JSX 所要求的。從 Vue 的 Babel 插件的3.4.0版本開(kāi)始,我們會(huì)在以 ES2015 語(yǔ)法聲明的含有 JSX 的任何方法和 getter 中 (不是函數(shù)或箭頭函數(shù)中) 自動(dòng)注入const h = this.$createElement,這樣你就可以去掉(h)參數(shù)了。對(duì)于更早版本的插件,如果h在當(dāng)前作用域中不可用,應(yīng)用會(huì)拋錯(cuò)。

JSX語(yǔ)法學(xué)習(xí)文檔: vuejs/JSX JSX語(yǔ)法簡(jiǎn)介

文章每周持續(xù)更新,可以微信搜索「 前端大集錦 」第一時(shí)間閱讀,回復(fù)【視頻】【書(shū)籍】領(lǐng)取200G視頻資料和30本PDF書(shū)籍資料

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。