vue js cant understand keep alive

北城以北 提交于 2019-12-08 06:46:33

问题


I'm doing tests with this code:
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>

And the js part

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]
  }
})

I did the alert() test to see whether vue will re-render the component. I see that with or without wraping <component> with <keep-alive>, the alert() if called only the first time I enter the Home tab. So I have two questions:
1. What exactly keep-alive does? cause it seems that anyway the component is created only once.
2. Is it possible for vue to show/hide the tabs, instead of replacing single DOM element? But still not to load the component until it is shown.


回答1:


You've put the alert() call in the function that asynchronously returns the component definition. This function will only get called the first time the definition of the component is accessed.

The <keep-alive> tag will cache an instance of a component so that it does not get destroyed and need to be mounted again.

Using your example, you can see that the mounted hook gets called any time you switch to the "Home" tab:

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>

But, by wrapping the dynamic component in a <keep-alive> tag, you are telling Vue to cache a reference to the home route's component. So the mounted hook only gets called the first time the component is instantiated:

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.


As for your second question: if you want to add all of the tabs to the DOM initially and simply hide the inactive tab components, then render each tab using the v-for directive, and use the v-show directive to only show the active tab.

Here's an example:

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>

And, if I understand your last sentence correctly, if you really want to not create a tab component until the tab is initially active, but then want to hide the tab's HTML content when another tab is active, you'd need to keep track of which tabs have been activated in a data property and then use the v-if directive to initially prevent the component from initializing.

Something like this:

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>



回答2:


You need to wrap the component tag with the <keep-alive></keep-alive>

I have updated your Jsfiddle



来源:https://stackoverflow.com/questions/49517443/vue-js-cant-understand-keep-alive

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!