Vue使用render函數渲染組件詳解(使用iView渲染表頭篩選爲例)

此文源碼案例:歡迎Star

目錄

Vue 組件的開發有幾種方式:單文件組件,使用 render 函數渲染,使用 template。

在大多數的情況下,Vue 可以使用單文件/template 的方式來創建頁面;然而在有一些情況我們需要使用 JavaScript 的編程能力,比如使用第三方框架時,想要自定義某個功能;這個時候就可以使用到 render 函數。

本文將使用 iView table 爲例,通過 render 函數添加可搜索的表頭篩選。

一、Render 函數參數詳解

//一個簡單的例子:渲染一個p標籤,內容爲 '我是p標籤的內容'
new Vue({
  render: createElement => createElement("p", "我是p標籤的內容")
});

其實 createElement()還有更強大的參數:

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // 一個 HTML 標籤字符串,組件選項對象,或者
  // 解析上述任何一種的一個 async 異步函數。必需參數。
  "div",

  // {Object}
  // 一個包含模板相關屬性的數據對象,
  // 你可以在 template 中使用這些特性。可選參數。
  {
    // (詳情見下一節)
  },

  // {String | Array}
  // 子虛擬節點 (VNodes),由 `createElement()` 構建而成,
  // 也可以使用字符串來生成“文本虛擬節點”。可選參數。
  [
    "先寫一些文字",
    createElement("h1", "一則頭條"),
    createElement(MyComponent, {
      props: {
        someProp: "foobar"
      }
    })
  ]
);

createElement(obj,{},[])

參數詳解:

  1. 第一個參數爲渲染成哪個節點,接受 String,Object,Function 三種類型;
    • 如果是 String,比如是 div,那麼表示此標籤將會渲染成 div 標籤;String 渲染成普通的 html 標籤
    • 如果是 Object,比如是一個 Vue 的組件:TableFilter,那麼表示此標籤將會渲染一個組件。通常我們在單文件組件中使用的時候是 import {Table} from “iview”;然後在 template 中引用此標籤;而使用 render 則需要使用 createElement 方式創建一個。
    • 如果是 Function,則可以根據自己的業務邏輯動態覺得是渲染成普通的 html 標籤還是 Vue 組件。
  1. 第二個參數接受 一個對象{}類型的數據。其主要作用類似於組件中對某一個節點設置各種 bind 屬性:設置樣式 style,設置事件 on,設置類 class,設置自定義的命令,設置普通的 html 屬性,設置傳遞參數 props 等等

有一點要注意:正如在模板語法中,v-bind:class 和 v-bind:style,會被特別對待一樣,在 VNode 數據對象中,下列屬性名是級別最高的字段。該對象也允許你綁定普通的 HTML 特性,就像 DOM 屬性一樣,比如 innerHTML (這會取代 v-html 指令)。

{
  // 和`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`,當子組件使用$emit()方式發生,使用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
}

你會發現使用 render 函數沒有與 v-model 的直接對應 - 你必須自己實現相應的邏輯:比如

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

  1. 第三個參數爲列表類型的數據,表示當前渲染組件的有哪些子組件。
render(createElement=>
    return createElement(
    'div',
    {
    
    },
    // {String | Array}
    // 子虛擬節點 (VNodes),由 `createElement()` 構建而成,
    // 也可以使用字符串來生成“文本虛擬節點”。可選參數。
    [//
        '先寫一些文字',
        createElement('h1', '一則頭條'),
        createElement(MyComponent, {
        props: {
            someProp: 'foobar'
        }
        })
    ]
    ))

注意:子組件中的每一項(VNodes)都必須是唯一的;意味着,下面的 render function 是無效的:

render: function (createElement) {
  var myParagraphVNode = createElement('p', 'hi')
  return createElement('div', [
    // 錯誤-重複的 VNodes
    myParagraphVNode, myParagraphVNode
  ])
}

二、樣例:如何給表格組件添加表頭篩選

效果圖:

iview的默認table組件不支持 表頭輸入框篩選,官方地址
此處默認您有了一定的vue開發基礎。
套用iview官方的例子:

//顯示錶格的例子
<template>
  <Table border :columns="columns7" :data="data6"></Table>
</template>
<script>
import { Table ,Button,Icon,Modal} from "iview";
import Vue from "vue";
export default {
  components: {
    Table
  },
  data() {
    return {
      columns7: [
        {
          title: "Name",
          key: "name",
          //使用render函數自定義列顯示效果:文本加粗
          render: (h, params) => {
            return h("div", [//使用render渲染一個div標籤
              h(Icon, {//使用render渲染一個iview的組件
                props: {//傳遞參數
                  type: "person"
                }
              }),
              h("strong", params.row.name)//文字加粗
            ]);
          }
        },
        {
          title: "Age",
          key: "age"
        },
        {
          title: "Address",
          key: "address"
        },
        {
          title: "Action",
          key: "action",
          width: 150,
          align: "center",
          render: (h, params) => {
            return h("div", [//渲染一個div標籤
              h(
                Button,//在div標籤下渲染一個iview組件
                {
                  props: {//傳遞參數
                    type: "primary",
                    size: "small"
                  },
                  style: {//設置樣式
                    marginRight: "5px"
                  },
                  on: {//監聽$emit事件
                    click: () => {
                      this.show(params.index);
                    }
                  }
                },
                "View"
              ),
              h(
                Button,
                {
                  props: {
                    type: "error",
                    size: "small"
                  },
                  on: {
                    click: () => {
                      this.remove(params.index);
                    }
                  }
                },
                "Delete"
              )
            ]);
          }
        }
      ],
      data6: [
        {
          name: "John Brown",
          age: 18,
          address: "New York No. 1 Lake Park"
        },
        {
          name: "Jim Green",
          age: 24,
          address: "London No. 1 Lake Park"
        },
        {
          name: "Joe Black",
          age: 30,
          address: "Sydney No. 1 Lake Park"
        },
        {
          name: "Jon Snow",
          age: 26,
          address: "Ottawa No. 2 Lake Park"
        }
      ]
    };
  },
  methods: {
    show(index) {
      this.$Modal.info({
        title: "User Info",
        content: `Name:${this.data6[index].name}<br>Age:${
          this.data6[index].age
        }<br>Address:${this.data6[index].address}`
      });
    },
    remove(index) {
      this.data6.splice(index, 1);
    }
  },

  

  mounted(){
      //modal注入
      Vue.prototype.$Modal=Modal;
  }
  
};
</script>

由於table組件表頭篩選不支持輸入框篩選,那麼我們就必須的自己繪製。
思路如下:

  • 找到表頭所在的節點
  • 在表頭節點後添加一個自定義篩選的div節點;
  • 使用render函數渲染一個下拉輸入的單文件組件
 mounted(){
      //modal注入
      Vue.prototype.$Modal=Modal;
      //等dom元素渲染完成之後渲染篩選
      this.$nextTick(()=>{
        this.renderHeaderFilter();
      })
  }

methods:{
 //添加頭部篩選
    renderHeaderFilter(){
      let allHeader =document.querySelectorAll(".ivu-table-header .ivu-table-cell");
console.log(allHeader);

      allHeader.forEach((element)=>{
        let createNew=document.createElement("div");
        createNew.classList.add("vue-header-filter");
        element.appendChild(createNew);
        new Vue({
          render(h){
            return h(TableHeaderFilter,{
              props:{}
            })
          }
        }).$mount(createNew);
      })
    }
}

最後實現的效果爲:

此文源碼案例:歡迎Star

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章