【问题标题】:Is there a vue.js equivalent of ngTemplateOutlet?是否有 ngTemplateOutlet 的 vue.js 等价物?
【发布时间】:2019-04-05 01:33:59
【问题描述】:

vue.js 是否与 Angular 的 *ngTemplateOutlet 指令等效?假设我有一些这样定义的组件:

    <template>
        <div id="independentComponent">
            Hello, {{firstName}}!
        </div>
    </template>
    <script>
        export default {
            name: "independentComponent",
            props: ['firstName']
        }
    </script>

    ...

    <template>
        <div id="someChildComponent">
            <slot></slot>
            <span>Let's get started.</span>
        </div>
    </template>
    <script>
        export default {
            name: "someChildComponent"
        }
    </script>

我希望能够做这样的事情:

<template>
    <div id="parentComponent">
        <template #indepdentInstance>
            <independentComponent :firstName="firstName" />
        </template>
        <someChildComponent>
            <template #indepdentInstance></template>
        </someChildComponent>
    </div>
</template>
<script>
    export default {
        name: "parentComponent",
        components: {
            someChildComponent,
            independentComponent
        },
        data() {
            return {
                firstName: "Bob"
            }
        }
    }
</script>

在 Angular 中,我可以使用

<div id="parentComponent">
    <someChildComponent>
        <ng-container *ngTemplateOutlet="independentInstance"></ng-container>
    </someChildComponent>

    <ng-template #independentInstance>
        <independentComponent [firstName]="firstName"></independentComponent>
    </ng-template>
</div>

但看起来 Vue 要求将元素写入 DOM 中的模板位置。有什么方法可以引用内联元素并使用它作为插槽传递给另一个组件?

【问题讨论】:

  • 很多时候不可能按照另一个框架的确切原则在一个框架中编写解决方案。你到底想在这里完成什么?我敢肯定会有一种 vue 的方式来做到这一点。

标签: javascript vue.js transclusion


【解决方案1】:

你不能重用像ngTemplateOutlet这样的模板,但可以结合$refsv-pre和运行时模板编译的思想与v-runtime-template来实现这一点。

首先,创建可重复使用的模板(&lt;ng-template #independentInstance&gt;):

<div ref="independentInstance" v-show="false">
    <template v-pre> <!-- v-pre disable compiling content of template -->
        <div> <!-- We need this div, because only one root element allowed in templates -->
            <h2>Reusable template</h2>
            <input type="text" v-model="testContext.readWriteVar">
            <input type="text" v-model="readOnlyVar">
            <progress-bar></progress-bar>
        </div>
    </template>
</div>

现在,您可以重复使用independentInstance 模板:

<v-runtime-template
    :template="$refs.independentInstance.innerHTML"
    v-if="$refs.independentInstance">
</v-runtime-template>

但请记住,您不能从 independentInstancetemplate 内部修改 readOnlyVar - vue 会警告您:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "readOnlyVar"

但是你可以将它包装在object 中,它会起作用:

@Component({
    components: {
        VRuntimeTemplate
    }
})
export default class ObjectList extends Vue {
    reusableContext = {
        readWriteVar: '...'
    };

    readOnlyVar = '...';
}

【讨论】:

  • 这似乎很有趣,但有些不适合我。
【解决方案2】:

您可以尝试由 Vue 核心团队成员 LinusBorg 编写的 Portal vue

PortalVue 是一组由两个组件组成的组件,它们允许您渲染一个 文档中任何位置的组件模板(或其中的一部分) - 甚至 在您的 Vue 应用程序控制的部分之外!

示例代码:

<template>
  <div id="parentComponent">
    <portal to="independentInstance">
      <!-- This slot content will be rendered wherever the <portal-target>
           with name 'independentInstance' is located. -->
      <independent-component :first-name="firstName" />
    </portal>
    
    <some-child-component>
        <portal-target name="independentInstance">
          <!--
          This component can be located anywhere in your App.
          The slot content of the above portal component will be rendered here.
          -->
        </portal-target>
    </some-child-component>
  </div>
</template>

还有一个vue-simple-portal由同一作者编写,它更小,但将组件安装到正文元素的末尾。

【讨论】:

  • 链接不可用
【解决方案3】:

X 模板

使用x-template。在index.html 文件中定义一个脚本标签。

x-template 然后可以在模板定义内的多个组件中引用为#my-template

运行 sn-p 作为示例。

有关x-templates 的更多信息,请参阅 Vue.js 文档。

Vue.component('my-firstname', {
  template: '#my-template',
  data() {
    return {
      label: 'First name'
    }
  }
});

Vue.component('my-lastname', {
  template: '#my-template',
  data() {
    return {
      label: 'Last name'
    }
  }
});

new Vue({
  el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <my-firstname></my-firstname>
  <my-lastname></my-lastname>
</div>

<script type="text/x-template" id="my-template">
  <div>
    <label>{{ label }}</label>
    <input />
  </div>
</script>

【讨论】:

    【解决方案4】:

    @NekitoSP 的回答给了我一个解决方案的想法。我已经实现了下面的示例。它对我有用。也许您想将其用作带有 props 的自定义组件。

    关键字:#named #template #vue

    <template>
      <div class="container">
        <div ref="templateRef" v-if="false">write here your template content and add v-if for hide in current place</div>
        ....some other contents goes here
        <p v-html="getTemplate('templateRef')"></p>
      </div>
    </template>
    
    
    <script lang="ts">
      import Vue from 'vue';
      Vue.extend({
        methods:{
          getTemplate(tempRef){
            return this.$refs[tempRef].innerHTML
          }
        }
      })
    </script>
    
    

    【讨论】:

      【解决方案5】:

      我不太确定我是否理解您的问题,但如果我想在一个模板中添加两个组件,我会尝试为您提供一些我会选择做的事情。

      HeaderSection.vue

          <template>
              <div id="header_id" :style="'background:'+my_color">
                  welcome to my blog
              </div>
          </template>
          <script>
              export default {
      
                  props: ['my_color']
              }
          </script>
      

      BodySection.vue

          <template>
              <div id="body_id">
                  body section here
              </div>
          </template>
          <script>
              export default {
      
              }
          </script>
      

      home.vue

      <template>
      
          <div id="parentComponent">
      
              <header-section :color="my_color" />
      
              <body-section />
          </div>
      </template>
      <script>
          import HeaderSection from "./components/HeaderSection.vue"
          import BodySection from "./components/BodySection.vue"
          export default {
      
              name: "home",
      
              components: {
                  HeaderSection,
                  BodySection
              },
      
              data() {
                  return {
                      my_color: "red"
                  }
              }
          }
      </script>
      

      【讨论】:

      • 这不是问题的答案。您演示了父子组件的使用。问题是关于如何使用常见的 html 标记而不在同一个组件中复制而不创建组件。
      猜你喜欢
      • 2021-03-29
      • 2011-01-16
      • 2020-09-20
      • 2011-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多