vue3動(dòng)態(tài)路由+動(dòng)態(tài)組件+緩存應(yīng)用方式
Vue3 案例復(fù)現(xiàn)(動(dòng)態(tài)注冊(cè)組件及路由重定向)
1. 項(xiàng)目結(jié)構(gòu)
假設(shè)項(xiàng)目有src目錄,src目錄下包含views(存放組件)、router(存放路由相關(guān)文件)和store(用于狀態(tài)管理,這里假設(shè)使用 Vuex)。
2. Vuex 存儲(chǔ)相關(guān)信息(src/store/index.js)
import { createStore } from 'vuex';
const store = createStore({
state: {
userRole: null, // 存儲(chǔ)用戶角色,如 'user' 或 'admin'
permissions: [] // 存儲(chǔ)用戶權(quán)限列表
},
mutations: {
SET_USER_ROLE(state, role) {
state.userRole = role;
},
SET_PERMISSIONS(state, perms) {
state.permissions = perms;
}
},
actions: {
setUserRole({ commit }, role) {
commit('SET_USER_ROLE', role);
},
setPermissions({ commit }, perms) {
commit('SET_PERMISSIONS', perms);
}
}
});
export default store;3. 動(dòng)態(tài)注冊(cè)組件示例(src/router/index.js)
import { createRouter, createWebHistory } from 'vue-router';
import store from '../store';
import Home from '../views/Home.vue';
import Login from '../views/Login.vue';
// 動(dòng)態(tài)導(dǎo)入組件函數(shù)
const loadComponent = (path) => () => import(`../views/${path}.vue`);
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
redirect: '/login'
},
{
path: '/login',
component: Login
},
{
path: '/home',
component: Home
}
]
});
// 模擬獲取用戶角色和權(quán)限后動(dòng)態(tài)注冊(cè)路由
const updateRoutes = () => {
const userRole = store.state.userRole;
const newRoutes = [];
if (userRole === 'admin') {
newRoutes.push({
path: '/admin/dashboard',
component: loadComponent('AdminDashboard')
});
}
if (userRole === 'user') {
newRoutes.push({
path: '/user/profile',
component: loadComponent('UserProfile')
});
}
router.addRoute(...newRoutes);
};
// 路由守衛(wèi),在每次路由變化前檢查用戶角色和權(quán)限,更新路由
router.beforeEach((to, from, next) => {
const userRole = store.state.userRole;
if (!userRole && to.path!== '/login') {
next('/login');
} else {
if (userRole) {
updateRoutes();
}
next();
}
});
export default router;4. 登錄組件(src/views/Login.vue)
<template>
<div>
<h2>Login</h2>
<input type="text" v-model="username" placeholder="Username" />
<input type="password" v-model="password" placeholder="Password" />
<button @click="login">Login</button>
</div>
</template>
<script>
import { useStore } from 'vuex';
export default {
data() {
return {
username: '',
password: ''
};
},
methods: {
login() {
const store = useStore();
// 模擬登錄驗(yàn)證,這里簡(jiǎn)單假設(shè)用戶名為 'admin' 時(shí)是管理員角色
if (this.username === 'admin') {
store.dispatch('setUserRole', 'admin');
store.dispatch('setPermissions', ['admin:dashboard']);
} else {
store.dispatch('setUserRole', 'user');
store.dispatch('setPermissions', ['user:profile']);
}
}
}
};
</script>在這個(gè)示例中:
- 通過(guò) Vuex 存儲(chǔ)用戶角色和權(quán)限信息。
- 在路由模塊中,
loadComponent函數(shù)用于動(dòng)態(tài)導(dǎo)入組件。updateRoutes函數(shù)根據(jù)用戶角色動(dòng)態(tài)添加路由。 - 路由守衛(wèi)
beforeEach在每次路由變化前檢查用戶狀態(tài),如果用戶未登錄且不是訪問(wèn)登錄頁(yè),則重定向到登錄頁(yè)。如果用戶已登錄,則根據(jù)用戶角色更新路由,實(shí)現(xiàn)動(dòng)態(tài)注冊(cè)組件和動(dòng)態(tài)控制路由重定向。實(shí)際應(yīng)用中,可以從后端獲取真實(shí)的用戶角色和權(quán)限數(shù)據(jù)。
動(dòng)態(tài)組件
動(dòng)態(tài)組件基礎(chǔ)概念
- 在Vue 3中,動(dòng)態(tài)組件允許你根據(jù)數(shù)據(jù)的變化動(dòng)態(tài)地切換顯示的組件。
- 這是通過(guò)
<component>標(biāo)簽來(lái)實(shí)現(xiàn)的,它有一個(gè)特殊的屬性:is,這個(gè)屬性的值可以是一個(gè)組件選項(xiàng)對(duì)象(例如,通過(guò)import導(dǎo)入的組件)或者組件的名字(如果組件是通過(guò)app.component()方法全局注冊(cè)的)。
簡(jiǎn)單的動(dòng)態(tài)組件示例
- 創(chuàng)建組件
首先,在src/views目錄下創(chuàng)建幾個(gè)組件,例如Home.vue、Profile.vue和Admin.vue。
以Home.vue為例:
<template>
<div>
<h2>Home Page</h2>
</div>
</template>
<script>
export default {
name: 'Home'
};
</script>- 在父組件中使用動(dòng)態(tài)組件
在src/App.vue(假設(shè)這是父組件)中使用動(dòng)態(tài)組件:
<template>
<div>
<component :is="currentComponent"></component>
<button @click="changeComponent('Home')">Go to Home</button>
<button @click="changeComponent('Profile')">Go to Profile</button>
<button @click="changeComponent('Admin')">Go to Admin</button>
</div>
</template>
<script>
import Home from './views/Home.vue';
import Profile from './views/Profile.vue';
import Admin from './views/Admin.vue';
import { ref } from 'vue';
export default {
setup() {
const components = {
Home,
Profile,
Admin
};
const currentComponent = ref('Home');
const changeComponent = (componentName) => {
currentComponent.value = componentName;
};
return {
currentComponent,
changeComponent
};
}
};
</script>在這個(gè)示例中,currentComponent是一個(gè)ref,它存儲(chǔ)了當(dāng)前要顯示的組件的名字。
changeComponent函數(shù)用于根據(jù)按鈕點(diǎn)擊事件來(lái)改變currentComponent的值,從而切換顯示的組件。<component :is="currentComponent"></component>會(huì)根據(jù)currentComponent的值動(dòng)態(tài)地渲染相應(yīng)的組件。
結(jié)合路由使用動(dòng)態(tài)組件(動(dòng)態(tài)路由組件)
- 路由配置
在src/router/index.js中配置路由,假設(shè)你已經(jīng)安裝并配置了vue - router:
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import Profile from '../views/Profile.vue';
import Admin from '../views/Admin.vue';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/profile',
component: Profile
},
{
path: '/admin',
component: Admin
}
]
});
export default router;在路由組件中使用動(dòng)態(tài)組件(嵌套動(dòng)態(tài)組件)
假設(shè)在Profile.vue組件中,你還想根據(jù)用戶的不同設(shè)置(例如用戶的不同狀態(tài)或者權(quán)限)動(dòng)態(tài)地顯示內(nèi)部組件。首先,在src/views目錄下創(chuàng)建UserProfile.vue和CompanyProfile.vue組件。
然后在Profile.vue中使用動(dòng)態(tài)組件:
<template>
<div>
<h2>Profile Page</h2>
<component :is="innerComponent"></component>
<button @click="changeInnerComponent('UserProfile')">User Profile</button>
<button @click="changeInnerComponent('CompanyProfile')">Company Profile</button>
</div>
</template>
<script>
import UserProfile from './UserProfile.vue';
import CompanyProfile from './CompanyProfile.vue';
import { ref } from 'vue';
export default {
setup() {
const innerComponents = {
UserProfile,
CompanyProfile
};
const innerComponent = ref('UserProfile');
const changeInnerComponent = (componentName) => {
innerComponent.value = componentName;
};
return {
innerComponent,
changeInnerComponent
};
}
};
</script>- 這樣,當(dāng)用戶訪問(wèn)
/profile路由時(shí),會(huì)先顯示Profile.vue組件,然后在Profile.vue內(nèi)部,又可以根據(jù)用戶操作動(dòng)態(tài)地顯示UserProfile.vue或者CompanyProfile.vue組件。
動(dòng)態(tài)加載組件(異步組件)
- 原理
對(duì)于大型應(yīng)用或者有性能優(yōu)化需求的場(chǎng)景,你可能不希望一次性加載所有組件,而是在需要的時(shí)候再加載。
Vue 3支持異步加載組件,通過(guò)import()函數(shù)來(lái)實(shí)現(xiàn)。import()函數(shù)返回一個(gè)Promise,當(dāng)Promise被解決時(shí),組件就被加載完成。
- 示例
在src/router/index.js中修改路由配置,以Admin.vue為例,將其改為異步加載:
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import Profile from '../views/Profile.vue';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/profile',
component: Profile
},
{
path: '/admin',
component: () => import('../views/Admin.vue')
}
]
});
export default router;這樣,Admin.vue組件只有在用戶訪問(wèn)/admin路由時(shí)才會(huì)被加載,減少了初始加載時(shí)間和資源占用。同時(shí),你也可以在組件內(nèi)部結(jié)合動(dòng)態(tài)組件和異步加載,實(shí)現(xiàn)更復(fù)雜的動(dòng)態(tài)組件設(shè)置。
vue3 中動(dòng)態(tài)路由 應(yīng)用
Vue 3 動(dòng)態(tài)路由應(yīng)用場(chǎng)景
用戶信息展示
- 場(chǎng)景描述:當(dāng)有一個(gè)用戶管理系統(tǒng),需要查看每個(gè)用戶的詳細(xì)信息。不同用戶有不同的用戶 ID,通過(guò)動(dòng)態(tài)路由可以根據(jù)用戶 ID 加載特定用戶的資料頁(yè)面。
- 示例:路由可以定義為
/user/:id,其中:id是動(dòng)態(tài)參數(shù)。在用戶列表頁(yè)面,每個(gè)用戶的鏈接可以是/user/1、/user/2等,點(diǎn)擊鏈接后會(huì)跳轉(zhuǎn)到對(duì)應(yīng)的用戶詳情頁(yè)面,頁(yè)面根據(jù)傳入的id從后端獲取并展示該用戶的信息。
分類內(nèi)容展示
- 場(chǎng)景描述:在一個(gè)博客系統(tǒng)或者電商系統(tǒng)中,有不同的分類,每個(gè)分類下有多個(gè)內(nèi)容項(xiàng)。通過(guò)動(dòng)態(tài)路由可以根據(jù)分類 ID 或名稱來(lái)展示該分類下的內(nèi)容。
- 示例:比如電商系統(tǒng)中,路由
/category/:categoryName,可以有/category/electronics、/category/clothing等。點(diǎn)擊這些鏈接后,對(duì)應(yīng)的商品列表頁(yè)面會(huì)根據(jù)傳入的分類名稱從數(shù)據(jù)庫(kù)中獲取并展示該分類下的商品。
多語(yǔ)言支持
- 場(chǎng)景描述:網(wǎng)站需要支持多種語(yǔ)言,根據(jù)不同的語(yǔ)言代碼加載相應(yīng)的語(yǔ)言包和頁(yè)面內(nèi)容。
- 示例:路由
/lang/:languageCode,如/lang/en、/lang/zh,頁(yè)面組件根據(jù)languageCode動(dòng)態(tài)加載對(duì)應(yīng)的語(yǔ)言文本資源和顯示相應(yīng)的界面布局。
Vue 3 動(dòng)態(tài)路由實(shí)例
項(xiàng)目準(zhǔn)備
- 創(chuàng)建一個(gè) Vue 3 項(xiàng)目,可以使用
vue - cli或者vite等工具。 - 假設(shè)項(xiàng)目結(jié)構(gòu)有
src目錄,src下有views(存放頁(yè)面組件)、router(存放路由相關(guān)文件)。
路由配置(src/router/index.js)
import { createRouter, createWebHistory } from 'vue-router';
import UserProfile from '../views/UserProfile.vue';
import ProductList from '../views/ProductList.vue';
import NotFound from '../views/NotFound.vue';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/user/:id',
component: UserProfile,
props: true
},
{
path: '/product/:category',
component: ProductList,
props: true
},
{
path: '/:pathMatch(.*)*',
component: NotFound
}
]
});
export default router;用戶資料組件(src/views/UserProfile.vue)
<template>
<div>
<h2>User Profile for ID: {{ id }}</h2>
<!-- 這里可以根據(jù) id 從后端獲取用戶數(shù)據(jù)并展示,比如姓名、年齡等信息 -->
</div>
</template>
<script>
export default {
props: ['id'],
setup(props) {
return {
id: props.id
};
}
};
</script>產(chǎn)品列表組件(src/views/ProductList.vue)
<template>
<div>
<h2>Products in Category: {{ category }}</h2>
<!-- 根據(jù) category 從后端獲取商品列表并展示 -->
</div>
</template>
<script>
export default {
props: ['category'],
setup(props) {
return {
category: props.category
};
}
};
</script>404 組件(src/views/NotFound.vue)
<template>
<div>
<h2>404 - Page Not Found</h2>
</div>
</template>在App.vue中使用路由鏈接(src/App.vue)
<template>
<div id="app">
<router-link :to="{ name: 'userProfile', params: { id: 1 }}">User 1 Profile</router-link>
<router-link :to="{ name: 'userProfile', params: { id: 2 }}">User 2 Profile</router-link>
<br>
<router-link :to="{ name: 'productList', params: { category: 'electronics' }}">Electronics Products</router-link>
<router-link :to="{ name: 'productList', params: { category: 'clothing' }}">Clothing Products</router-link>
<router-view></router-view>
</div>
</template>在這個(gè)實(shí)例中:
- 路由配置了兩個(gè)動(dòng)態(tài)路由
/user/:id和/product/:category,分別用于用戶資料展示和產(chǎn)品列表展示。 - 對(duì)應(yīng)的
UserProfile和ProductList組件通過(guò)props接收動(dòng)態(tài)參數(shù),并可以在組件內(nèi)部進(jìn)行進(jìn)一步操作。 App.vue中使用router - link創(chuàng)建了指向不同動(dòng)態(tài)路由的鏈接,方便用戶導(dǎo)航。同時(shí),還有一個(gè)404頁(yè)面用于處理未匹配的路由。
removeRoute 以及 hasRoute
removeRoute應(yīng)用場(chǎng)景及實(shí)例
應(yīng)用場(chǎng)景
- 權(quán)限變更:當(dāng)用戶的權(quán)限發(fā)生變化,某些路由不再可用時(shí),需要從路由表中移除這些路由。例如,用戶從管理員權(quán)限降級(jí)為普通用戶權(quán)限,之前管理員權(quán)限下的特定路由(如系統(tǒng)設(shè)置、用戶管理等路由)需要被移除。
- 模塊卸載:在一個(gè)復(fù)雜的單頁(yè)應(yīng)用中,如果有一些可插拔的模塊,當(dāng)這些模塊被卸載時(shí),相關(guān)的路由也應(yīng)該被移除。比如一個(gè)電商應(yīng)用中的促銷活動(dòng)模塊,活動(dòng)結(jié)束后,相關(guān)的促銷活動(dòng)路由(如
/promotion/:id)應(yīng)該被移除。
實(shí)例
- 假設(shè)我們有一個(gè)簡(jiǎn)單的應(yīng)用,包含一個(gè)管理員路由
/admin/dashboard,用戶最初以管理員身份登錄,后來(lái)權(quán)限變更為普通用戶。 - 在
src/router/index.js中,路由配置如下:
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import AdminDashboard from '../views/AdminDashboard.vue';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/admin/dashboard',
component: AdminDashboard
}
]
});
export default router;- 在
src/store/index.js(假設(shè)使用Vuex來(lái)管理狀態(tài))中,當(dāng)用戶權(quán)限變更時(shí),觸發(fā)removeAdminRoute動(dòng)作:
import { createStore } from 'vuex';
export default createStore({
state: {
userRole: 'admin'
},
mutations: {
CHANGE_USER_ROLE(state, role) {
state.userRole = role;
}
},
actions: {
removeAdminRoute({ state, commit }, newRole) {
const router = require('../router/index.js').default;
if (state.userRole === 'admin' && newRole!== 'admin') {
router.removeRoute('adminDashboard');
commit('CHANGE_USER_ROLE', newRole);
}
}
}
});- 在
src/App.vue(或其他合適的地方),模擬權(quán)限變更:
<template>
<div>
<button @click="changeUserRole('user')">Change to User Role</button>
<router - view></router - view>
</div>
</template>
<script>
import { useStore } from 'vuex';
export default {
setup() {
const store = useStore();
const changeUserRole = (role) => {
store.dispatch('removeAdminRoute', role);
};
return {
changeUserRole
};
}
};
</script>hasRoute應(yīng)用場(chǎng)景及實(shí)例
應(yīng)用場(chǎng)景:
- 路由檢查與導(dǎo)航引導(dǎo):在進(jìn)行頁(yè)面導(dǎo)航之前,可以使用
hasRoute來(lái)檢查目標(biāo)路由是否存在。這在復(fù)雜的路由嵌套或者動(dòng)態(tài)添加/刪除路由的場(chǎng)景中非常有用。例如,在一個(gè)多模塊應(yīng)用中,一個(gè)模塊可能會(huì)動(dòng)態(tài)添加一些路由,另一個(gè)模塊在導(dǎo)航時(shí)需要確認(rèn)這些路由是否已經(jīng)添加,以避免出現(xiàn)404錯(cuò)誤。 - 權(quán)限檢查與路由隱藏:除了在導(dǎo)航過(guò)程中檢查路由是否存在,還可以結(jié)合用戶權(quán)限來(lái)檢查是否有特定的路由。如果用戶沒(méi)有訪問(wèn)某個(gè)路由的權(quán)限,并且該路由也不存在于當(dāng)前路由表中(可能已經(jīng)被移除),可以在界面上隱藏相關(guān)的導(dǎo)航鏈接,提供更好的用戶體驗(yàn)。
實(shí)例:
- 假設(shè)我們有一個(gè)應(yīng)用,有兩個(gè)模塊:
ModuleA和ModuleB。ModuleA會(huì)動(dòng)態(tài)添加一個(gè)路由/moduleA/special,ModuleB在導(dǎo)航到/moduleA/special之前需要檢查該路由是否存在。 - 在
src/router/index.js中,初始路由配置:
import { createRouter, createWebHistory } from 'vue-router';
import Home from '../views/Home.vue';
import ModuleA from '../views/ModuleA.vue';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/moduleA',
component: ModuleA
}
]
});
export default router;- 在
ModuleA.vue中,動(dòng)態(tài)添加路由:
<template>
<div>
<h2>Module A</h2>
<button @click="addSpecialRoute">Add Special Route</button>
</div>
</template>
<script>
import { createRouter, createWebHistory } from 'vue-router';
import SpecialPage from '../views/SpecialPage.vue';
export default {
setup() {
const addSpecialRoute = () => {
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/moduleA/special',
component: SpecialPage,
name:'moduleASpecial'
}
]
});
router.addRoute('/moduleA', {
path: '/moduleA/special',
component: SpecialPage,
name:'moduleASpecial'
});
};
return {
addSpecialRoute
};
}
};
</script>- 在
ModuleB.vue(假設(shè)是一個(gè)導(dǎo)航組件)中,檢查路由是否存在并進(jìn)行導(dǎo)航:
<template>
<div>
<h2>Module B</h2>
<button @click="navigateToSpecialRoute">
Navigate to Special Route in Module A
</button>
</div>
</template>
<script>
import { useRouter } from 'vue-router';
export default {
setup() {
const router = useRouter();
const navigateToSpecialRoute = () => {
if (router.hasRoute('moduleASpecial')) {
router.push('/moduleA/special');
} else {
console.log('Route does not exist.');
}
};
return {
navigateToSpecialRoute
};
}
}
};
</script>思考(當(dāng)頁(yè)面被刷新時(shí)store 數(shù)據(jù)會(huì)被重置,怎么保持登錄狀態(tài),并建立當(dāng)前緩存)
使用瀏覽器本地存儲(chǔ)(Local Storage或Session Storage)保存登錄狀態(tài)相關(guān)數(shù)據(jù)
原理:
- 瀏覽器的本地存儲(chǔ)可以在頁(yè)面刷新或關(guān)閉后仍然保留數(shù)據(jù)。當(dāng)用戶登錄成功后,將用戶的登錄狀態(tài)(如登錄令牌、用戶信息等)存儲(chǔ)到本地存儲(chǔ)中。
- 在頁(yè)面加載時(shí),從本地存儲(chǔ)中讀取這些數(shù)據(jù)來(lái)恢復(fù)登錄狀態(tài)。
示例(使用Vuex和Local Storage)
- 存儲(chǔ)登錄狀態(tài)到本地存儲(chǔ)(在登錄成功的邏輯中)
import { setItem } from '../utils/localStorageUtil'; // 假設(shè)這個(gè)函數(shù)用于設(shè)置本地存儲(chǔ)的值
export const login = ({ commit }, user) => {
// 模擬登錄成功后的邏輯,比如獲取用戶令牌
const token = 'generated_token';
// 將令牌和用戶信息存儲(chǔ)到本地存儲(chǔ)
setItem('userToken', token);
setItem('userInfo', user);
commit('SET_USER', user);
commit('SET_LOGGED_IN', true);
};- 在
src/store/actions.js(假設(shè)將登錄相關(guān)動(dòng)作放在單獨(dú)的文件中)中,修改登錄動(dòng)作:
從本地存儲(chǔ)中恢復(fù)登錄狀態(tài)(在應(yīng)用初始化時(shí))
import { getItem } from '../utils/localStorageUtil';
import { createApp } from 'vue';
import store from './store';
import App from './App.vue';
import router from './router';
const app = createApp(App);
const userToken = getItem('userToken');
const userInfo = getItem('userInfo');
if (userToken && userInfo) {
store.commit('SET_USER', userInfo);
store.commit('SET_LOGGED_IN', true);
}
app.use(store).use(router).mount('#app');- 在
src/main.js中,在創(chuàng)建Vue應(yīng)用之前,檢查本地存儲(chǔ)中的登錄狀態(tài)并恢復(fù):
利用Vuex - Persistedstate插件持久化存儲(chǔ)狀態(tài)
原理:
vuex - persistedstate插件可以自動(dòng)將Vuex的狀態(tài)保存到本地存儲(chǔ)或者其他存儲(chǔ)介質(zhì)中,并在應(yīng)用重新加載時(shí)恢復(fù)狀態(tài)。- 它通過(guò)訂閱Vuex的變化,將狀態(tài)數(shù)據(jù)序列化后存儲(chǔ),在初始化時(shí)再反序列化并恢復(fù)狀態(tài)。
示例:
安裝插件:
npm install vuex - persistedstate
首先,安裝vuex - persistedstate:
配置插件(在src/store/index.js中)
import { createStore } from 'vuex';
import createPersistedState from 'vuex - persistedstate';
const store = createStore({
state: {
user: null,
loggedIn: false
},
mutations: {
SET_USER(state, user) {
state.user = user;
},
SET_LOGGED_IN(state, loggedIn) {
state.loggedIn = loggedIn;
}
},
plugins: [
createPersistedState({
storage: window.localStorage,
paths: ['user', 'loggedIn']
})
]
});
export default store;- 在這個(gè)配置中,
createPersistedState插件會(huì)將user和loggedIn這兩個(gè)狀態(tài)屬性保存到本地存儲(chǔ)中,并且在頁(yè)面刷新后從本地存儲(chǔ)中恢復(fù)這些狀態(tài)。
建立緩存機(jī)制(以路由組件緩存為例)
原理:
- 對(duì)于一些不需要每次都重新加載的頁(yè)面組件(比如用戶資料頁(yè)面,在用戶登錄狀態(tài)不變的情況下內(nèi)容不會(huì)改變),可以使用Vue的
keep - alive組件來(lái)緩存。 keep - alive會(huì)將包裹的組件實(shí)例緩存起來(lái),下次再訪問(wèn)該組件時(shí),會(huì)直接使用緩存的實(shí)例,而不是重新創(chuàng)建。
示例(在路由視圖中緩存組件):
- 在
src/App.vue中,使用keep - alive包裹router - view:
<template>
<div id="app">
<keep - alive>
<router - view></router - view>
</keep - alive>
</div>
</template>- 這樣,在路由切換時(shí),如果是已經(jīng)訪問(wèn)過(guò)的組件,會(huì)優(yōu)先從緩存中獲取,減少了組件重新加載的次數(shù),對(duì)于保持頁(yè)面狀態(tài)(如表單填寫(xiě)狀態(tài)、滾動(dòng)位置等)也很有幫助。
- 不過(guò)要注意,對(duì)于一些數(shù)據(jù)可能會(huì)變化的組件,需要正確地處理緩存更新的情況,比如通過(guò)
activated和deactivated生命周期鉤子來(lái)更新數(shù)據(jù)。
keep-alive
keep - alive實(shí)例應(yīng)用場(chǎng)景
多標(biāo)簽頁(yè)系統(tǒng)(Tab System)
- 場(chǎng)景描述:在一個(gè)類似瀏覽器標(biāo)簽頁(yè)的應(yīng)用界面中,用戶可以在多個(gè)頁(yè)面(標(biāo)簽)之間切換。這些頁(yè)面可能包含表單、圖表、列表等各種內(nèi)容。例如,一個(gè)數(shù)據(jù)管理系統(tǒng),用戶可以在“用戶列表”“數(shù)據(jù)報(bào)表”“系統(tǒng)設(shè)置”等多個(gè)標(biāo)簽頁(yè)之間切換。
keep - alive優(yōu)勢(shì):使用keep - alive可以緩存這些標(biāo)簽頁(yè)對(duì)應(yīng)的組件。當(dāng)用戶切換回之前訪問(wèn)過(guò)的標(biāo)簽頁(yè)時(shí),組件不需要重新渲染,能夠快速恢復(fù)之前的狀態(tài),提供流暢的用戶體驗(yàn)。例如,“用戶列表”標(biāo)簽頁(yè)中的搜索條件、滾動(dòng)位置和選中的行等信息都能得以保留。
向?qū)奖韱危╓izard - style Forms)
- 場(chǎng)景描述:在一個(gè)包含多個(gè)步驟的表單應(yīng)用中,如電商平臺(tái)的購(gòu)物流程(包括購(gòu)物車、收貨地址、支付方式等步驟)或用戶注冊(cè)流程(包含基本信息、驗(yàn)證信息、興趣愛(ài)好等步驟)。
keep - alive優(yōu)勢(shì):將每個(gè)步驟對(duì)應(yīng)的組件用keep - alive包裹,可以在用戶來(lái)回切換步驟時(shí),保持每個(gè)步驟表單中已填寫(xiě)的數(shù)據(jù)和狀態(tài)。這樣可以避免用戶因?yàn)轫?yè)面重新加載而丟失數(shù)據(jù),減少用戶的操作成本和煩躁情緒。
復(fù)雜的圖表展示(Complex Chart Display)
- 場(chǎng)景描述:在數(shù)據(jù)可視化應(yīng)用中,有多種復(fù)雜的圖表,如柱狀圖、折線圖、餅圖等,這些圖表可能需要從后端獲取數(shù)據(jù)并進(jìn)行渲染,而且用戶可能會(huì)頻繁切換查看不同類型的圖表。
keep - alive優(yōu)勢(shì):通過(guò)keep - alive緩存圖表組件,當(dāng)用戶切換回之前查看過(guò)的圖表時(shí),不需要重新獲取數(shù)據(jù)和重新渲染圖表,能夠快速顯示之前的圖表狀態(tài),提高應(yīng)用的響應(yīng)速度,特別是在數(shù)據(jù)量較大或者獲取數(shù)據(jù)的接口響應(yīng)較慢的情況下,這種優(yōu)勢(shì)更加明顯。
keep - alive注意點(diǎn)總結(jié):
keep-alive(exclude / include)
生命周期鉤子的變化
activated和deactivated鉤子:被keep - alive包裹的組件會(huì)新增activated和deactivated生命周期鉤子。activated鉤子在組件從緩存中激活時(shí)調(diào)用,deactivated鉤子在組件被緩存(切換到其他組件)時(shí)調(diào)用。在這些鉤子中,可以進(jìn)行一些特定的操作,比如在activated鉤子中重新獲取數(shù)據(jù)(如果數(shù)據(jù)可能已經(jīng)更新),或者在deactivated鉤子中暫停一些定時(shí)器或動(dòng)畫(huà)。- 與其他生命周期鉤子的關(guān)系:需要注意的是,當(dāng)組件被緩存時(shí),
mounted等生命周期鉤子不會(huì)再次觸發(fā),除非組件被重新創(chuàng)建(例如緩存被清除或者組件對(duì)應(yīng)的v - node被重新創(chuàng)建)。這意味著如果組件的初始化操作放在mounted鉤子中,并且組件被緩存,這些操作可能不會(huì)在每次顯示組件時(shí)執(zhí)行,需要根據(jù)情況調(diào)整到activated鉤子或者其他合適的地方。
組件狀態(tài)更新與緩存更新
- 數(shù)據(jù)更新問(wèn)題:如果緩存的組件中的數(shù)據(jù)可能會(huì)被其他組件或全局狀態(tài)的變化所影響,需要考慮如何正確地更新緩存組件中的數(shù)據(jù)。一種方法是在
activated鉤子中檢查數(shù)據(jù)是否需要更新,并根據(jù)需要重新獲取數(shù)據(jù)或者更新數(shù)據(jù)。另一種方法是使用響應(yīng)式數(shù)據(jù)(如Vuex中的狀態(tài)或者ref/reactive對(duì)象),并在數(shù)據(jù)變化時(shí)通過(guò)合適的方式通知緩存組件進(jìn)行更新。 - 動(dòng)態(tài)組件與
keep - alive:當(dāng)keep - alive包裹動(dòng)態(tài)組件時(shí),需要特別注意組件的切換和緩存更新。如果動(dòng)態(tài)組件的類型或者屬性發(fā)生變化,可能需要考慮如何處理緩存中的舊組件實(shí)例。例如,可以在動(dòng)態(tài)組件切換時(shí),清除舊組件的緩存或者根據(jù)新的組件屬性更新緩存中的組件。
緩存的清除與管理
- 手動(dòng)清除緩存:在某些情況下,可能需要手動(dòng)清除
keep - alive的緩存。例如,當(dāng)用戶執(zhí)行了某個(gè)操作(如退出登錄或者更新了某些關(guān)鍵數(shù)據(jù))后,希望重新加載所有組件,而不是使用緩存??梢酝ㄟ^(guò)keep - alive組件提供的exclude或include屬性來(lái)控制哪些組件被緩存,或者通過(guò)編程方式(如在Vuex的mutation或action中)清除緩存。 - 緩存大小和性能考慮:如果緩存的組件過(guò)多或者組件本身占用內(nèi)存較大,可能會(huì)影響應(yīng)用的性能。需要根據(jù)應(yīng)用的實(shí)際情況,合理地選擇要緩存的組件,并考慮緩存的生命周期和清除策略,以避免內(nèi)存泄漏或者性能下降的問(wèn)題。
keep - alive基礎(chǔ)回顧
keep - alive是Vue.js中的一個(gè)組件,用于緩存內(nèi)部的組件。- 當(dāng)組件在
keep - alive內(nèi)部被切換時(shí),它們不會(huì)被銷毀,而是被緩存起來(lái),下次再顯示時(shí)可以快速恢復(fù),減少重新渲染的時(shí)間。
include屬性應(yīng)用實(shí)例
場(chǎng)景描述:
- 假設(shè)我們有一個(gè)應(yīng)用,有三個(gè)路由組件:
Home.vue、Profile.vue和Settings.vue。 - 我們希望只緩存
Home.vue和Profile.vue組件,因?yàn)?code>Settings.vue組件的內(nèi)容可能會(huì)經(jīng)常變化,每次進(jìn)入都希望重新加載。
代碼實(shí)現(xiàn):
- 在
App.vue文件中:
<template>
<div id="app">
<keep - alive :include="['Home', 'Profile']">
<router - view></router - view>
</keep - alive>
</div>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App'
});
</script>- 這里的
include屬性是一個(gè)數(shù)組,數(shù)組中的元素是要被緩存的組件的名稱。 - 在這個(gè)例子中,只有名稱為
Home和Profile的組件會(huì)被keep - alive緩存。需要注意的是,組件名稱是在組件定義時(shí)通過(guò)name屬性指定的。 - 例如,在
Home.vue組件中應(yīng)該有如下定義:
<template>
<div>
Home Component
</div>
</template>
<script>
export default {
name: 'Home'
};
</script>exclude屬性應(yīng)用實(shí)例:
場(chǎng)景描述:
- 假設(shè)我們有同樣的三個(gè)路由組件,但是我們希望緩存除了
Settings.vue之外的所有組件。 - 這種情況可以使用
exclude屬性。
代碼實(shí)現(xiàn):
- 在
App.vue文件中:
<template>
<div id="app">
<keep - alive :exclude="['Settings']">
<router - view></router - view>
</keep - alive>
</div>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
name: 'App'
});
</script>- 這里的
exclude屬性也是一個(gè)數(shù)組,數(shù)組中的元素是不被緩存的組件的名稱。 - 在這個(gè)例子中,名稱為
Settings的組件不會(huì)被keep - alive緩存,而Home.vue和Profile.vue組件會(huì)被緩存。
結(jié)合動(dòng)態(tài)組件的應(yīng)用實(shí)例(進(jìn)階)
場(chǎng)景描述:
- 假設(shè)我們有一個(gè)頁(yè)面,里面有一個(gè)動(dòng)態(tài)組件,根據(jù)用戶的選擇可以切換不同的子組件,如
ComponentA.vue、ComponentB.vue和ComponentC.vue。 - 我們希望根據(jù)用戶的權(quán)限來(lái)決定哪些組件可以被緩存。
- 例如,普通用戶只能看到
ComponentA.vue和ComponentB.vue,并且只有ComponentA.vue可以被緩存;管理員可以看到所有組件,并且ComponentB.vue和ComponentC.vue可以被緩存。
代碼實(shí)現(xiàn):
- 在父組件(假設(shè)為
Parent.vue)中:
<template>
<div>
<keep - alive :include="cachedComponents">
<component :is="currentComponent"></component>
</keep - alive>
<button @click="changeComponent('ComponentA')">Show ComponentA</button>
<button @click="changeComponent('ComponentB')">Show ComponentB</button>
<button @click="changeComponent('ComponentC')">Show ComponentC</button>
</div>
</template>
<script>
import { defineComponent, ref } from 'vue';
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';
import ComponentC from './ComponentC.vue';
export default defineComponent({
setup() {
const components = {
ComponentA,
ComponentB,
ComponentC
};
const currentComponent = ref('ComponentA');
const userRole = 'user'; // 假設(shè)用戶角色,實(shí)際應(yīng)用中應(yīng)該從用戶信息獲取
const cachedComponents = userRole === 'user'? ['ComponentA'] : ['ComponentB', 'ComponentC'];
const changeComponent = (componentName) => {
currentComponent.value = componentName;
};
return {
currentComponent,
cachedComponents,
changeComponent
};
}
});
</script>- 在這個(gè)例子中,
cachedComponents是一個(gè)響應(yīng)式數(shù)組,根據(jù)用戶角色來(lái)決定哪些組件應(yīng)該被包含在keep - alive的緩存中。 - 動(dòng)態(tài)組件
component會(huì)根據(jù)currentComponent的值來(lái)切換顯示不同的子組件,并且只有在cachedComponents數(shù)組中的組件才會(huì)被keep - alive緩存。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器功能
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)簡(jiǎn)單的計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-09-09
vue?async?await?promise等待異步接口執(zhí)行完畢再進(jìn)行下步操作教程
在Vue中可以使用異步函數(shù)和await關(guān)鍵字來(lái)控制上一步執(zhí)行完再執(zhí)行下一步,這篇文章主要給大家介紹了關(guān)于vue?async?await?promise等待異步接口執(zhí)行完畢再進(jìn)行下步操作的相關(guān)資料,需要的朋友可以參考下2023-12-12
vue setInterval 定時(shí)器失效的解決方式
這篇文章主要介紹了vue setInterval 定時(shí)器失效的解決方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-07-07
Vue實(shí)現(xiàn)拖放排序功能的實(shí)例代碼
本文通過(guò)實(shí)例代碼給大家介紹了Vue中實(shí)現(xiàn)拖放排序功能,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07
Vue3項(xiàng)目剛創(chuàng)建就報(bào)錯(cuò)的問(wèn)題及解決
這篇文章主要介紹了Vue3項(xiàng)目剛創(chuàng)建就報(bào)錯(cuò)的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10

