Vue Render函數(shù)

前置知識(shí):vm.$slots

  • 類型{ [name: string]: ?Array<VNode> }

  • 只讀

  • 響應(yīng)性:否

  • 詳細(xì)

    用來訪問被插槽分發(fā)的內(nèi)容。每個(gè)具名插槽有其相應(yīng)的 property (例如:v-slot:foo 中的內(nèi)容將會(huì)在 vm.$slots.foo 中被找到)。default property 包括了所有沒有被包含在具名插槽中的節(jié)點(diǎn),或 v-slot:default 的內(nèi)容。

    請(qǐng)注意插槽不是響應(yīng)性的。如果你需要一個(gè)組件可以在被傳入的數(shù)據(jù)發(fā)生變化時(shí)重渲染,我們建議改變策略,依賴諸如 propsdata 等響應(yīng)性實(shí)例選項(xiàng)。

    注意:v-slot:foo 在 2.6 以上的版本才支持。對(duì)于之前的版本,你可以使用廢棄了的語法。

    在使用渲染函數(shù)書寫一個(gè)組件時(shí),訪問 vm.$slots 最有幫助。

<blog-post>
  <template v-slot:header>
    <h1>About Me</h1>
  </template>

  <p>Here's some page content, which will be included in vm.$slots.default, because it's not inside a named slot.</p>

  <template v-slot:footer>
    <p>Copyright 2016 Evan You</p>
  </template>

  <p>If I have some content down here, it will also be included in vm.$slots.default.</p>.
</blog-post>

Vue.component('blog-post', {
  render: function (createElement) {
    var header = this.$slots.header
    var body   = this.$slots.default
    var footer = this.$slots.footer
    return createElement('div', [
      createElement('header', header),
      createElement('main', body),
      createElement('footer', footer)
    ])
  }
})

節(jié)點(diǎn)、樹以及虛擬 DOM

createElement 參數(shù)
// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一個(gè) HTML 標(biāo)簽名、組件選項(xiàng)對(duì)象,或者
  // resolve 了上述任何一種的一個(gè) async 函數(shù)。必填項(xiàng)。
  'div',

  // {Object}
  // 一個(gè)與模板中 attribute 對(duì)應(yīng)的數(shù)據(jù)對(duì)象。可選。
  {
    // (詳情見下一節(jié))
  },

  // {String | Array}
  // 子級(jí)虛擬節(jié)點(diǎn) (VNodes),由 `createElement()` 構(gòu)建而成,
  // 也可以使用字符串來生成“文本虛擬節(jié)點(diǎn)”。可選。
  [
    '先寫一些文字',
    createElement('h1', '一則頭條'),
    createElement(MyComponent, { 
// vnode數(shù)據(jù)對(duì)象的屬性,props,data,methods等等,也可以是HTML attribute
      props: {
        someProp: 'foobar'
      }
    })
  ]
)

有一點(diǎn)要注意:正如 v-bind:class 和 v-bind:style 在模板語法中會(huì)被特別對(duì)待一樣,它們?cè)?VNode 數(shù)據(jù)對(duì)象中也有對(duì)應(yīng)的頂層字段。該對(duì)象也允許你綁定普通的 HTML attribute,也允許綁定如 innerHTML 這樣的 DOM property (這會(huì)覆蓋 v-html 指令)。

{
  // 與 `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 attribute
  attrs: {
    id: 'foo'
  },
  // 組件 prop
  props: {
    myProp: 'bar'
  },
  // DOM property
  domProps: {
    innerHTML: 'baz'
  },
  // 事件監(jiān)聽器在 `on` 內(nèi),
  // 但不再支持如 `v-on:keyup.enter` 這樣的修飾器。
  // 需要在處理函數(shù)中手動(dòng)檢查 keyCode。
  on: {
    click: this.clickHandler
  },
  // 僅用于組件,用于監(jiān)聽原生事件,而不是組件內(nèi)部使用
  // `vm.$emit` 觸發(fā)的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 自定義指令。注意,你無法對(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)
  },
  // 如果組件是其它組件的子組件,需為插槽指定名稱
  slot: 'name-of-slot',
  // 其它特殊頂層 property
  key: 'myKey',
  ref: 'myRef',
  // 如果你在渲染函數(shù)中給多個(gè)元素都應(yīng)用了相同的 ref 名,
  // 那么 `$refs.myRef` 會(huì)變成一個(gè)數(shù)組。
  refInFor: true
}

下面是使用render創(chuàng)建的組件示例:

var getChildrenTextContent = function (children) {
  return children.map(function (node) {
    return node.children
      ? getChildrenTextContent(node.children)
      : node.text
  }).join('')
}

