Vue 實(shí)現(xiàn)關(guān)聯(lián)頁面多級跳轉(zhuǎn)(頁面下鉆)功能

背景

在項(xiàng)目開發(fā)過程中赞枕,經(jīng)常會遇到從上一個頁面跳轉(zhuǎn)到下一個頁面的需求尝江,俗稱下鉆苗踪。比如在概覽頁面的數(shù)據(jù)缩挑,需要查看詳情枪狂,點(diǎn)擊某個圖表或按鈕捺弦,即可跳轉(zhuǎn)到詳情頁面查看詳情數(shù)據(jù)躁垛。

目前為止俩莽,我們的項(xiàng)目中還沒有一個統(tǒng)一的頁面跳轉(zhuǎn)方法川梅,實(shí)現(xiàn)頁面跳轉(zhuǎn)的方式也因人而異疯兼,并且現(xiàn)有的很多項(xiàng)目只能在兩個頁面之間來回跳轉(zhuǎn),基本沒有完整的實(shí)現(xiàn)多個頁面互相跳轉(zhuǎn)的功能贫途。

關(guān)聯(lián)頁面跳轉(zhuǎn)做為項(xiàng)目的常用功能吧彪,并且執(zhí)行的都是重復(fù)性高的代碼邏輯,非常有必要把相關(guān)的邏輯抽出來丢早,封裝成簡單易用的公共方法和公共組件姨裸。

目的

統(tǒng)一各個項(xiàng)目的關(guān)聯(lián)跳轉(zhuǎn)方法邏輯,封裝成簡單易用的公共組件。

方案設(shè)計

首先啦扬,分析一下關(guān)聯(lián)頁面跳轉(zhuǎn)大概的邏輯步驟:

  1. 進(jìn)入頁面 A;
  2. 頁面A跳轉(zhuǎn)到頁面 B;
  3. 進(jìn)入頁面 B;
  4. 返回頁面 A;
  5. 進(jìn)入頁面 A中狂,即重新回到步驟 1 開始。

然后扑毡,對以上步驟進(jìn)行細(xì)分:

  1. 假設(shè)步驟 1 是正常進(jìn)入頁面胃榕,這時候沒有邏輯需要處理;
  2. 步驟 2 需要從頁面 A 跳轉(zhuǎn)到頁面 B,要實(shí)現(xiàn)這一步瞄摊,就必需知道頁面 B 的路由地址勋又,通過 VueRouter 跳轉(zhuǎn)到頁面 B 的路由地址。并且如果頁面 B 需要的一些查詢數(shù)據(jù)换帜,就要把頁面 B 的數(shù)據(jù)保存起來楔壤,等到步驟 3 使用;
  3. 進(jìn)入頁面B后惯驼,如果要獲取頁面 A 傳過來的一些查詢數(shù)據(jù)蹲嚣,就要先判斷是不是從頁面 A 跳轉(zhuǎn)過來的,如果是祟牲,就從保存數(shù)據(jù)的地方獲取頁面 A 傳過來的數(shù)據(jù)隙畜;
  4. 頁面 B 返回頁面 A,就必需知道頁面 A 的路由地址说贝,通過 VueRouter 跳轉(zhuǎn)到頁面 A 的路由地址议惰。這里的路由地址,需要在步驟 2 跳轉(zhuǎn)之前進(jìn)行保存乡恕,這里才可以取到言询;
  5. 可以發(fā)現(xiàn),步驟1和步驟5都是進(jìn)入頁面 A傲宜,但是執(zhí)行的邏輯卻不一樣运杭,所以,頁面 A 如果要恢復(fù)跳轉(zhuǎn)到頁面 B 之前的一些數(shù)據(jù)蛋哭,就要先判斷是不是從頁面 B 跳轉(zhuǎn)回來的县习,如果是,就從保存數(shù)據(jù)的地方獲取跳轉(zhuǎn)之前頁面 A 的數(shù)據(jù)谆趾;這里的跳轉(zhuǎn)之前的數(shù)據(jù)躁愿,需要在步驟 2 跳轉(zhuǎn)之前進(jìn)行保存,這里才可以取到沪蓬。

接下來彤钟,為了實(shí)現(xiàn)上述的邏輯,我們先確定用來保存頁面 A頁面 B 的數(shù)據(jù)的方法跷叉,這里采用的是 VUEX逸雹。再梳理一下以上邏輯步驟营搅,畫出流程圖。

流程圖

源頁面

file

目標(biāo)頁面

file

具體實(shí)現(xiàn)

源頁面跳轉(zhuǎn)到目標(biāo)頁面

這一步的邏輯寫在 VUEX 中梆砸,每次需要進(jìn)行這一步操作转质,直接調(diào) VUEX 中對應(yīng)的方法即可。具體實(shí)現(xiàn)邏輯帖世,就是先把源頁面和目標(biāo)頁面的標(biāo)識添加到路由參數(shù)上(目的是為了區(qū)分當(dāng)前頁面是進(jìn)行的目標(biāo)頁面還是返回的源頁面)休蟹,再保存源頁面和目標(biāo)頁面的數(shù)據(jù),然后進(jìn)行路由跳轉(zhuǎn)日矫。

在 store.js 中添加兩個以下兩個變量:

tgtPageParams: {}, // 關(guān)聯(lián)跳轉(zhuǎn)的目標(biāo)頁面數(shù)據(jù)(只保留一項(xiàng)數(shù)據(jù))
srcPageParams: [], // 關(guān)聯(lián)跳轉(zhuǎn)的源頁面數(shù)據(jù)(數(shù)組類型赂弓,保留多個頁面的數(shù)據(jù),可以多層返回哪轿,直到返回初始頁面)

然后添加以下方法:

// 關(guān)聯(lián)跳轉(zhuǎn)盈魁,跳轉(zhuǎn)到目標(biāo)頁面,并保存源頁面和目標(biāo)頁面的數(shù)據(jù)到 Vuex
goTargetPage(state, options) {
    // 在源頁面的 query 添加 tgtPageName 標(biāo)識窃诉,記住目標(biāo)頁面
    options.srcParams.query = Object.assign({}, options.srcParams.query, { tgtPageName: options.tgtParams.name });
    // 在目標(biāo)頁面的 query 添加 srcPageName 標(biāo)識杨耙,記住源頁面
    options.tgtParams.query = Object.assign({}, options.tgtParams.query, { srcPageName: options.srcParams.name });

    state.srcPageParams.push(options.srcParams); // 保存源頁面數(shù)據(jù)
    state.tgtPageParams = options.tgtParams; // 保存目標(biāo)頁面數(shù)據(jù)

    router.push({ name: options.tgtParams.name, query: options.tgtParams.query }); // 跳轉(zhuǎn)到目標(biāo)頁面
},

目標(biāo)頁面返回源頁面

這一步的邏輯寫在 VUEX 中,每次需要進(jìn)行這一步操作褐奴,直接調(diào) VUEX 中對應(yīng)的方法即可按脚。具體實(shí)現(xiàn)邏輯,就是從 state.srcPageParams 中取到源頁面的數(shù)據(jù)(包括路由地址和參數(shù))敦冬,然后進(jìn)行路由跳轉(zhuǎn)。

VUEX 中添加以下方法:

