【问题标题】:vue js cant understand keep alivevue js 看不懂keep alive
【发布时间】:2018-09-06 03:09:27
【问题描述】:

我正在使用以下代码进行测试:
https://jsfiddle.net/b2qj69o1/25/

  <button
    v-for="tab in tabs"
    v-bind:key="tab.name"
    v-bind:class="['tab-button', { active: currentTab.name === tab.name }]"
    v-on:click="currentTab = tab"
  >{{ tab.name }}</button>

  <component
    v-bind:is="currentTab.component"
    class="tab"
  ></component>

还有js部分

var tabs = [
  {
    name: 'Home', 
    component: function() { 
    alert();  // test
    return new Promise((resolve, reject) => resolve({ 
      template: '<div>Home component</div>' 
    }))
    }
  },
  {
    name: 'Posts',
    component: {
      template: '<div>Posts component</div>'
    }
  },
  {
    name: 'Archive',
    component: {
      template: '<div>Archive component</div>',
    }
  }
]
new Vue({
  el: '#dynamic-component-demo',
  data: {
    tabs: tabs,
    currentTab: tabs[1]
  }
})

我做了 alert() 测试看看 vue 是否会重新渲染组件。我看到无论是否使用&lt;keep-alive&gt; 包装&lt;component&gt;,如果仅在我第一次进入主页选项卡时调用alert()。所以我有两个问题:
1. keep-alive到底是做什么的?因为似乎无论如何该组件只创建一次。
2. vue 是否可以显示/隐藏选项卡,而不是替换单个 DOM 元素?但是在组件显示出来之前还是不要加载它。

