vue之render基本書寫方法

Vue 推薦在絕大多數狀況下使用 template 來建立你的 HTML。然而在一些場景中,你真的須要 JavaScript 的徹底編程的能力,這就是 render 函數,它比 template 更接近編譯器。html

render 基本寫法

export default {
  name: 'renderTest',
  data() {},
  render: (createElement) => {
    return createElement(tag, data, context)
  },
  props: {},
  methods: {},
  created () {}
  ...
}

Vue 經過創建一個虛擬 DOM 對真實 DOM 發生的變化保持追蹤。
render函數提供了一個參數createElement(能夠簡寫爲h), 用來生成DOM, 其有三個參數:vue

  • 第一個參數: {String | Object | Function}, 必要參數,一個 HTML 標籤字符串,組件選項對象,或者一個返回值類型爲 String/Object 的函數
  • 第二個參數: {Object},可選參數,一個包含模板相關屬性的數據對象,這樣,您能夠在 template 中使用這些屬性。
  • 第三個參數: {String | Array},可選參數, 子節點 (VNodes),由 createElement() 構建而成, 或使用字符串來生成「文本節點」。

數據對象詳解git

{
  // 和`v-bind:class`同樣的 API
  'class': {
    foo: true,
    bar: false
  },
  // 和`v-bind:style`同樣的 API
  style: {
    color: 'red',
    fontSize: '14px'
  },
  // 正常的 HTML 特性
  attrs: {
    id: 'foo'
  },
  // 組件 props
  props: {
    myProp: 'bar'
  },
  // DOM 屬性
  domProps: {
    innerHTML: 'baz'
  },
  // 事件監聽器基於 `on`
  // 因此再也不支持如 `v-on:keyup.enter` 修飾器
  // 須要手動匹配 keyCode。
  on: {
    click: this.clickHandler
  },
  // 僅對於組件,用於監聽原生事件,而不是組件內部使用 `vm.$emit` 觸發的事件。
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 自定義指令。注意,您沒法對綁定中的 `oldValue` 賦值
  // Vue 會爲您持續追蹤
  directives: [
    {
      name: 'my-custom-directive',
      value: '2',
      expression: '1 + 1',
      arg: 'foo',
      modifiers: {
        bar: true
      }
    }
  ],
  // Scoped slots in the form of
  // { name: props => VNode | Array<VNode> }
  scopedSlots: {
    default: props => createElement('span', props.text)
  },
  // 若是組件是其餘組件的子組件,需爲插槽指定名稱
  slot: 'name-of-slot',
  // 其餘特殊頂層屬性
  key: 'myKey',
  ref: 'myRef'
}

v-if 、 v-for 與 v-model
render 函數中沒有與v-if 、 v-for 、v-model 相應的 api, 必須本身來實現相應的邏輯github

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

完整示例
renderTest組件express

export default {
  name: 'renderTest',
  data() {
    return {
    }
  },
  render: function (createElement) {

    return createElement(
      'h' + this.level,
      {
        on: {
          click: this.clickHandler
        }
      },
      [
        createElement('a', {
          attrs: {
            href: '##'
          }
        }, this.$slots.default)
      ]
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  },
  methods: {
    clickHandler () {}
  }
}

使用組件npm

<renderTest :level="1">
  <button> Hello world!</button>
</renderTest>

組件中自定義render(函數式組件)

render組件編程

export default {
  name: 'renderTest',
  functional: true,
  props: {
    render: Function,
    item: String,
    index: Number
  },
  render: (h, ctx) => {
    const params = {
      item: ctx.props.item,
      index: ctx.props.index
    }
    return ctx.props.render(h, params)
  }
}

咱們將組件記爲 functional,這意味它無狀態(沒有 data),無實例(沒有 this 上下文)。
在添加 functional: true 以後,組件的 render 函數之間將增長 ctx 參數
組件須要的一切都是經過 ctx 傳遞,包括:api

  • props: 提供 props 的對象
  • children: VNode 子節點的數組
  • slots: slots 對象
  • data: 傳遞給組件的 data 對象
  • parent: 對父組件的引用

list組件數組

<template>
<ul class="bs-list left" style="width:200px;">
  <li v-for="(item, i) in listDate" class="list-item" :key="i">
    <span>{{item}}</span>
    <renderTest v-if="renderfun" :item="item" :index="i" :render="renderfun"></renderTest>
  </li>
</ul>
</template>
<script>
import renderTest from './renderTest.js'
export default {
  name: 'lists',
  components: { renderTest },
  props: {
    listDate: {
      type: Array
    },
    renderfun: {
      type: Function,
      default() {
        return () => false
      }
    }
  }
}
</script>

使用組件babel

<template>
  <div class="text">
     <lists :listDate="listDate" :renderfun="renderfun"></lists>
  </div> 
</template>
<script>
import lists from './lists.vue'
export default {
  name: 'text',
  components: { lists },
  data() {
    return {
      listDate: [ 'list item 1', 'list item 2', 'list item 3', 'list item 4', 'list item 5' ],
      renderfun: (h, ctx) => {
        return h('div', {
          style: {display: 'inline-block', float: 'right'}
        }, [
          h('button', {
            class: ['btn-primary'],
            on: {
              click: () => {
                this.listDate.splice(ctx.index, 1)
              }
            }
          }, '刪除')
        ])
      }
    }
  }
}
</script>

JSX使用

安裝babel依賴
npm install babel-plugin-syntax-jsx babel-plugin-transform-vue-jsx babel-helper-vue-jsx-merge-props babel-preset-env --save-dev

.babelrc 文件配置

{
  "presets": ["env"],
  "plugins": ["transform-vue-jsx"]
}

書寫格式

render (h) {
  return (
    <div
      // 普通的屬性
      id="foo"
      // DOM 屬性,添加前綴`domProps`
      domPropsInnerHTML="bar"
      // 事件監聽,添加前綴 `on` or `nativeOn`
      onClick={this.clickHandler}
      nativeOnClick={this.nativeClickHandler}
      // 其餘屬性
      class={{ foo: true, bar: false }}
      style={{ color: 'red', fontSize: '14px' }}
      key="key"
      ref="ref"
      slot="slot">
    </div>
  )
}

實例

export default {
  name: 'renderJSX',
  data() {
    return {
      num: 1
    }
  },
  render() {
    const data = {
      class: ['b', 'c']
    }
    return (<div class = 'div'>
    <span>123</span>
      <button onClick={this.clickHandler} class='btn-primary'>btn</button>
      <span class='a' {...data}>{this.num}</span>
    </div>)
  },
  props: {
    level: {
      type: Number
    }
  },
  methods: {
    clickHandler() {
      this.num++
      console.log('clickHandler')
    }
  },
  created() {}
}

https://vuefe.cn/v2/guide/render-function.html
https://github.com/vuejs/babel-plugin-transform-vue-jsx

相關文章
相關標籤/搜索