// 關(guān)聯(lián)跳轉(zhuǎn)唯沮,跳轉(zhuǎn)回源頁面
goSourcePage(state, vm) {
    let obj = state.srcPageParams.slice(-1)[0]; // 取數(shù)組的最后一項(xiàng)
    // 如果 Vuex 有上一頁的數(shù)據(jù)脖旱,則根據(jù) Vuex 的數(shù)據(jù)返回上一面
    if (obj && Object.keys(obj).length > 0) {
        router.push({ name: obj.name, query: obj.query }); // 進(jìn)行跳轉(zhuǎn)
    }
    // 如果 Vuex 中沒有上一頁的數(shù)據(jù),但是路由上有上一頁的標(biāo)志介蛉,則根據(jù)路由標(biāo)志返回上一頁(這是為了防止在詳情頁中刷新時萌庆,Vuex 數(shù)據(jù)丟失,無法返回上一頁的問題)
    else if (vm && vm.$route.query.srcPageName) {
        router.push({ name: vm.$route.query.srcPageName });
    }
},

進(jìn)入目標(biāo)頁面使用VUEX數(shù)據(jù)/返回源頁面恢復(fù)VUEX數(shù)據(jù)

這一步的邏輯是把上面方案設(shè)計中的步驟 3步驟 5 合并起來了币旧,寫在公共函數(shù)文件中践险,每次需要進(jìn)行這一步操作,直接調(diào) Vue.prototype 中對應(yīng)的方法即可吹菱。具體實(shí)現(xiàn)邏輯是:判斷當(dāng)前頁面是源頁面還是目標(biāo)頁面巍虫,如果是目標(biāo)頁面,那就使用源頁面?zhèn)鬟^來的數(shù)據(jù)鳍刷,如果是源頁面占遥,就恢復(fù)跳轉(zhuǎn)之前的數(shù)據(jù)。

在公共函數(shù)文件 utils.js 中添加以下方法输瓜,并掛載到 Vue.prototype 上:

/**
 * 關(guān)聯(lián)跳轉(zhuǎn)相關(guān)的頁面可以使用此方法
 * 1瓦胎、源頁面:可以把保存到 Vuex 中的數(shù)據(jù)恢復(fù)到 data 中使用
 * 2芬萍、目標(biāo)頁面:可以把源頁面?zhèn)鬟f到 Vuex 中的數(shù)據(jù)放到 data 中使用
 * 3、源頁面數(shù)據(jù)恢復(fù)后搔啊,刪除 Vuex 中對應(yīng)的備份數(shù)據(jù)柬祠,刪除路由上保存的目標(biāo)頁標(biāo)識
 * @param vm {object} 必填 當(dāng)前 Vue 組件實(shí)例
 */
$changeVueData: (vm) => {
    let tgtParams = store.state.tgtPageParams;
    let srcParams = vm.$store.state.srcPageParams.slice(-1)[0] || {}; // 取最后一個元素值
    let name = vm.$route.name;
    let query = vm.$deepCopyJSON(vm.$route.query); // 這里深拷貝是因?yàn)?$route.query 需要更新

    // 判斷當(dāng)前頁是 目標(biāo)頁面 還是 源頁面
    // 判斷條件是 先判斷路由名是否一致,再判斷指定的 query 的屬性值是否也一致
    let isTgtPage = tgtParams.name === name &&
        (tgtParams.checkKeys ? tgtParams.checkKeys.every(key => tgtParams.query[key] === query[key]) : true);
    let isSrcPage = srcParams.name === name &&
        (srcParams.checkKeys ? srcParams.checkKeys.every(key => srcParams.query[key] === query[key]) : true);

    // 如果當(dāng)前頁面是目標(biāo)頁面
    if (isTgtPage) {
        Object.assign(vm.$data, tgtParams.data || {}); // 將 源頁面?zhèn)鬟^來的數(shù)據(jù) 更新到當(dāng)前頁面的 data()负芋,以便頁面進(jìn)行查詢
    }
    // 如果當(dāng)前頁面是源頁面
    if (isSrcPage) {
        Object.assign(vm.$data, srcParams.data || {}); // 跳轉(zhuǎn)前保存的數(shù)據(jù) 更新到當(dāng)前頁面的 data()漫蛔,以便頁面進(jìn)行還原
        store.commit('popSourcePage'); // 將 srcPageParams 的最后一項(xiàng)數(shù)據(jù)刪除
        // 源頁面關(guān)聯(lián)跳轉(zhuǎn)邏輯結(jié)束后,清除掉當(dāng)前頁路由上的目標(biāo)頁標(biāo)識示罗,防止刷新頁面有問題
        delete query.tgtPageName;
        vm.$router.push({ name, query });
    }
},

