【问题标题】:Vue - any way to reuse SLOT?Vue - 任何重用 SLOT 的方法?
【发布时间】:2018-01-03 08:25:46
【问题描述】:

我正在使用 Uikit 开发响应式 Vue.js 应用程序。我必须创建 2 个菜单 - 一个用于桌面版本,一个用于移动版本。所以我必须定义相同的菜单项 2 次,第一次用于桌面链接插槽,第二次用于移动。我不想定义它 2x。我怎样才能优雅地处理这个问题?有什么想法吗?

<template id="app">
  <app-layout>

    <template slot="navlinks-desktop">
      <router-link to="/link1" tag="li" exact><a>Link 1</a></router-link>
      <router-link to="/link2" tag="li" exact><a>Link 2</a></router-link>
    </template>

    <template slot="navlinks-mobile">
      <router-link to="/link1" tag="li" exact><a>Link 1</a></router-link>
      <router-link to="/link2" tag="li" exact><a>Link 2</a></router-link>
    </template>

    <transition name="fade" slot="content">
      <router-view></router-view>
    </transition>

    <p slot="footer">Footer text</p>

  </app-layout>
</template>

<template id="app-layout">
  <div class="main-container">

    <header class="uk-margin-bottom">
      <nav class="uk-navbar uk-navbar-attached">
        <div class="uk-navbar-brand uk-hidden-small">My Application</div>
        <div class="uk-navbar-flip">
          <ul class="uk-navbar-nav uk-hidden-small">
            <slot name="navlinks-desktop"></slot>
          </ul>
          <div class="uk-navbar-toggle uk-button-dropdown uk-visible-small uk-dropdown-close" data-uk-dropdown="{mode: 'click'; justify: 'nav'}">
            <div class="uk-dropdown uk-dropdown-navbar uk-dropdown-small">
              <ul class="uk-nav uk-nav-dropdown">
                <slot name="navlinks-mobile"></slot>
              </ul>
            </div>
          </div>
        </div>
        <div class="uk-navbar-brand uk-navbar-center uk-visible-small">My Application</div>
      </nav>
    </header>

    <div class="uk-container uk-container-center">
      <main>              
        <slot name="content"></slot>
      </main>
    </div>

    <footer class="uk-text-center fixed-bottom">
      <slot name="footer"></slot>
    </footer>

  </div>
</template>

编辑:所以...经过一些研究和仔细查看文档后,我必须说,这个特定问题没有解决方案...虽然可以重用插槽,但只需以编程方式呈现它,这是不可能的使用路由器链接重用插槽。

示例 - 没有 hack - 真正的“Vue 方式”重用插槽:

<template id="app">
  <app-layout>

    <template slot="myslot">
      <li>THIS WORKS</li>
      <li>WILL</li>
      <li>WORK</li>
    </template>

  </app-layout>
</template>

<script>
  Vue.component('app-layout', {
    render: function (createElement) {
      var myslot = this.$slots.myslot

      return createElement('div', [
        createElement('ul', myslot),
        createElement('ul', myslot)
      ])
    }
  })
</script>

它简单、优雅、易读。但是,不幸的是,如果您以编程方式呈现组件,则不能使用模板。所以像这样......

<template id="app-layout">
  <div class="main-container">

    <header class="uk-margin-bottom">
      <nav class="uk-navbar uk-navbar-attached">
        <div class="uk-navbar-brand uk-hidden-small">My Application</div>
        <div class="uk-navbar-flip">
          <ul class="uk-navbar-nav uk-hidden-small">
            <slot name="myslot"></slot>
          </ul>
        </div>
      </nav>
    </header>

  </div>
</template>

...你可以扔掉,你必须创建整个结构,使用 createElement 函数以编程方式一一创建,这真的很痛苦且容易出错。是的,你可以使用 JSX,但是你必须使用 Babel 进行编译。这不是我想要的......非工作示例:

<template id="app">
  <app-layout>

    <template slot="navlinks">
      <router-link to: "/">Home</router-link>
      <router-link to: "/products">Products</router-link>
      <router-link to: "/about">About</router-link>
    </template>

  </app-layout>
</template>

<script>
  Vue.component('app-layout', {
    render: function (createElement) {
      var navlinks = this.$slots.navlinks

      return createElement('div', [
        createElement('ul', navlinks),
        createElement('ul', navlinks)
      ])
    }
  })
</script>

在本例中,第二个 UL 将为空。不幸的是,从事实的角度来看,在 Vue 中所有 vNode 都必须是唯一的,这是完全正常的,它将保持为空......

所以结果是我不能使用上面的结构。我不能有模板应用程序布局,我有带有包装元素的模板。最后,我改写成这样:

<template id="app">
  <div class="main-container">

    <header class="uk-margin-bottom">
      <nav class="uk-navbar uk-navbar-attached">
        <div class="uk-navbar-brand uk-hidden-small">My Application</div>
        <div class="uk-navbar-flip">
          <ul class="uk-navbar-nav uk-hidden-small">
            <router-link to="/link1" tag="li" exact><a>Link 1</a></router-link>
            <router-link to="/link2" tag="li" exact><a>Link 2</a></router-link>
          </ul>
          <div class="uk-navbar-toggle uk-button-dropdown uk-visible-small uk-dropdown-close" data-uk-dropdown="{mode: 'click'; justify: 'nav'}">
            <div class="uk-dropdown uk-dropdown-navbar uk-dropdown-small">
              <ul class="uk-nav uk-nav-dropdown">
                <router-link to="/link1" tag="li" exact><a>Link 1</a></router-link>
                <router-link to="/link2" tag="li" exact><a>Link 2</a></router-link>
              </ul>
            </div>
          </div>
        </div>
        <div class="uk-navbar-brand uk-navbar-center uk-visible-small">My Application</div>
      </nav>
    </header>

    <div class="uk-container uk-container-center">
      <main>              
        <transition name="fade" slot="content">
          <router-view></router-view>
        </transition>
      </main>
    </div>

    <footer class="uk-text-center fixed-bottom">
      <p slot="footer">Footer text</p>
    </footer>

  </div>
</template>

没有两个单独的模板,没有插槽。但也没有编程模板渲染。所以我保持我的示例应用程序干净且易于理解。这就是重点......

PS:不过,如果我错了,我会很高兴,有人告诉我如何使用路由器链接重用插槽。

EDIT2:我踢了 Uikit 并使用了 Bulma。这让我可以创建更好、更易读的结构。

<!-- APPLICATION VIEW -->
  <template id="app">
    <app-layout>

      <template slot="navbar-items">
        <router-link to="/" class="navbar-item" exact>Home</router-link>
        <router-link to="/about" class="navbar-item" exact>About</router-link>
      </template>

      <transition name="fade" mode="out-in" slot="content">
        <keep-alive>
          <router-view></router-view>
        </keep-alive>
      </transition>

      <p slot="footer">&copy;2017 Wal De Mar</p>

    </app-layout>
  </template>
<!-- APPLICATION VIEW -->

<!-- TEMPLATE FOR APP VIEW -->
  <template id="app-layout">
    <div class="container">

      <header>
        <nav class="navbar">
          <div class="navbar-brand">
            <a href="/" class="navbar-item">BRAND</a>
            <div class="navbar-burger" data-target="main-menu">
              <span></span>
              <span></span>
              <span></span>
            </div>
          </div>
          <div id="main-menu" class="navbar-menu">
            <div class="navbar-end">
              <slot name="navbar-items"></slot>
            </div>
          </div>
        </nav>
      </header>

      <main>
        <slot name="content"></slot>
      </main>

      <footer>
        <slot name="footer"></slot>
      </footer>

    </div>
  </template>
<!-- TEMPLATE FOR APP VIEW -->

<script>
  Vue.component('app-layout', {
    template: '#app-layout'
  })

  new Vue({
    template: '#app',
    router,
  }).$mount('#app')
</script>

【问题讨论】:

  • 将菜单项构建为自己的组件,然后将组件添加到插槽中?
  • 使用 JavaScript,v-for.
  • V-for?嗯……听起来很有希望……但是怎么做呢?有什么例子吗?或链接到示例?
  • 嗨,navlinks-desktopnavlinks-mobile 看起来完全一样。为什么不只使用一个名为 navlinks 的插槽并在不同的菜单包装器中使用它?
  • 是的,这就是我想要的。但是怎么做?你能重写我的模板吗?

标签: javascript vue.js uikit vuejs2 vue-component


【解决方案1】:

您可以创建一个包含导航链接的单独组件并使用它来代替插槽。

<!-- NavLinks.vue -->
<template>
  <router-link to="/link1" tag="li" exact><a>Link 1</a></router-link>
  <router-link to="/link2" tag="li" exact><a>Link 2</a></router-link>
</template>

<template id="app">
  <app-layout>
    <transition name="fade" slot="content">
      <router-view></router-view>
    </transition>

    <p slot="footer">Footer text</p>

  </app-layout>
</template>

<template id="app-layout">
  <div class="main-container">

    <header class="uk-margin-bottom">
      <nav class="uk-navbar uk-navbar-attached">
        <div class="uk-navbar-brand uk-hidden-small">My Application</div>
        <div class="uk-navbar-flip">
          <ul class="uk-navbar-nav uk-hidden-small">
            <nav-links></nav-links>
          </ul>
          <div class="uk-navbar-toggle uk-button-dropdown uk-visible-small uk-dropdown-close" data-uk-dropdown="{mode: 'click'; justify: 'nav'}">
            <div class="uk-dropdown uk-dropdown-navbar uk-dropdown-small">
              <ul class="uk-nav uk-nav-dropdown">
                <nav-links></nav-links>
              </ul>
            </div>
          </div>
        </div>
        <div class="uk-navbar-brand uk-navbar-center uk-visible-small">My Application</div>
      </nav>
    </header>

    <div class="uk-container uk-container-center">
      <main>              
        <slot name="content"></slot>
      </main>
    </div>

    <footer class="uk-text-center fixed-bottom">
      <slot name="footer"></slot>
    </footer>

  </div>
</template>

【讨论】:

  • 你真的确定吗?
  • >>> 在同一渲染树中发现插槽“navlinks”重复存在 - 这可能会导致渲染错误。
  • @WaldemarIce 我已经更新了我的答案以使用组件而不是插槽。
【解决方案2】:

您可以多次使用命名槽。在下面的 sn-p 中,我有一个组件需要两个插槽,另一个组件向它提供两次相同的插槽。

Vue.component('twoMenus', {
  template: '#two-menu-template'
});

new Vue({
  el: '#app',
  components: {
    slotDoubler: {
      template: '#slot-doubler-template'
    }
  }
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
<div id="app">
  <two-menus>
    <div slot="menu1">I am a menu</div>
    <div slot="menu2">I am a different menu</div>
  </two-menus>
  <hr>
  <slot-doubler>
    <div slot="reusable">This menu appears twice!</div>
  </slot-doubler>
</div>

<template id="two-menu-template">
  <div>
    <div>The first menu is here:
      <slot name="menu1"></slot>
    </div>
    <div>And the second is here:
      <slot name="menu2"></slot>
    </div>
  </div>
</template>

<template id="slot-doubler-template">
  <div>
    <two-menus>
      <div slot="menu1">
        <slot name="reusable"></slot>
      </div>
      <div slot="menu2">
        <slot name="reusable"></slot>
      </div>
    </two-menus>
  </div>
</template>

【讨论】:

    【解决方案3】:

    如果我需要用一个数据填充两个菜单,我会采用另一种更简单、更好的方法。

    您可以拥有一个 javascript 菜单对象并通过 v-for 填充菜单项。您可以完全访问您的 dom 并根据需要操作它。我在下面为你写了一个小演示

    如果您需要复制,可以将 v-for 模板移动到 component 并将菜单对象传递给它

    new Vue({
      el:"#app",
      data : {
        menu : [
          {
            // have each menu item as js object
            text : 'Home',
            to : { name : 'somewhere'},
            href : '/somewhere'
          },
          {
            // you can even have a divider
            divider : true
          },
          {
            text : 'About',
            to : { name : 'somewhere'},
            href : '/somewhere'
          },
          {
            // have each menu item as js object
            text : 'Services',
            to : { name : 'somewhere'},
            href : '/somewhere',
            children : [
              // you can even have child menu
            ]
          },
        ]
      }
    })
    .mobile-menu {
        background : grey;
        border : black solid 3px;
    }
    <script src="https://unpkg.com/vue@2.2.6/dist/vue.js"></script>
    <div id="app">
    
      
      <ul class="desktop-menu"> 
        <template v-for="item in menu">
          <li v-if="item.divider">
            <span class="divider" >--------</span>
          </li>
          <li v-else>
            <router-link :to="item.to" v-text="item.text"></router-link>
          </li>
        </template>
      </ul>
      
      <ul class="mobile-menu"> 
        <template v-for="item in menu">
          <li v-if="item.divider">
            <span class="divider" >--------</span>
          </li>
          <li v-else>
            <router-link :to="item.to" v-text="item.text"></router-link>
          </li>
        </template>
      </ul>
    </div>

    【讨论】:

      【解决方案4】:

      如果我了解您的要求,您可以多次使用同一个插槽。

      只需将同一个插槽复制并粘贴到两个位置

      ```html

      <header class="uk-margin-bottom">
        <nav class="uk-navbar uk-navbar-attached">
          <div class="uk-navbar-brand uk-hidden-small">My Application</div>
          <div class="uk-navbar-flip">
            <ul class="uk-navbar-nav uk-hidden-small">
              <slot name="navlinks-desktop"></slot>
            </ul>
            <div class="uk-navbar-toggle uk-button-dropdown uk-visible-small uk-dropdown-close" data-uk-dropdown="{mode: 'click'; justify: 'nav'}">
              <div class="uk-dropdown uk-dropdown-navbar uk-dropdown-small">
                <ul class="uk-nav uk-nav-dropdown">
              <slot name="navlinks-desktop"></slot>
                </ul>
              </div>
            </div>
          </div>
          <div class="uk-navbar-brand uk-navbar-center uk-visible-small">My Application</div>
        </nav>
      </header>
      
      <div class="uk-container uk-container-center">
        <main>              
          <slot name="content"></slot>
        </main>
      </div>
      
      <footer class="uk-text-center fixed-bottom">
        <slot name="footer"></slot>
      </footer>
      

      ```

      【讨论】:

      • 如果您将路由器链接传递给插槽,它将不起作用。路由器链接将仅在第一个插槽中呈现。
      猜你喜欢
      • 1970-01-01
      • 2017-10-21
      • 1970-01-01
      • 2021-10-30
      • 1970-01-01
      • 2017-08-01
      • 1970-01-01
      • 2020-12-20
      • 1970-01-01
      相关资源
      最近更新 更多