是否可以从单个文件组合将内容添加到全局Vue组件?

时间:2018-11-21 10:12:00

标签: javascript vue.js vuejs2 vue-component

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

此组件非常简单

<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组件中将<v-elements-generator :elements="$store.state.help.helpElements" />替换为VHelp

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

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

然后在我的VElementsGenerator组件中,我具有一个render函数,该函数具有来自诸如

之类的对象的虚拟DOM中的render元素。
<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>

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

因此,我试图找到另一种方法,仅在需要在子组件上使用时,才将内容添加到基本布局中使用的全局组件中。

由于HTML页面体系结构是完全错误的,因此我不能直接在儿童组合中使用此VHelp组件。

我想知道是否可以在不重新创建组件新实例的情况下从单个文件组件向组件插槽添加内容(最好是HTML)?

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

有什么想法吗?

1 个答案:

答案 0 :(得分:0)

在商店中,您应该只存储数据,而不是HTML结构。解决此问题的方法是将v-help组件内容的当前状态存储在商店中。然后,您将有一个带有插槽的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;
  }
}

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