返回上一頁按鈕

為了更方便的使用關(guān)聯(lián)跳轉(zhuǎn)功能惩猫,把返回上一頁按鈕封裝成了一個組件,具體實(shí)現(xiàn)代碼如下:

// back-button.vue
<template>
    <button class="primary-btn return-btn" v-if="showBackBtn" @click="backFn">
        <i class="return-icon"></i>{{ backText }}
    </button>
</template>
<script>
export default {
    name: 'back-button',
    props: {
        // 返回上一頁的文字
        backText: {
            type: String,
            default: () => '上一步'
        },
        // 返回上一頁的函數(shù)
        backFn: {
            type: Function,
            default: () => {}
        }
    },
    data() {
        return {
            showBackBtn: false,
        };
    },
    mounted() {
        this.setBackBtnShow();
    },
    activated() {
        this.setBackBtnShow();
    },
    methods: {
        // 更新返回上一頁按鈕的狀態(tài)
        setBackBtnShow() {
            this.$nextTick(() => {
                let srcPage = this.$store.state.srcPageParams.slice(-1)[0];
                this.showBackBtn = Boolean(srcPage && Object.keys(srcPage).length > 0);
            });
        },
    },
};
</script>
<style scoped lang="scss">
</style>

容錯部分

考慮到關(guān)聯(lián)跳轉(zhuǎn)的過程中蚜点,有可能用戶會突然中斷轧房,或者刷新頁面等異常操作,設(shè)計了部分容錯機(jī)制:

// 根組件 App.vue
/*...省略的代碼...*/
watch: {
    // 監(jiān)聽绍绘,當(dāng)路由發(fā)生變化的時候執(zhí)行
    $route(to, from) {
        // 如果即不是源頁面奶镶,也不是目標(biāo)頁面,則清空 Vuex 中保存的數(shù)據(jù)
        // 防止在關(guān)聯(lián)跳轉(zhuǎn)的過程中切換菜單或者進(jìn)行其他操作陪拘,導(dǎo)致 Vuex 中有上一次關(guān)聯(lián)跳轉(zhuǎn)殘留的數(shù)據(jù)
        if (!to.query.srcPageName && !to.query.tgtPageName) {
            this.$store.commit('clearTargetPage');
            this.$store.commit('clearSourcePage');
        }
    },
},
/*...省略的代碼...*/

使用示例

根據(jù)上述方案設(shè)計部分的步驟:
步驟 1 和步驟 5 厂镇,進(jìn)入頁面 A,邏輯在同個頁面左刽,代碼如下:

// 頁面 A.vue
/*...省略的代碼...*/
mounted() {
    vm = this;
    vm.$changeVueData(vm); // 關(guān)聯(lián)跳轉(zhuǎn)相關(guān)頁面捺信,每次進(jìn)入頁面,必需執(zhí)行 $changeVueData 函數(shù)欠痴,具體用法參考調(diào)用方法的注釋

    vm.ready();
},
/*...省略的代碼...*/

步驟 2迄靠,從頁面A跳轉(zhuǎn)到頁面 B,代碼如下:

// 頁面 A.vue
/*...省略的代碼...*/
methods: {
    // 跳轉(zhuǎn)到 B 頁面
    goUserSituation: function (name) {
        let srcParams = {
            name: vm.$route.name,
            query: vm.$route.query
        };
        let tgtParams = {
            name: 'user-situation',
            data: {
                checkedSystem: name
            }
        };
        vm.$goTargetPage(srcParams, tgtParams);
    },
},
/*...省略的代碼...*/

步驟 3喇辽,進(jìn)入頁面 B掌挚,代碼如下:

// 頁面 B.vue
/*...省略的代碼...*/
mounted() {
    vm = this;
    vm.$changeVueData(vm); // 關(guān)聯(lián)跳轉(zhuǎn)相關(guān)頁面,每次進(jìn)入頁面菩咨,必需執(zhí)行 $changeVueData 函數(shù)吠式,具體用法參考調(diào)用方法的注釋

    vm.ready();
},
/*...省略的代碼...*/

步驟 4,返回頁面 A抽米,代碼如下:

// 頁面 B.vue
/*...省略的代碼...*/
<template>
    <div>
        <backButton :backFn="$goSourcePage"></backButton>
        /*...省略的代碼...*/
    </div>
</template>
/*...省略的代碼...*/

總結(jié)

本文詳細(xì)介紹了關(guān)聯(lián)頁面多級跳轉(zhuǎn)(頁面下鉆)功能的實(shí)現(xiàn)特占,核心思想便是通過 VUEX 全局狀態(tài)管理,保存關(guān)聯(lián)跳轉(zhuǎn)源頁面和目標(biāo)頁面的數(shù)據(jù)缨硝,在跳轉(zhuǎn)之前摩钙,把需要的數(shù)據(jù)保存起來,跳轉(zhuǎn)到目標(biāo)頁面時查辩,把目標(biāo)頁面需要的數(shù)據(jù)從 VUEX 中獲取胖笛,跳轉(zhuǎn)回源頁面時网持,把源頁面的數(shù)據(jù)從 VUEX 中恢復(fù)。

把這幾個關(guān)鍵動作长踊,封裝成通用方法和組件功舀,即統(tǒng)一了各個項(xiàng)目的關(guān)聯(lián)頁面跳轉(zhuǎn)方式,也提高了代碼的質(zhì)量身弊,更有利于后期維護(hù)辟汰。另外,文章中的容錯部分阱佛,只寫了一部分帖汞,如果后續(xù)需要繼續(xù)完善該功能,可以把容錯部分完善一下凑术。

最后翩蘸,感謝大家的閱讀,希望對大家有幫助淮逊,如果有不同的想法和建議催首,歡迎提出。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末泄鹏,一起剝皮案震驚了整個濱河市郎任,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌备籽,老刑警劉巖舶治,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異车猬,居然都是意外死亡歼疮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門诈唬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缩麸,你說我怎么就攤上這事铸磅。” “怎么了杭朱?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵阅仔,是天一觀的道長。 經(jīng)常有香客問我弧械,道長八酒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任刃唐,我火速辦了婚禮羞迷,結(jié)果婚禮上界轩,老公的妹妹穿的比我還像新娘。我一直安慰自己衔瓮,他們只是感情好浊猾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著热鞍,像睡著了一般葫慎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上薇宠,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天偷办,我揣著相機(jī)與錄音,去河邊找鬼澄港。 笑死椒涯,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的慢睡。 我是一名探鬼主播逐工,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼漂辐!你這毒婦竟也來了泪喊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤髓涯,失蹤者是張志新(化名)和其女友劉穎袒啼,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纬纪,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蚓再,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了包各。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摘仅。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖问畅,靈堂內(nèi)的尸體忽然破棺而出娃属,到底是詐尸還是另有隱情,我是刑警寧澤护姆,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布矾端,位于F島的核電站,受9級特大地震影響卵皂,放射性物質(zhì)發(fā)生泄漏秩铆。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一灯变、第九天 我趴在偏房一處隱蔽的房頂上張望殴玛。 院中可真熱鬧捅膘,春花似錦、人聲如沸族阅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坦刀。三九已至愧沟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鲤遥,已是汗流浹背沐寺。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盖奈,地道東北人混坞。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像钢坦,于是被迫代替她去往敵國和親究孕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容