【问题标题】:Is it possible to add content to a global Vue component from a single file comp?是否可以从单个文件 comp 向全局 Vue 组件添加内容?
【发布时间】:2019-04-23 21:12:24
【问题描述】:

我已经制作了一个全局组件,它将呈现我们想要的内容。

这个组件很简单

<template>
  <section
    id="help"
    class="collapse"
  >
    <div class="container-fluid">
      <slot /> 
    </div>
  </section>
</template>

<script>
  export default {
    name: 'VHelp',
  };
</script>

我在我的基本模板中使用它

<v-help />

我正在尝试使用从另一个单个文件组件向此组件槽添加内容。

<v-help>
  <p>esgssthsrthsrt</p>
</v-help>

但这在逻辑上创建了我的 comp 的另一个实例,其中包含 p 标记。这不是我想做的正确事情。

所以我尝试使用虚拟 DOM 和渲染功能,将 slot 替换为 &lt;v-elements-generator :elements="$store.state.help.helpElements" /&gt; 在我的 VHelp 组合中。

helpElements 商店是一个包含对象的简单数组。

{
   type: 'a',
   config: {
     class: 'btn btn-default',
   },
   nestedElements: [
     {
       type: 'span',
       value: 'example',
     },
     {
       type: 'i',
     },
   ],
},

然后在我的VElementsGenerator comp 中,我有一个渲染函数,该函数在虚拟 DOM 中使用类似对象的渲染元素

<script>
  import {
    cloneDeep,
    isEmpty,
  } from 'lodash';

  export default {
    name: 'VElementsGenerator',
    props: {
      elements: {
        type: Array,
        required: true,
      },
    },
    methods: {
      iterateThroughObject(object, createElement, isNestedElement = false) {
        const generatedElement = [];

        for (const entry of object) {
          const nestedElements = [];
          let elementConfig = {};

          if (typeof entry.config !== 'undefined') {
            elementConfig = cloneDeep(entry.config);
          }

          if (entry.nestedElements) {
            nestedElements.push(this.iterateThroughObject(entry.nestedElements, createElement, true));
          }

          generatedElement.push(createElement(
            entry.type,
            isEmpty(elementConfig) ? entry.value : elementConfig,
            nestedElements
          ));

          if (typeof entry.parentValue !== 'undefined') {
            generatedElement.push(entry.parentValue);
          }
        }

        if (isNestedElement) {
          return generatedElement.length === 1 ? generatedElement[0] : generatedElement;
        }

        return createElement('div', generatedElement);
      },
    },
    render(createElement) {
      if (this.elements) {
        return this.iterateThroughObject(this.elements, createElement);
      }

      return false;
    },
  };
</script>

第二种方法效果很好,但是如果我想渲染复杂的数据,渲染函数内部使用的对象非常长并且读取起来很复杂。

所以我试图找到另一种方法来将内容添加到基础布局中使用的全局组件,仅当我希望在子组件上使用它时。

我不能在子组件中直接使用这个VHelp 组件,因为 HTML 页面架构会完全错误。

我想知道是否可以将内容(最好是 HTML)从单个文件 comp 添加到组件槽,而无需重新创建组件的新实例?

此外,我认为将 HTML 保存为 Vuex 商店中的字符串非常难看。所以我什至不知道这是否可行,以及我是否需要完全改变我尝试这样做的方式。

有什么想法吗?

【问题讨论】:

    标签: javascript vue.js vuejs2 vue-component


    【解决方案1】:

    在商店中,您应该只存储数据而不是 HTML 结构。解决这个问题的方法是将 v-help 组件内容的当前状态存储在 store 中。然后,您将拥有一个带有插槽的 v-help 组件(就像您已经提议的那样)。您应该根据商店中的状态传递不同的内容。这是一个抽象的例子:

    <v-help>
      <content-one v-if="$store.state.content === 'CONTENT_ONE' />
      <content-two v-else-if="$store.state.content === 'CONTENT_TWO' />
      <content-fallback v-else />
    </v-help>
    

    其他地方的子元素:

    <div>
      <button @click="$store.commit('setContentToOne')">Content 1</button>
    </div>
    

    Vuex 商店:

    state: {
      content: null
    },
    mutations: {
      setContentToOne(state) {
        state.content = 'CONTENT_ONE';
      }
    }
    

    当然,这取决于您的要求,尤其是如果这是实现此目标的最佳方式,则取决于使用了多少不同的场景。如果我理解正确,您正在将帮助元素保存到商店。您还可以在其中保存一组当前选定的帮助元素,然后直接在v-help 组件中显示它们。


    编辑

    当然,您也可以只将静态组件(或其名称)保存在商店中。然后,您可以在子组件中动态决定哪些内容显示在v-help 中。这是一个例子:

    <v-help>
      <component :is="$store.state.helpComponent" v-if="$store.state.helpComponent !== null" />
    </v-help>
    

    测试组件:

    <template>
      test component
    </template>
    
    <script>
    export default {
      name: 'test-component'
    };
    </script>
    

    其他地方的子元素(变体1,将名称存储在Vuex中)

    <div>
      <button @click="$store.commit('setHelpComponent', 'test-component')">Set v-help component to 'test-component'</button>
    </div>
    

    其他地方的子元素(变体2,将整个组件存储在Vuex中)

    <template>
      <button @click="$store.commit('setHelpComponent', testComponent)">Set v-help component to testComponent (imported)</button>
    </template>
    
    <script>
    import TestComponent from '@/components/TestComponent';
    
    export default {
      name: 'some-child-component',
      computed: {
        testComponent() {
          return TestComponent;
        }
      }
    };
    </script>
    

    其他地方的子元素(变体 3,存储名称,从导入的组件派生,在 Vuex 中;我会使用这个变体

    <template>
      <button @click="$store.commit('setHelpComponent', testComponentName)">Set v-help component to 'test-component'</button>
    </template>
    
    <script>
    import TestComponent from '@/components/TestComponent';
    
    export default {
      name: 'some-child-component',
      computed: {
        testComponentName() {
          return TestComponent.name;
        }
      }
    };
    </script>
    

    Vuex 商店:

    state: {
      helpComponent: null
    },
    mutations: {
      setHelpComponent(state, value) {
        state.helpComponent = value;
      }
    }
    

    另请参阅动态组件的文档(&lt;component :is=""&gt; 语法):https://vuejs.org/v2/guide/components.html#Dynamic-Components

    【讨论】:

    • 是的,如果我在 v-help 中有一个默认内容,这将运行良好。但是我所有的东西都可以在v-help 里面,我不能提前知道什么会传递给这个组件。每个页面上的内容都是独一无二的,这就是为什么我认为我不能使用您解释的方式。
    • 好的,这些内容从何而来?来自服务器请求还是静态 vue 组件?
    • 这是在 vue 组件上定义的静态内容
    • @Jérôme 如果是静态组件,需要在子组件中决定应该使用哪个组件,可以将组件的名称存储在 store 中。我已经添加了一个例子。
    猜你喜欢
    • 2021-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-03
    • 2020-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多