Vue.component('anchored-heading', {
  render: function (createElement) {
    // 創(chuàng)建 kebab-case 風(fēng)格的 ID
    var headingId = getChildrenTextContent(this.$slots.default)
      .toLowerCase()
      .replace(/\W+/g, '-')
      .replace(/(^-|-$)/g, '')

    return createElement(
      'h' + this.level,
      [
        createElement('a', {
          attrs: {
            name: headingId,
            href: '#' + headingId
          }
        }, this.$slots.default)
      ]
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})
VNode 必須唯一

組件樹中的所有 VNode 必須是唯一的。這意味著,下面的渲染函數(shù)是不合法的:

render: function (createElement) {
  var myParagraphVNode = createElement('p', 'hi')
  return createElement('div', [
    // 錯(cuò)誤 - 重復(fù)的 VNode
    myParagraphVNode, myParagraphVNode
  ])
}

如果你真的需要重復(fù)很多次的元素/組件,你可以使用工廠函數(shù)來實(shí)現(xiàn)。例如,下面這渲染函數(shù)用完全合法的方式渲染了 20 個(gè)相同的段落:

render: function (createElement) {
  return createElement('div',
    Array.apply(null, { length: 20 }).map(function () {
      return createElement('p', 'hi')
    })
  )
}

使用 JavaScript 代替模板功能

v-ifv-for

只要在原生的 JavaScript 中可以輕松完成的操作,Vue 的渲染函數(shù)就不會(huì)提供專有的替代方法。比如,在模板中使用的 v-if 和 v-for:

<ul v-if="items.length">
  <li v-for="item in items">{{ item.name }}</li>
</ul>
<p v-else>No items found.</p>

這些都可以在渲染函數(shù)中用 JavaScript 的 if/else 和 map 來重寫:

props: ['items'],
render: function (createElement) {
  if (this.items.length) {
    return createElement('ul', this.items.map(function (item) {
      return createElement('li', item.name)
    }))
  } else {
    return createElement('p', 'No items found.')
  }
}

v-model

渲染函數(shù)中沒有與 v-model 的直接對(duì)應(yīng)——你必須自己實(shí)現(xiàn)相應(yīng)的邏輯:

props: ['value'],
render: function (createElement) {
  var self = this
  return createElement('input', {
    domProps: {
      value: self.value
    },
    on: {
      input: function (event) {
        self.$emit('input', event.target.value)
      }
    }
  })
}

事件 & 按鍵修飾符

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

on: {
  '!click': this.doThisInCapturingMode,
  '~keyup': this.doThisOnce,
  '~!mouseover': this.doThisOnceInCapturingMode
}

對(duì)于有些修飾符,私有前綴都不是必須的,因?yàn)槟憧梢栽谑录幚砗瘮?shù)中使用事件方法:

on: {
  keyup: function (event) {
    // 如果觸發(fā)事件的元素不是事件綁定的元素
    // 則返回
    if (event.target !== event.currentTarget) return
    // 如果按下去的不是 enter 鍵或者
    // 沒有同時(shí)按下 shift 鍵
    // 則返回
    if (!event.shiftKey || event.keyCode !== 13) return
    // 阻止 事件冒泡
    event.stopPropagation()
    // 阻止該元素默認(rèn)的 keyup 事件
    event.preventDefault()
    // ...
  }
}

插槽

你可以通過 this.$slots 訪問靜態(tài)插槽的內(nèi)容,每個(gè)插槽都是一個(gè) VNode 數(shù)組:

render: function (createElement) {
  // `<div><slot></slot></div>`
  return createElement('div', this.$slots.default)
}

也可以通過 this.$scopedSlots 訪問作用域插槽,每個(gè)作用域插槽都是一個(gè)返回若干 VNode 的函數(shù):

props: ['message'],
render: function (createElement) {
  // `<div><slot :text="message"></slot></div>`
  return createElement('div', [
    this.$scopedSlots.default({
      text: this.message
    })
  ])
}

如果要用渲染函數(shù)向子組件中傳遞作用域插槽,可以利用 VNode 數(shù)據(jù)對(duì)象中的 scopedSlots 字段:

render: function (createElement) {
  // `<div><child v-slot="props"><span>{{ props.text }}</span></child></div>`
  return createElement('div', [
    createElement('child', {
      // 在數(shù)據(jù)對(duì)象中傳遞 `scopedSlots`
      // 格式為 { name: props => VNode | Array<VNode> }
      scopedSlots: {
        default: function (props) {
          return createElement('span', props.text)
        }
      }
    })
  ])
}

以上示例均來自vue2官網(wǎng)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容