如何以编程方式创建 Vue.js 插槽?

Posted

技术标签:

【中文标题】如何以编程方式创建 Vue.js 插槽?【英文标题】:How to create Vue.js slot programmatically? 【发布时间】:2018-10-13 12:13:35 【问题描述】:

我有以下带有插槽的组件:

<template>
    <div>
        <h2> someProp </h2>
        <slot></slot>
    </div>
</template>

由于某些原因,我不得不手动实例化这个组件。我就是这样做的:

const Constr = Vue.extend(MyComponent);
const instance = new Constr(
    propsData:  someProp: 'My Heading' 
).$mount(body);

问题是:我无法以编程方式创建插槽内容。到目前为止,我可以创建简单的基于字符串的插槽:

const Constr = Vue.extend(MyComponent);
const instance = new Constr(
    propsData:  someProp: 'My Heading' 
);

// Creating simple slot
instance.$slots.default = ['Hello'];

instance.$mount(body);

问题是 - 如何以编程方式创建 $slots 并将其传递给我正在使用 new 创建的实例?

注意:我没有使用完整版本的 Vue.js(仅限运行时)。所以我没有可用的 Vue.js 编译器来即时编译模板。

【问题讨论】:

我在 Vue.js 应用程序中的单元测试教程中发现,您可以在 Vue.extend 中传递 slot 参数。在此示例中,作者使用 vue-test-utils 但我认为您可以尝试在构造函数中添加 slots 属性。来源:alexjoverm.github.io/2017/10/02/Test-Vue-js-Slots-in-Jest/… 谢谢,@ThomasFerro。我试过这个,但它不起作用。它似乎使用了一些未记录的内部 API。 正如我所说,他正在使用 vue-test-utils。我正在尝试使用Vue.extend() 我不知道这是否是正确的方法,所以我只将其作为评论发布...您可以将 Vnodes 分配给 $slots.default (我在这里找到了此信息@ 987654322@) 并且您可以使用 this.$createElement (vuejs.org/v2/guide/render-function.html) 创建 vnode 我已经修改了 CSS 技巧示例中的示例,该示例显示了如何完成:codesandbox.io/s/jl66zxnwz9 Vue pass slot template to extended component的可能重复 【参考方案1】:

我查看了Vue.js 的 TypeScript 定义文件,并在 Vue 组件实例上发现了一个未记录的函数:$createElement()。我的猜测是,它与传递给组件的render(createElement) 函数的函数相同。所以,我可以解决它:

const Constr = Vue.extend(MyComponent);
const instance = new Constr(
    propsData:  someProp: 'My Heading' 
);

// Creating simple slot
const node = instance.$createElement('div', ['Hello']);
instance.$slots.default = [node];

instance.$mount(body);

但这显然是未记录的,因此是值得怀疑的方法。如果有更好的方法可用,我不会将其标记为已回答。

【讨论】:

这不是一个有问题的方法(或者更确切地说,不是一个私有函数)正如这里所讨论的:github.com/vuejs/vuejs.org/issues/658#issuecomment-267465006【参考方案2】:

(这并不能真正回答How to create Vue.js slot programatically?。但它确实解决了你的问题。)

与使用 $createElement() 相比,此技巧不那么骇人听闻。

基本上,创建一个将MyComponent注册为本地组件的新组件。

const Constr = Vue.extend(
  template: `
  <MyComponent someProp="My Heading">
    <div>slot here !!!</div>
  </MyComponent>
  `,
  components: 
    MyComponent: MyComponent
  
);
const instance = new Constr().$mount('#app');

演示: https://jsfiddle.net/jacobgoh101/shrn26p1/

【讨论】:

它可以工作,但我需要 Vuejs 完整构建(Vue + 编译器)才能工作。是的,使用额外组件的成本感觉不那么骇人听闻。【参考方案3】:

我想我终于偶然发现了一种以编程方式创建插槽元素的方法。据我所知,该方法似乎不适用于功能组件。我不知道为什么。

如果您正在为组件实现自己的渲染方法,您可以使用 createElement 方法(或您在渲染方法中将其别名为的任何内容)以编程方式创建您传递给子元素的插槽,并传递一个数据哈希包括 slot: NAME_OF_YOUR_SLOT 后跟该插槽内的子数组。

例如:

Vue.config.productionTip = false
Vue.config.devtools = false;

Vue.component('parent', 
  render (createElement) 
    return createElement('child', [
      createElement('h1',  slot: 'parent-slot' , 'Parent-provided Named Slot'),
      createElement('h2',  slot: 'default' , 'Parent-provided Default Slot')
    ])
  
)

Vue.component('child', 
  template: '<div><slot name="parent-slot" /><slot /></div>'
)

new Vue(
  el: '#app',
  template: '<parent />'
)
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>

<div id='app'>
</div>

【讨论】:

【参考方案4】:

我刚刚在 vue 论坛看到了一个答案: slots

原理是:没有什么像createElement('slot'..) 相反,有一个渲染函数提供 slotted innerHtml 作为函数: $scopedSlots.default()

用法:

render: function (createElement) 
  const self = this;
  return createElement("div", this.$scopedSlots.default());

如果您想提供默认值以防插槽没有内容,您需要自己编写一个区别并呈现其他内容。 (上面的链接有一个更详细的例子)

该函数返回一个数组,因此它不能用作渲染函数的根。它需要像上面示例中的 div 一样包装到单个容器节点中。

【讨论】:

以上是关于如何以编程方式创建 Vue.js 插槽?的主要内容,如果未能解决你的问题,请参考以下文章

Vue.js 和 Jest - Element-UI 如何以编程方式确认 MessageBox?

您如何以编程方式更新 Qt 中的 UI?

Vue.js 和 vue-phone-number-input 包组件:以编程方式设置国家代码和电话号码

使用编程导航 Vue.js 传递道具

Vue插槽实现原理

如何正确使用 vue js Web 组件内部的插槽并应用样式