如何封装/包装一个 VueJS 组件?

Posted

技术标签:

【中文标题】如何封装/包装一个 VueJS 组件?【英文标题】:How to encapsulate / wrap a VueJS component? 【发布时间】:2018-07-26 05:06:27 【问题描述】:

大家好,请原谅我的英语:-)

我有一个可以采用动态插槽的 Vue 组件(插槽的名称将取决于道具)。

我在几个地方使用它,并且一些插槽总是存在的。

为了避免冗余,我正在寻找一种方法来创建一个“包装”最终组件的组件,以允许我只定义附加插槽。

如果有一个“明显”的方法来实现它,我可能会错过它:-)

代码示例

没有“包装组件”

<b-table
  show-empty
  small
  hover

  [...some others and always present props...]

  :items="aDataVarThatWillChangeBasedOnTheContext"

  [...some others and uniq props...]
>
  <template slot="same-1">
   A slot that will always be present with the same content (for example, a checkbox in the first column)
  </template>

  <template slot="same-2">
   A slot that will always be present with the same content (for example, some action buttons in the last column)
  </template>

  [...some others and always present slots...]

  <template slot="not-the-same">
   A slot that is only used in this context (for example, a duration based on a row timestamp and a timestamp picked by the user)
  </template>

  [...some others and uniq slots...]
</b-table>

带有“包装组件”

<my-b-table
  :items="aDataVarThatWillChangeBasedOnTheContext"
>
  <template slot="not-the-same">
   A slot that is only used in this context (for example, a duration based on a row timestamp and a timestamp picked by the user)
  </template>
</my-b-table>

注意:动态插槽名称不可预测。 如果我突然需要一个“foo”列,我应该能够传递一个“foo”插槽(在我的情况下是一个“HEAD_foo”插槽)

一些研究

我读到here:

它们(功能组件)作为包装组件也非常有用。例如,当您需要:

以编程方式选择其他几个组件之一来委派给 在将子组件、道具或数据传递给子组件之前对其进行操作

而且“在将子组件、道具或数据传递给子组件之前对其进行操作”似乎正是我所需要的。

我查看了渲染函数,但很多东西似乎没有实现,比如 v-model,我很难弄清楚如何传递动态插槽......

提前感谢您的回答!

up:在 07.03.2018 我仍然不知道如何解决这个案子

【问题讨论】:

【参考方案1】:

找到了那个月前我还不清楚的答案。

(“动态”在这里的意思是“不是由组件明确声明,而是由父级给出”)

包装组件

props 和 scoped slots 可以由 createElement 函数的 options 对象动态提供。

“简单”插槽可以由createElement函数的childs数组动态给出。

包装组件

除非组件具有功能性,否则道具不能是动态的。

插槽始终可以动态检索。

只有在组件不起作用时才能检索作用域插槽。

结论

不可能同时拥有动态道具和作用域插槽...

但是可以声明所有需要的道具,然后使用“非功能性”组件作为包装器和包装器。


如何

从非功能组件中检索

var component = Vue.component('component-name', 
  props: ['name', 'of', 'the', 'props'],

  // [...]

  aMethod: function () 
    this._props // all the declared props
    this.$slots // all the slots
    this.$scopedSlots // all the scoped slots
  
);

从功能组件中检索

var component = Vue.component('component-name', 
  functional: true,

  render: function (createElement, context) 
    context.props // all the props
    context.children // all the slots as an array
    context.slots() // all the slots as an object
  
);

给子组件

var component = Vue.component('component-name', 
  render: function (createElement) 
    return createElement(
      childComponent,
      
        props: propsToGive,
        scopedSlots: scopedSlotsToGive
      ,
      [
        // non-scoped slots to give
        createElement('slot-tag-name', slot: 'slot-name')
      ]
    );
  
);

参考文献

https://vuejs.org/v2/guide/render-function.html

https://vuejs.org/v2/guide/render-function.html#createElement-Arguments

https://vuejs.org/v2/guide/render-function.html#Functional-Components


沙盒

https://jsfiddle.net/5umk7p52/

【讨论】:

【参考方案2】:

只需从您自定义的&lt;b-table&gt; 中制作一个常规组件即可。

您需要为您的组件定义一个items 属性,以作为&lt;b-table&gt; 组件的items 传递。

而且,要为您的组件定义slot,您需要使用&lt;slot&gt; 标记,并使用name 属性指定名称。

如果您想让&lt;b-table&gt; 组件中的一个插槽可以在&lt;my-b-table&gt; 组件中访问,只需将&lt;slot&gt; 标记作为自定义组件中插槽的内容传递。

看起来像这样:

Vue.component('my-b-table', 
  template: `
    <b-table
      show-empty
      small
      hover
      :items="items"  
    >
      <template slot="same-1">
        Content to pass to the b-table's slot
      </template>

      <slot name="not-the-same">
        A slot that is only used in this context
      </slot>

      <template slot="last_edit">
        <slot name="last_edit">
          A slot to pass content to the b-table component's slot
        </slot>
      </template>
    </b-table>
  `,
  props:  items: Array ,
);

【讨论】:

遗憾的是,这并不简单:使用 b-table 组件,您可以使用插槽以特定方式显示特定列。例如:如果您有一个名为“last_edit”的列,您可以使用这样的插槽:``` ``` 因为它是一个“动态”插槽,所以我想将它们“交给”b-table 子组件 我不确定我是否遵循。您的意思是您希望该插槽也出现在您的 &lt;my-b-table&gt; 组件中? 是的,我需要能够为 b-table 通过 my-b-table 提供一个插槽 困难在于我事先不知道要通过 my-b-table 组件的插槽名称,因为它们在每种用法中都会有所不同:&lt;template slot="&lt;I-dont-know-for-now&gt;"&gt; &lt;slot name="&lt;I-dont-know-for-now&gt;"&gt; A slot to pass content to the b-table component's slot &lt;/slot&gt; &lt;/template&gt; 也许书籍页面会显示一个日期,并且文章页面将显示价格,两个“页面组件”都应该能够使用 my-b-table 来避免显示复选框、id 和其他相同字段的冗余,并且都应该能够描述如何处理特定字段 您能否将您的所有要求添加为您的问题的编辑,我会尝试看看是否可以提出不同的答案?

以上是关于如何封装/包装一个 VueJS 组件?的主要内容,如果未能解决你的问题,请参考以下文章

Vuejs基础之:组件

Vuejs组件

滴滴饿了么的都选用的前端框架Vuejs饿了么基于Vuejs还封装了一整套前端组件,并开源了!

vuejs 单文件组件.vue 文件

vuejs的组件化开发中怎么自定义class覆盖原有样式?

如何在 vuejs 中使用循环包装 2 个或更多 html 元素