項(xiàng)目上遇到一個(gè)這樣的需求舅巷,就是在打開含有iframe界面時(shí)羔味,去切換標(biāo)簽,切回含有iframe界面的時(shí)候钠右,要求iframe界面不刷新赋元。vue框架中,我們?nèi)ヌ幚泶祟悊栴}飒房,通常馬上就會(huì)想到去使用vue框架中自帶的keep-alive組件搁凸,所以一開始我也是去使用了keep-alive,但是發(fā)現(xiàn)沒有達(dá)到預(yù)期效果,后面通過研究和查閱資料發(fā)現(xiàn)狠毯,在vue項(xiàng)目中加入了含有iframe的頁面护糖,在路由切換的過程中,使用keep-alive是達(dá)不到數(shù)據(jù)緩存的效果的嚼松。
2嫡良、使用keep-alive緩存不了iframe界面原因
(1)vue中的keep-alive 【1】原理:Vue 的緩存機(jī)制并不是直接存儲(chǔ) DOM 結(jié)構(gòu),而是將 DOM 節(jié)點(diǎn)抽象成了一個(gè)個(gè) VNode節(jié)點(diǎn)献酗。因此寝受,Vue 的 keep-alive 緩存也是基于 VNode節(jié)點(diǎn) 而不是直接存儲(chǔ) DOM 節(jié)點(diǎn)。在需要渲染的時(shí)候從Vnode渲染到真實(shí)DOM上罕偎。 【2】參數(shù):Keep-alive 組件提供了 include 和 exclude 兩個(gè)屬性很澄,允許組件有條件的進(jìn)行緩存。 include: 字符串或正則表達(dá)式。只有匹配的組件會(huì)被緩存甩苛。 exclude: 字符串或正則表達(dá)式蹂楣。任何匹配的組件都不會(huì)被緩存。 【3】Keep-alive 組件提供了兩個(gè)生命鉤子函數(shù)浪藻,分別是 activated 和 deactivated 捐迫。 activated :當(dāng)頁面存在緩存的時(shí)候執(zhí)行該函數(shù)。 deactivated :在頁面結(jié)束時(shí)觸發(fā)該方法爱葵,可清除掉滾動(dòng)方法等緩存施戴。 (2)iframe中keep-alive機(jī)制失效原因:iframe頁里的內(nèi)容并不屬于節(jié)點(diǎn)的信息,所以使用keep-alive依然會(huì)重新渲染iframe內(nèi)的內(nèi)容萌丈。而且iframe每一次渲染就相當(dāng)于打開一個(gè)新的網(wǎng)頁窗口赞哗,即使把節(jié)點(diǎn)保存下來,在渲染時(shí)iframe頁還是刷新的辆雾。
3肪笋、vue中實(shí)現(xiàn)iframe內(nèi)容緩存的思路
(1)保存iframe頁里的節(jié)點(diǎn)信息我們很難去操作,這個(gè)時(shí)候我們就該想是否能在vue的route-view節(jié)點(diǎn)上動(dòng)些手腳度迂。 (2)我們可以在切換不含iframe的界面時(shí)使用vue路由藤乙,在切換含iframe頁的界面時(shí)利用v-show來控制顯示隱藏,使iframe的節(jié)點(diǎn)不被刪除惭墓,以此來防止界面節(jié)點(diǎn)被重新更新坛梁,從而達(dá)到保存iframe節(jié)點(diǎn)數(shù)據(jù)的效果。 具體該怎么操作呢腊凶?請(qǐng)繼續(xù)往下看: 第一步:我們先需要修改一下路由配置划咐,如下:
{
path: '/printReportShow',
component: Layout,
redirect: '/printReportShow',
hidden: false,
name: 'Layout',
children: [
{
path: '/printReportShow',
iframeComponent: printReportShow,
name: 'printReportShow',
hidden: false,
meta: {title: '打印預(yù)覽',}
}
]
},
從以上代碼,不難看出/printReportShow就是我們要跳轉(zhuǎn)的路由地址钧萍,我們可以看到printReportShow的路由配置中組件我寫的是iframeComponent而非官方教程中的component褐缠,這樣造成的后果就是在 router-view 節(jié)點(diǎn)中渲染時(shí),頁面顯示的是空白頁风瘦。 第二步:修改主界面AppMain.vue(你的可能不叫這個(gè))中的template队魏,添加vue的內(nèi)置組件component,如下:
<template>
<section class="app-main">
<transition name="move" mode="out-in" @enter="onEnter">
<keep-alive :include="cachedViews">
<router-view :key="key" />
</keep-alive>
</transition>
<!--iframe頁-->
<component
v-for="item in hasOpenComponentsArr"
:key="item.children[0].name"
:is="item.children[0].name"
v-show="$route.path === item.path"
></component>
</section>
</template>
從以上代碼我們可以看到万搔,iframe節(jié)點(diǎn)數(shù)據(jù)在根節(jié)點(diǎn)App.vue渲染時(shí)器躏,也隨即跟著渲染了,但是考慮到性能原因蟹略,對(duì)此我們選擇將iframe的顯示做成懶加載形式的登失,只有在用戶進(jìn)入相應(yīng)的頁面時(shí)才觸發(fā)渲染,在渲染完畢后再通過v-show去控制界面在切換時(shí)的顯示與隱藏挖炬。 第三步:AppMain.vue中具體的邏輯處理
<script>
import Vue from 'vue';
export default {
name: 'AppMain',
computed: {
// 實(shí)現(xiàn)懶加載揽浙,只渲染已經(jīng)打開過(hasOpen:true)的iframe頁
hasOpenComponentsArr() {
return this.componentsArr.filter(item => {
return item.children[0].hasOpen
});
}
},
data() {
return {
componentsArr: []
}
},
watch: {
$route() {
// 判斷當(dāng)前路由是否iframe頁
this.isOpenIframePage();
}
},
created() {
// 設(shè)置iframe頁的數(shù)組對(duì)象
const componentsArr = this.getComponentsArr();
componentsArr.forEach((item) => {
// Vue.component(item.name, item.component);
Vue.component(item.children[0].name, item.children[0].component);
});
this.componentsArr = componentsArr;
// 判斷當(dāng)前路由是否iframe頁
this.isOpenIframePage();
},
methods:{
// 根據(jù)當(dāng)前路由設(shè)置hasOpen
isOpenIframePage() {
const target = this.componentsArr.find(item => {
return item.path === this.$route.path
});
if (target && !target.children[0].hasOpen) {
target.children[0].hasOpen = true;
}
},
// 遍歷路由的所有頁面,把含有iframeComponent標(biāo)識(shí)的收集起來
getComponentsArr() {
const router = this.$router;
const routes = router.options.routes;
const iframeArr = []
const singleRoutes = routes[6];
const printReportObj = {
name: routes[6].children[0].name,
path: routes[6].children[0].path,
hasOpen: false, // 是否打開過,默認(rèn)false
component: routes[6].children[0].iframeComponent // 組件文件的引用
}
singleRoutes.children.splice(0,1);
singleRoutes.children.push(printReportObj);
iframeArr.push(singleRoutes);
return iframeArr
}
}
}
</script>
備注:由于我們系統(tǒng)中就涉及到一個(gè)iframe界面馅巷,所以在處理上可能有些地方寫死了膛虫,如果是多iframe的可以自行參考著做相應(yīng)的修改。 4钓猬、拓展
由于切換標(biāo)簽稍刀,含iframe的component組件不會(huì)再觸發(fā)路由鉤子函數(shù)和生命周期函數(shù),導(dǎo)致界面數(shù)據(jù)無法做更新操作敞曹,同時(shí)瀏覽器刷新時(shí)账月,界面數(shù)據(jù)會(huì)有丟失的問題,所以我們可以考慮去監(jiān)聽sessionStorage的值澳迫,以此來更新數(shù)據(jù)局齿。詳情可以看下面這篇文章:vue中如何實(shí)現(xiàn)對(duì)sessionStorage的監(jiān)聽。
4.vue中如何實(shí)現(xiàn)對(duì)sessionStorage的監(jiān)聽
1橄登、使用場景
(1)在vue項(xiàng)目中抓歼,為了在瀏覽器刷新時(shí),頁面數(shù)據(jù)不丟失拢锹,我們會(huì)選擇將數(shù)據(jù)瀏覽器緩存sessionStorage中谣妻。 (2)為了傳遞參數(shù) 由于切換標(biāo)簽,含iframe的組件不會(huì)在觸發(fā)路由鉤子函數(shù)和生命周期函數(shù)卒稳,導(dǎo)致界面數(shù)據(jù)無法做更新操作拌禾,同時(shí)還要解決瀏覽器刷新時(shí),界面數(shù)據(jù)不丟失的問題展哭,所以才考慮到去監(jiān)聽sessionStorage的值,以此來更新數(shù)據(jù)闻蛀。
2匪傍、具體實(shí)現(xiàn)思路
(1)在vue項(xiàng)目main.js文件中,在vue原型上定義resetSetItem,代碼如下:
Vue.prototype.resetSetItem = function (key, newVal) {
// 創(chuàng)建一個(gè)StorageEvent事件
var newStorageEvent = document.createEvent('StorageEvent');
const storage = {
setItem: function (k, val) {
sessionStorage.setItem(k, val);
// 初始化創(chuàng)建的事件
newStorageEvent.initStorageEvent(key, false, false, k, null, val, null, null);
// 派發(fā)對(duì)象
window.dispatchEvent(newStorageEvent)
}
}
return storage.setItem(key, newVal);
}
2)通過resetSetItem方法存值觉痛,代碼如下:
self.resetSetItem('printReportShowQuery', JSON.stringify(query));
3)getItem取值和window.addEventListener監(jiān)聽役衡,代碼如下:
created() {
let routQuery = {};
let self = this;
// ***為resetSetItem函數(shù)對(duì)應(yīng)的第一個(gè)key參數(shù)
window.addEventListener('***', ()=> {
let printReportShowQuery = JSON.parse(sessionStorage.getItem('printReportShowQuery'));
routQuery.url = printReportShowQuery.url;
routQuery.params = JSON.parse(printReportShowQuery.params);
self.init(routQuery);
});
},