怎么利用render函數對第三方UI組件進行二次封裝


上一篇文章介紹了《怎樣高效地利用第三方UI組件》,以Input組件為例,介紹了一下怎么使用第三方的UI組件。

實話實話,Input組件還是屬於比較簡單的組件,需要我們自己重寫的東西還是比較少的。

如果是Tabs組件呢?

怎么二次封裝Tabs組件

我們先看一下element-ui組件的使用方式(官方示例):

<template>
  <el-tabs v-model="activeName" @tab-click="handleClick">
    <el-tab-pane label="用戶管理" name="first">用戶管理</el-tab-pane>
    <el-tab-pane label="配置管理" name="second">配置管理</el-tab-pane>
    <el-tab-pane label="角色管理" name="third">角色管理</el-tab-pane>
    <el-tab-pane label="定時任務補償" name="fourth">定時任務補償</el-tab-pane>
  </el-tabs>
</template>
<script>
  export default {
    data() {
      return {
        activeName: 'second'
      };
    },
    methods: {
      handleClick(tab, event) {
        console.log(tab, event);
      }
    }
  };
</script>

是不是有點難受了?

並不是我們簡單的把參數傳遞過來,v-bind="$attrs"v-on="$listeners"就能解決的了。

怎么辦?

訴求

我們先看下,二次封裝Tabs的訴求是什么?

我肯定是不希望再寫一堆el-tab-pane的,再配上label, name,那沒啥意義,根本沒有達到減少代碼的目的。

那就涉及到怎么把傳遞過來的$slots轉換成el-tab-pane的內容的問題了。

如果沒有寫過react,也不太了解render函數(確實基本上很少寫render函數),那就有點困難了。

怎么封裝?

事實上,我提供的方案,也是vue官方文檔提供的,利用render函數寫jsx。

上代碼吧!

<script>
let currentTab = null

export default {
  name: "YvTabs",
  model: {
    prop: "value",
    event: "change"
  },
  props: {
    position: {
      type: String,
      default: "top",
      validator: val => ["top", "right", "bottom", "left"].includes(val)
    },
    value: {
      type: String,
      default: ""
    }
  },
  render(h) {
    return (
      <el-tabs
        tab-position={this.position}
        value={this.value}
        onTab-click={this.handleClick}
      >
        {this.$slots.default.map(tab => {
          const {
            data: {
              key,
              attrs: { name, type = null }
            }
          } = tab
          return (
            <el-tab-pane key={key} name={name} lazy>
              {type ? (
                <template slot="label">
                  {type === "icon" ? (
                    <i class={"el-icon-" + key} />
                  ) : (
                    <icon icon={key} />
                  )}
                  &nbsp;{name}
                </template>
              ) : <template slot="label">{name}</template> }
              {tab}
            </el-tab-pane>
          )
        })}
      </el-tabs>
    )
  },
  methods: {
    handleClick(vm) {
      const { name = "" } = vm
      if (name !== currentTab) {
        currentTab = name
        this.$emit("change", name)
      }
    }
  }
}
</script>

需要注意的是,在render函數內,是沒有辦法利用vue自定義的各種指令的,譬如v-model,不過,別難受,不能寫就不寫,v-model僅僅是語法糖,真沒啥。

還有一個就是el-tabs提供的事件tab-click,剛開始我很天真的以為:在jsx內要改成駝峰式的事件名稱,就使用了onTabClick,我想很多人都可能跟我踩一樣的坑的,然而,並不是,這里並不能用onTabClick,而是需要寫成onTab-click,意不意外,驚不驚喜?

剩下的就是對$slots的內容有點約束了,譬如每個DOM節點,需要傳入key、 name、 type等字段,這個可以依據各自需求進行各種自定義。我這里key和name是對應el-tab-pane的屬性label、name使用的,而type屬性,純粹是為了定義icon的,可以使用el-icon,也可以自定義各種圖標字體,僅此而已。

當習慣了render函數進行二次封裝,也知道了v-bind="$attrs", v-on="$listeners",還有什么我們不可以進行封裝的呢?

下面就是各種愉快的玩耍了。

只要在項目開始初期,按照設計圖,自定義一遍各種組件的樣式,剩下的就是各組件相互組合的問題了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM