简体   繁体   English

Vue-重用SLOT的任何方式?

[英]Vue - any way to reuse SLOT?

Im working on responsive Vue.js app with Uikit. 我正在使用Uikit开发响应式Vue.js应用程序。 I must create 2 menus - one for desktop version, one for mobile. 我必须创建2个菜单-一个用于桌面版本,一个用于移动设备。 So I must define same menu items 2 times, first for desktop links slot, second time for mobile. 因此,我必须两次定义相同的菜单项,第一次是针对桌面链接插槽,第二次是针对移动设备。 I dont want define it 2x. 我不想将其定义为2x。 How can I deal with this problem elegantly? 我该如何优雅地处理这个问题? Any ideas? 有任何想法吗?

<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>

EDIT: So... After some research and closer look to documentation, I must say, that this specific problem DOES NOT HAVE SOLUTION... Although it is possible to reuse slots, just render it programatically, IT IS NOT POSSIBLE TO REUSE SLOT WITH ROUTER LINK. 编辑:所以...经过一番研究并仔细研究了文档,我必须说,这个特定的问题没有解决方法...尽管可以重复使用插槽,但是只能以编程方式呈现它,所以不可能重新使用插槽与路由器链接。

Example - no hacks - true "Vue way" to reuse slots: 示例-没有技巧-重复使用插槽的真正“方法”:

<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>

Its simple, elegant and readable. 它简单,优雅且可读。 But, unfortunately, if you render component programatically, you cant use templates. 但是,不幸的是,如果以编程方式渲染组件,则无法使用模板。 So something like this... 像这样

<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>

...you can throw away and you must create whole structure, one by one programatically with createElement function, which is really painfull and error prone. ...您可以扔掉,必须使用createElement函数以编程的方式逐一创建整个结构,这确实很痛苦并且容易出错。 Yes, you can use JSX, but then you must transpile it with Babel. 是的,您可以使用JSX,但是必须先将其与Babel一起转换。 And this is not what I want... Non working example: 这不是我想要的...非工作示例:

<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>

In this example, second UL will be empty. 在此示例中,第二个UL将为空。 And unfortunately, from the fact perspective that in Vue all vNodes must be unique, it is perfectly normal, that it will remain empty... 不幸的是,从事实的角度来看,在Vue中,所有vNode都必须是唯一的,这是完全正常的,它将保持为空...

So the result is that I can not use structure as above. 所以结果是我不能使用上面的结构。 I cant have template app-layout for which I have template with wrapping elements. 我无法使用模板应用程序布局,而模板中没有包含包装元素的模板。 Finally, I rewrote it this way: 最后,我将其重写为:

<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>

No two separate templates, no slots. 没有两个单独的模板,没有插槽。 But no programmatical template rendering also. 但是也没有程序化模板渲染。 So I kept my example application clean and easy to understand. 因此,我使示例应用程序保持简洁并易于理解。 And that was the point... 这就是重点...

PS: STILL, I WILL BE VERY HAPPY, IF I AM WRONG AND SOMEONE SHOW ME HOW TO REUSE SLOT WITH ROUTER LINK. PS:仍然,如果我很差劲,如果有人告诉我如何使用ROUTER LINK重新使用广告位,我会非常高兴。

EDIT2: I kicked Uikit and used Bulma instead. EDIT2:我踢了Uikit,改用布尔玛。 This allows me to create better, more readable structure. 这使我可以创建更好,更易读的结构。

<!-- 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>

You could create a separate component that contains the navigation links and use it instead of the slots. 您可以创建一个包含导航链接的单独组件,并使用它而不是插槽。

<!-- 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>

You can use a named slot more than once. 您可以多次使用命名插槽。 In the snippet below, I have a component that takes two slots and another component that feeds it the same slot twice. 在下面的代码段中,我有一个占用两个插槽的组件,另一个将相同的插槽馈入两次的组件。

 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> 

if i need to populate two menus with one data i would go with another much easier and good approach. 如果我需要用一个数据填充两个菜单,我会选择另一种简单易行的方法。

you can have a javascript menu object and via v-for populate the menu items. 您可以使用一个javascript菜单对象,并通过v-for填充菜单项。 you can have full access to your dom and manipulate it as you wish. 您可以完全访问自己的dom,并根据需要进行操作。 i have written a little demo for you below 我在下面为你写了一个小样

if you need to duplicate you can move the v-for template to a component and pass the menu object to it 如果您需要复制,可以将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> 

If i understand your requirement you can use the same slot any number of time. 如果我了解您的要求,则您可以多次使用同一插槽。

just copy and paste the same slot in two places 只需在两个位置复制并粘贴相同的插槽

```html 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>

``` ```

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM