趕時間的可以跳到最后看源代碼主慰,看不懂為什么這樣寫的回頭來重新看。
事情要從上個月說起该肴,公司接了個項目藐不,需要做一個微信端的應(yīng)用秦效,時間是兩個月拱雏。
由于之前做過一個簡單的微信商城铸抑,非常鄙視微信頁面跳轉(zhuǎn)時的拖沓,所以決定做單頁應(yīng)用蒲赂,使用vue框架來開發(fā)刁憋。
vue是個不錯的框架,加上源碼豐富若皱,一路做來還算順利尘颓,沒遇到什么特別難的地方。
滿以為下周可以順利上線了互广,直到要放到微信端的時候出問題了卧土。
客戶需要在公眾號做一個這樣的菜單:
菜單的入口鏈接到單頁應(yīng)用的不同頁面尤莺。
而單頁應(yīng)用的內(nèi)部地址是通過#后面的內(nèi)容指引router跳轉(zhuǎn)的:
例如跳轉(zhuǎn)到關(guān)于我們的頁面
http://xxxx.xxx.com/#/about?appid=1&cpid=1
其中appid和cpid是必須的參數(shù),傳給單頁應(yīng)用后會賦值給vuex作為全局變量扫皱。
但是代碼打包發(fā)布之后捷绑,通過這個地址死活進(jìn)不去該進(jìn)的頁面氢妈,一律進(jìn)到根目錄下首量。據(jù)后端人員反映进苍,經(jīng)過微信瀏覽器傳到服務(wù)器的頁面地址鸭叙,#號連同后面的參數(shù)一并丟失沈贝。
不是吧
這是個重大bug,如果下周在上線時客戶那邊不能忽悠過去,他們一定不肯支付尾款嗡善,公司可能會炒我魷魚学歧,老婆發(fā)現(xiàn)下個月工資卡沒錢到賬,會把家里電腦里的cpu拆了讓我跪袁铐,我受苦不要緊横浑,可憐了cpu呀伪嫁!
遂百度之。
十分鐘之后帝洪,我放棄了脚猾。找不到合適的解決方案
也有提出類似問題的
http://www.cnblogs.com/mingxinice/p/mingxin.html
但也沒有好的解決方案
還看到一個類似的但是看了半天他也不知道自己怎么解決的。
http://www.reibang.com/p/a1a31f9da272
或者是可以解決砰奕,將router模式改為history,但需要后端做一大堆事的提鸟,可這不就違背了前后端分離的真諦了嗎?
哎胸哥,為了平復(fù)我忐忑的心赡鲜,我心中不禁念起了大悲咒:“南無·喝啰怛那·哆啰夜耶...”
...在電光火石之間,我的心中閃過了一個念頭嘲更,得到了一個解決辦法。
這個辦法也許隱世的前端高手也正在使用篓冲,但除了這里纹因,你很難百度到琳拨,如果對你有幫助,請打賞一下惊畏。
如上所述密任,微信跳轉(zhuǎn)時颜启,瀏覽器會把#后面的hash值給搞掉,據(jù)說在ios中還會在前面加點東西浪讳。但是在#之前加的get參數(shù)是不會丟失的缰盏。
那么我們?yōu)槭裁床荒馨褏?shù)前移呢?像這樣:
http://xxxx.xxx.com/?appid=1&cpid=1
頁面跳轉(zhuǎn)怎么辦淹遵?我可以加一個page參數(shù)呀
http://xxxx.xxx.com/?appid=1&cpid=1&page=/about
這樣寫就完全丟棄了#
然后我們通過一個js方法獲取鏈接的參數(shù)
getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}
我把這個方法放到了一個文件中口猜,并作為全局對象引入,當(dāng)然透揣,你也可以直接放到main.js里
在main.js里,router.beforeEach這個鉤子之前济炎,將獲得的參數(shù)賦值給一個全局對象。
global.browserQuery = {
appid: getQueryString('appid'),
cpid: getQueryString('cpid'),
page: getQueryString('page')
}
在router.beforeEach的鉤子里這樣寫:
router.beforeEach(({ meta, path, query }, from, next) => {
if (browserQuery.appid && browserQuery.cpid) {
store.commit('appid', browserQuery.appid);
store.commit('cpid', browserQuery.cpid);
let p=browserQuery.page;
browserQuery={};
return next(p);
}
///其它代碼
})
解釋一下须尚,如果browserQuery不是空對象,說明是從外面的鏈接點擊首次進(jìn)入的侍咱,這時候?qū)⑺枰膮?shù)寫進(jìn)store中耐床,剩下的page是要跳轉(zhuǎn)到的路由,賦值給p變量楔脯,然后清空browserQuery對象撩轰,使下次跳轉(zhuǎn)不再進(jìn)入這個條件代碼段,然后用return next(p)跳轉(zhuǎn)到該去的頁面。
可是一測試钧敞,問題又來了。在本地測試(沒發(fā)布到微信)
鏈接:http://xxxx.xxx.com/?appid=1&cpid=1&page=/about
還是跳轉(zhuǎn)到根目錄麸粮,但后面加#/卻能正確跳轉(zhuǎn)到about頁面
http://xxxx.xxx.com/?appid=1&cpid=1&page=/about#/
為什么呢溉苛?唯一的解釋是,沒有hash值弄诲,router沒有準(zhǔn)備好愚战,所以默認(rèn)跳轉(zhuǎn)到根目錄。但#/會被微信丟掉呀齐遵,加了也沒用寂玲,這不矛盾嗎?
能不能跳轉(zhuǎn)兩次梗摇?先跳到#/,這時router加載了拓哟,再跳轉(zhuǎn)到#/about
所以我寫成這樣
router.beforeEach(({ meta, path, query }, from, next) => {
if (browserQuery.appid && browserQuery.cpid) {
store.commit('appid', browserQuery.appid);
store.commit('cpid', browserQuery.cpid);
delete browserQuery.appid;
return next('/');
}else if(browserQuery.page){
let p=browserQuery.page;
browserQuery={};
return next(p);
}
///其它代碼
})
頁面進(jìn)來后,刪掉appid,并讓它先跳轉(zhuǎn)到根目錄'/'伶授,這時appid已經(jīng)不在断序,所以不會進(jìn)入條件一,而會進(jìn)入else,因為page還在糜烹,清空browserQuery,并跳轉(zhuǎn)到該去的頁面about,因為跳轉(zhuǎn)前為根目錄'/',hash值已在违诗,應(yīng)當(dāng)不會出現(xiàn)上述問題。
但一測試疮蹦,還是不行诸迟。
我猜想這個router.beforeEach鉤子是不支持這樣瞬間跳轉(zhuǎn)兩次的。也就是在return next('/') 之后愕乎,不會立即執(zhí)行下一個beforeEach的鉤子阵苇。
能不能放在router.afterEach里呢?試試妆毕!
router.beforeEach(({ meta, path, query }, from, next) => {
if (browserQuery.appid && browserQuery.cpid) {
store.commit('appid', browserQuery.appid);
store.commit('cpid', browserQuery.cpid);
delete browserQuery.appid;
return next('/');
}
///其它代碼
})
router.afterEach(route => {
if (browserQuery.page) {
let p = browserQuery.page;
browserQuery = {};
router.push(p);//因為沒有next對象慎玖,直接調(diào)用router跳轉(zhuǎn)
}
})
當(dāng)router導(dǎo)航到根目錄'/'之后,馬上跳轉(zhuǎn)到about頁面笛粘,這時雖然是路由成功趁怔,但根目錄頁面還沒有渲染,其實看不出有什么差別薪前。成功了润努。
對!就這么解決了困擾單頁的多入口問題了示括。
如果你要將其中一個頁面分享出去铺浇,可以這樣組織鏈接,例如
http://xxxx.xxx.com/?appid=1&cpid=1&page=/detail/1
當(dāng)其他人點擊鏈接后垛膝,會被導(dǎo)航到
http://xxxx.xxx.com/#/detail/1頁面
從此你可以告別#的困擾鳍侣,單頁應(yīng)用從此不再受微信的歧視丁稀。
吸了一口維他奶,感嘆啊倚聚。業(yè)界難題被我輕而易舉解決了线衫,如果我不當(dāng)程序員,真實IT界的損失呀惑折。嘻嘻嘻嘻(笑)
全部代碼如下:
getQueryString(name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}
global.browserQuery = {
appid: getQueryString('appid'),
cpid: getQueryString('cpid'),
page: getQueryString('page')
}
router.beforeEach(({ meta, path, query }, from, next) => {
if (browserQuery.appid && browserQuery.cpid) {
store.commit('appid', browserQuery.appid);
store.commit('cpid', browserQuery.cpid);
delete browserQuery.appid;
return next('/');
}
///其它代碼
})
router.afterEach(route => {
if (browserQuery.page) {
let p = browserQuery.page;
browserQuery = {};
router.push(p);//因為沒有next對象授账,直接調(diào)用router跳轉(zhuǎn)
}
})