連續預告了好幾天,總算寫到這篇了。
系列篇2:Vue 全站快取二:如何設計全站快取
本篇為系列篇3:Vue 全站快取之 vue-router-then :前後頁資料傳遞
前言
在前文 Vue 全站快取之 keep-alive : 動態移除快取 和 Vue 全站快取二:如何設計全站快取 中,我們實現了全站快取的基礎框架,在頁面之間後退或同層頁面之間跳轉時可以複用快取,減少了請求頻率,提升了使用者體驗,最讚的是,開發邏輯理論上會簡單直觀很多。
出現了一個新需求
在父子元件之間,資料可以通過 Prop 和 $emit 事件來互相傳遞資料。
在本系列裡,我們研究的主題是前後頁的元件快取與複用,因為使用了 keep-alive 的快取特性,雖然前一頁在介面上已經被移出,在系統裡,前後頁元件物件仍然是同時存在的。
於是,一個新的需求自然而然的出現了,就是如何在前後頁元件之間傳遞資料
。
store 模式和 vuex
前文也曾說過,為了在不同頁面之間傳遞或共用資料,目前主流的方案是使用sotre模式或引入 vuex , 作為前後頁面之外的公共元件,所有頁面都可以直接對其存取資料。連結
這是一個很好的解決方案,可以相容很多場景,而且很值得繼續挖掘這個特性哦。
留個坑,最近在研究如何將這個Flux特性發揮到極致。
懶是人類進步的源泉
就我個人而言,我是很極端的懶,vuex 的確能解決前後頁資料交流的問題,然而許多時候我僅僅只是開啟一個頁面選擇一個客戶回來而已。
如果能寫成
<input v-model="searchText">
複製程式碼
我可不想寫成:
<input
v-bind:value="searchText"
v-on:input="searchText = $event.target.value"
>
複製程式碼
所以,還是商城的例子,如果在訂單頁面需要開啟一個新頁面去選擇一個地址,我可不想從 vuex 那裡繞一大圈寫一堆State
Mutation
Action
,我希望寫成下面這樣:
<!-- buySomething.vue -->
<inputSelectedAddress v-model="item.addressID" v-model-link="'/select_address'" placeholder="選擇地址" />
<!-- selectAddress.vue -->
<template>
<ul>
<li @click="selectOneAddress(1)">北京路南廣場東門101</li>
<li @click="selectOneAddress(2)">上海城下水道左邊202</li>
</ul>
</template>
<script>
methods:{
selectOneAddress:function(addressID){
this.$emit('input',addressID);
this.$router.go(-1);
}
}
</script>
複製程式碼
開啟一個地址選擇頁,選擇好地址後,直接將地址 id 傳回來,甚至直接和 v-model 相容起來,然後該幹啥幹啥,這樣的場景想想就很贊哇。
v-model-link
在上文的示例程式碼裡出現了v-model-link
這個自定義指令,這個指令來自外掛 vue-router-then 的一個特性。連結
v-model-link
的基本用法:
- 在繫結
v-model
的元素上使用v-model-link
指令指向一個新頁面 - 在新頁面裡使用者互動之後,使用
$emit
觸發一個input
事件將資料傳遞回來即可更新v-model
所對應的值。
請注意此處的場景差異:$emit本來是用來進行父子元件的事件傳遞的,在此處被挪用到了前後頁元件之間傳遞事件,
原理簡述:
v-model-link
的值是一個 url 地址,在點選該指令所在的 DOM 元素後,會跳轉到該 url 頁面,並在新的頁面裡監聽input事件反饋給指令所在的DOM 元素或元件。- 因為
v-model
就是一個input
事件的語法糖,所以其對應的值會受input
事件影響。 - 因為快取的存在,前一頁元件沒有被銷燬,這才是vue-router-then這個外掛成功使用的基礎。
- 所以,請注意正確設定各路由頁面的層級關係,如果離開頁面時元件被銷燬了,可就沒法玩了。
vue-router-then
vue-router-then的核心本質就是實現了在當前頁操作下一頁元件的功能,v-model-link
只是該功能基礎上的一個語法糖指令,當然也是最重要最常用的特性指令。
如何安裝
npm install vue-router-then --save;
複製程式碼
import Vue from 'vue'
import router from './router'
import routerThen from 'vue-router-then';
routerThen.initRouter(router)
Vue.use(routerThen)
複製程式碼
使用方法一:在當前頁面操作下一頁面的元件物件
該外掛名為vue-router-then,顧名思義,其最大的特性是給router的方法(push,replace,go)返回了一個promise物件,並使用新頁面的vm作為引數在then方法裡供處理。
methods:{
clickSomeOne:function(){
this.$routerThen.push('/hello_world').then(vm=>{
// 此處的 vm 即為新頁面hello_world的元件物件
console.log(vm);
});
},
}
複製程式碼
使用方法二:使用$routerThen.modelLink在當前頁獲得下一頁面元件上報的input事件中的值
$routerThen中還新增了一個自定義方法modelLink
,用來處理下一頁面上報的值,該方法同樣可以操控下一頁面元件物件。
methods:{
jumpToNextPage:function(value){
this.$routerThen.modelLink('/select_price',value=>{
// 此處獲得select_price頁面$emit出來的 input 事件中的值
console.log(value);
}).then(vm=>{
// 此處的 vm 即為新頁面select_price的元件物件
console.log(vm);
});
},
}
複製程式碼
使用方法三:配合 v-model 使用自定義指令 v-model-link
<inputCustomer v-model="item.customerID" v-model-link="'/select_customer'" />
複製程式碼
再舉個例子
在我們的記應收這款記賬APP中,有一個選擇客戶的頁面,該頁面支援搜尋客戶,如果搜尋不到客戶,則應該支援以關鍵字去建立一個客戶並選中新客戶。
也就是說涉及編輯合同->選擇客戶->新建客戶
這三個頁面,其中核心程式碼如下:
編輯合同:
<!-- InitEditContractDetail.vue -->
<inputCustomer v-model="item.customerID" v-model-link="'/customer/select_customer'"/>
複製程式碼
選擇客戶:
<!-- InitSelectCustomer.vue -->
<template>
<div>
<input type="text" v-model="keyword" />
<a @click="newCustomerJumpLink">新增</a>
</div>
</template>
<script>
methods:{
newCustomerJumpLink(){
this.$routerThen.modelLink('/customer/edit',value=>{
this.newCustomerAdded(value)
}).then(vm=>{
if (this.keyword)
{
vm.$set(vm.item,'name',this.keyword);
}
});
},
newCustomerAdded:function(customerID){
this.$emit('input',customerID);
this.$router.go(-1);
},
}
</script>
複製程式碼
新增客戶:
<!-- InitCustomerEdit.vue -->
<template>
<div>
<div>客戶名稱</div>
<input type="text" v-model="item.name" />
<div class="btn_submit" @click="submit">提交</div>
</div>
</template>
<script>
methods:{
submit:function(){
this.$emit('input',customerID);
this.$router.go(-1);
}
}
</script>
複製程式碼
後語
到此時,應付大部分的專案場景應該沒問題了。
後續還會發幾篇相關文章,研究 vue-router-then 的原理,研究全站快取這種極端情況下使用者的資料又會發生什麼奇怪的變化呢,又或者還能玩出哪些更有意思的花樣呢。
敬請期待。
更新續篇:Vue 全站快取之 vue-router-then 實現原理
原文來自阿星的部落格:wanyaxing.com/blog/201807…