【问题讨论】:

    标签: javascript vue.js components keep-alive


    【解决方案1】:

    您已将alert() 调用放入异步返回组件定义的函数中。这个函数只会在第一次访问组件的定义时被调用。

    &lt;keep-alive&gt; 标签会缓存一个组件的实例,这样它就不会被破坏而需要重新挂载。

    使用您的示例,您可以看到每当您切换到“主页”选项卡时都会调用 mounted 挂钩:

    var tabs = [
      {
        name: 'Home', 
        component: function() { 
        return new Promise((resolve, reject) => resolve({ 
          template: '<div>Home component</div>',
          mounted() {
            console.log('mounted')
          }
        }))
        }
      },
      {
        name: 'Posts',
        component: {
          template: '<div>Posts component</div>'
        }
      },
      {
        name: 'Archive',
        component: {
          template: '<div>Archive component</div>',
        }
      }
    ]
    
    new Vue({
      el: '#dynamic-component-demo',
      data: {
      	tabs: tabs,
        currentTab: tabs[1]
      }
    })
    .tab-button {
      padding: 6px 10px;
      border-top-left-radius: 3px;
      border-top-right-radius: 3px;
      border: 1px solid #ccc;
      cursor: pointer;
      background: #f0f0f0;
      margin-bottom: -1px;
      margin-right: -1px;
    }
    .tab-button:hover {
      background: #e0e0e0;
    }
    .tab-button.active {
      background: #e0e0e0;
    }
    .tab {
      border: 1px solid #ccc;
      padding: 10px;
    }
    <script src="https://unpkg.com/vue"></script>
    
    <div id="dynamic-component-demo" class="demo">
      <button
        v-for="tab in tabs"
        v-bind:key="tab.name"
        v-bind:class="['tab-button', { active: currentTab.name === tab.name }]"
        v-on:click="currentTab = tab"
      >{{ tab.name }}</button>
    
      <component
        v-bind:is="currentTab.component"
        class="tab"    
      ></component>
    </div>

    但是,通过将动态组件包装在 &lt;keep-alive&gt; 标记中,您是在告诉 Vue 缓存对主路由组件的引用。所以mounted 钩子只会在组件第一次实例化时被调用:

    var tabs = [
      {
        name: 'Home', 
        component: function() { 
        return new Promise((resolve, reject) => resolve({ 
          template: '<div>Home component</div>',
          mounted() {
            console.log('mounted')
          }
        }))
        }
      },
      {
        name: 'Posts',
        component: {
          template: '<div>Posts component</div>'
        }
      },
      {
        name: 'Archive',
        component: {
          template: '<div>Archive component</div>',
        }
      }
    ]
    
    new Vue({
      el: '#dynamic-component-demo',
      data: {
      	tabs: tabs,
        currentTab: tabs[1]
      }
    })
    .tab-button {
      padding: 6px 10px;
      border-top-left-radius: 3px;
      border-top-right-radius: 3px;
      border: 1px solid #ccc;
      cursor: pointer;
      background: #f0f0f0;
      margin-bottom: -1px;
      margin-right: -1px;
    }
    .tab-button:hover {
      background: #e0e0e0;
    }
    .tab-button.active {
      background: #e0e0e0;
    }
    .tab {
      border: 1px solid #ccc;
      padding: 10px;
    }
    <script src="https://unpkg.com/vue"></script>
    
    <div id="dynamic-component-demo" class="demo">
      <button
        v-for="tab in tabs"
        v-bind:key="tab.name"
        v-bind:class="['tab-button', { active: currentTab.name === tab.name }]"
        v-on:click="currentTab = tab"
      >{{ tab.name }}</button>
    
      <keep-alive>
        <component
          v-bind:is="currentTab.component"
          class="tab"    
        ></component>
      </keep-alive>
    </div>

    Here's the documentation on using the &lt;keep-alive&gt; tag with dynamic components.


    关于你的第二个问题:如果你想将所有选项卡添加到 DOM 并简单地隐藏非活动选项卡组件,然后使用 v-for 指令渲染每个选项卡,并使用 v-show 指令仅显示活动标签。

    这是一个例子:

    var tabs = [
      {
        name: 'Home', 
        component: function() { 
        return new Promise((resolve, reject) => resolve({ 
          template: '<div>Home component</div>',
        }))
        }
      },
      {
        name: 'Posts',
        component: {
          template: '<div>Posts component</div>'
        }
      },
      {
        name: 'Archive',
        component: {
          template: '<div>Archive component</div>',
        }
      }
    ]
    
    new Vue({
      el: '#dynamic-component-demo',
      data: {
      	tabs: tabs,
        currentTab: tabs[1]
      }
    })
    .tab-button {
      padding: 6px 10px;
      border-top-left-radius: 3px;
      border-top-right-radius: 3px;
      border: 1px solid #ccc;
      cursor: pointer;
      background: #f0f0f0;
      margin-bottom: -1px;
      margin-right: -1px;
    }
    .tab-button:hover {
      background: #e0e0e0;
    }
    .tab-button.active {
      background: #e0e0e0;
    }
    .tab {
      border: 1px solid #ccc;
      padding: 10px;
    }
    <script src="https://unpkg.com/vue"></script>
    
    <div id="dynamic-component-demo" class="demo">
      <button
        v-for="tab in tabs"
        v-bind:key="tab.name"
        v-bind:class="['tab-button', { active: currentTab.name === tab.name }]"
        v-on:click="currentTab = tab"
      >{{ tab.name }}</button>
    
      <component
        v-for="tab in tabs"
        v-bind:key="'component-' + tab.name"
        v-bind:is="tab.component"
        class="tab"
        v-show="currentTab === tab"
      ></component>
    </div>

    而且,如果我正确理解了您的最后一句话,如果您真的想在选项卡最初处于活动状态之前不创建选项卡组件,但是想要在另一个选项卡处于活动状态时隐藏选项卡的 HTML 内容,您需要跟踪在数据属性中激活了哪些选项卡,然后使用v-if 指令最初阻止组件初始化。

    类似这样的:

    var tabs = [
      {
        name: 'Home', 
        component: function() { 
        return new Promise((resolve, reject) => resolve({ 
          template: '<div>Home component</div>',
        }))
        }
      },
      {
        name: 'Posts',
        component: {
          template: '<div>Posts component</div>'
        }
      },
      {
        name: 'Archive',
        component: {
          template: '<div>Archive component</div>',
        }
      }
    ]
    
    new Vue({
      el: '#dynamic-component-demo',
      data: {
      	tabs: tabs,
        currentTab: tabs[1],
        activatedTabs: {}
      },
      watch: {
        currentTab: {
          immediate: true,
          handler(tab) {
            this.$set(this.activatedTabs, tab.name, true);
          }
        }
      }
    })
    .tab-button {
      padding: 6px 10px;
      border-top-left-radius: 3px;
      border-top-right-radius: 3px;
      border: 1px solid #ccc;
      cursor: pointer;
      background: #f0f0f0;
      margin-bottom: -1px;
      margin-right: -1px;
    }
    .tab-button:hover {
      background: #e0e0e0;
    }
    .tab-button.active {
      background: #e0e0e0;
    }
    .tab {
      border: 1px solid #ccc;
      padding: 10px;
    }
    <script src="https://unpkg.com/vue"></script>
    
    <div id="dynamic-component-demo" class="demo">
      <button
        v-for="tab in tabs"
        v-bind:key="tab.name"
        v-bind:class="['tab-button', { active: currentTab.name === tab.name }]"
        v-on:click="currentTab = tab"
      >{{ tab.name }}</button>
    
      <component
        v-for="tab in tabs"
        v-if="activatedTabs[tab.name]"
        v-bind:key="'component-' + tab.name"
        v-bind:is="tab.component"
        class="tab"
        v-show="currentTab === tab"
      ></component>
    </div>

    【讨论】:

    • 谢谢,我的第二个问题呢,即显示/隐藏选项卡而不是从 DOM 中删除它们
    • 对不起,我最初误解了你的问题。请参阅我的更新答案。
    • 谢谢!作品。我认为将 v-if 与 v-show 一起使用有点奇怪
    • 这很奇怪,我只会在使用 &lt;keep-alive&gt; 时这样做,如果由于某种原因我真的需要 html 在组件的选项卡之一处于非活动状态时挂起。
    【解决方案2】:

    你需要用&lt;keep-alive&gt;&lt;/keep-alive&gt;包裹组件标签

    我已经更新了你的Jsfiddle

    【讨论】:

    • 这两个问题都没有回答。
    猜你喜欢
    • 2016-09-07
    • 1970-01-01
    • 2018-03-31
    • 2020-08-25
    • 2023-03-07
    • 2021-07-20
    • 1970-01-01
    • 2019-12-16
    • 2013-03-25
    相关资源
    最近更新 更多