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
我正在用这段代码进行测试:
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