vue js 无法理解 keep alive

vue js cant understand keep alive

我正在用这段代码进行测试:
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 是否会重新渲染组件。我看到无论是否用 <keep-alive> 包裹 <component>,alert() 如果仅在我第一次进入“主页”选项卡时调用。所以我有两个问题:
1. keep-alive到底是做什么的?因为看起来无论如何组件只创建一次。
2. vue 是否可以 show/hide 选项卡,而不是替换单个 DOM 元素?但是在显示之前仍然不要加载组件。

您已将 alert() 调用放入异步 returns 组件定义的函数中。只有在第一次访问组件定义时才会调用此函数。

<keep-alive> 标签将缓存一个组件的实例,这样它就不会被破坏并需要重新安装。

使用您的示例,您可以看到每当您切换到 "Home" 选项卡时都会调用 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>

但是,通过将动态组件包装在 <keep-alive> 标记中,您是在告诉 Vue 缓存对 home 路由组件的引用。所以 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 <keep-alive> 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>

您需要用 <keep-alive></keep-alive>

包裹组件标签

我已经更新了你的 Jsfiddle