【问题标题】:Vue 2.5 - Slots / Slot scope - Encapsulate a library component into a custom componentVue 2.5 - Slots / Slot scope - 将库组件封装成自定义组件
【发布时间】:2023-03-06 18:10:01
【问题描述】:

我正在使用 vue 2.5 和库 Bootstrap-vue。 我对这个库的表格组件很感兴趣:https://bootstrap-vue.js.org/docs/components/table

但是我想封装这个组件以使用我不想重复的自定义配置来制作我自己的组件。这样我就不必每次都管理分页和过滤,并且可以添加其他功能,例如数据导出。

所以我创建了一个表格助手组件(现在只处理分页)

<template>
    <div>
        <b-table striped hover responsive
                 :items="items" :fields="fields"
                 :current-page="currentPage" per-page="10">
            <slot></slot>
        </b-table>
        <b-pagination :total-rows="items.length" per-page="10" v-model="currentPage"></b-pagination>
    </div>
</template>

<script>
    import bTable from 'bootstrap-vue/es/components/table/table'
    import bPagination from 'bootstrap-vue/es/components/pagination/pagination'

    export default {
        name: "table-helper",
        props: ['items', 'fields'],
        data() {
            return {
                currentPage: 1,
            }
        },
        components: {
            'b-table': bTable,
            'b-pagination': bPagination
        }
    }
</script>

我想像这样使用我的组件(使用 bootstrap-vue 插槽重新格式化列):

<table-helper :items="users" :fields="fields">
    <template slot="fullName" slot-scope="data">
        {{data.item.first_name}} {{data.item.last_name}}
    </template>
</table-helper>

显然它不起作用(我得到了表格但没有格式化列),因为&lt;template slot="fullName" slot-scope="data"&gt; 指的是我的自定义组件而不是 b-table 组件。

所以我想知道一种封装库组件的方法,它使用像这样的插槽和插槽范围。

感谢您的帮助。

【问题讨论】:

    标签: vuejs2


    【解决方案1】:

    要点:

    1. 模板 vs JSX:您可能必须使用 JSX 来实现它,尤其是对于 slot

    2. 插槽:对于您的情况,父组件的插槽将是b-table 的子 VNode,因此将插槽重塑为一个数组并将上下文从父更改为当前(如果不是scopedSlot会渲染不正确。),然后将它们放入function=h的第三个参数中(或者你可以调用createElement)。详情请查看Vue Guide: Create Element Arguments

    不管怎样,仔细阅读Best Way To Implement HOC,应该就能达到目的了。

    Vue.config.productionTip = false
    
    Vue.component('table-helper', {
      render (h) {
        const slots = Object.keys(this.$slots)
          .reduce((arr, key) => arr.concat(this.$slots[key]), [])
          .map(vnode => {
            vnode.context = this._self
            return vnode
          })
        const self = this
        
        return h('div', [
          h('b-table', {
            on: self.$listeners,
            props: Object.assign(self.$props, {currentPage: self.currentPage}),
            scopedSlots: self.$scopedSlots,
            attrs: self.$attrs
          },slots),
          h('b-pagination', {
            props: self.$props,
            domProps: {
              value: self.currentPage
            },
            on: {
              input: function (event) {
                self.currentPage = event
              }
            }
          })
        ])
      },
      //mixins: [{bTable.props}, {bPagination.props}],
      props: ['items', 'fields', 'perPage','totaRows'],
      data() {
        return {
          currentPage: 1,
        }
      }
    })
    
    new Vue({
      el: '#app',
      data() {
        return {
          fields: [ 'first_name', 'last_name', 'age', 'fullName' ],
          users: [
            { isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
            { isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
            { isActive: false, age: 89, first_name: 'Geneva', last_name: 'Wilson' },
            { isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' },
            { isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
            { isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
            { isActive: false, age: 89, first_name: 'Geneva', last_name: 'Wilson' },
            { isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' },
            { isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
            { isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
            { isActive: false, age: 89, first_name: 'Geneva', last_name: 'Wilson' },
            { isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' }
          ]
        }
      }
    })
    <link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
    <script src="//unpkg.com/babel-polyfill@latest/dist/polyfill.min.js"></script>
    <script src="https://unpkg.com/bootstrap-vue/dist/bootstrap-vue.js"></script>
    
    
    <div id="app">
      <table-helper :items="users" :fields="fields" :per-page="10" :total-rows="users.length">
          <template slot="fullName" slot-scope="data">
              {{data.item.first_name}} {{data.item.last_name}}
          </template>
      </table-helper>
    </div>

    【讨论】:

    • 有没有办法用 JSX 做到这一点? h('b-pagination', {props: self.$props} 在 JSX 中有等价物吗?
    • 你是在说这个babel-plugin-transform-vue-jsx
    • 我的意思是我想在 JSX 中做这样的事情以获得更漂亮的代码:&lt;b-table on={self.$listeners} props={Object.assign(self.$props, {currentPage: self.currentPage, filter: self.filter})} scopedSlots={self.$scopedSlots} attrs={self.$attrs}&gt; &lt;/b-table&gt;
    猜你喜欢
    • 1970-01-01
    • 2019-10-04
    • 2020-02-05
    • 2021-07-26
    • 2022-12-03
    • 2019-01-03
    • 1970-01-01
    • 2018-05-13
    • 1970-01-01
    相关资源
    最近更新 更多