趕時(shí)間的可以跳到最后看源代碼,看不懂為什么這樣寫(xiě)的回頭來(lái)重新看。
事情要從上個(gè)月說(shuō)起,公司接了個(gè)項(xiàng)目,需要做一個(gè)微信端的應(yīng)用,時(shí)間是兩個(gè)月。
由于之前做過(guò)一個(gè)簡(jiǎn)單的微信商城,非常鄙視微信頁(yè)面跳轉(zhuǎn)時(shí)的拖沓,所以決定做單頁(yè)應(yīng)用,使用vue框架來(lái)開(kāi)發(fā)。
vue是個(gè)不錯(cuò)的框架,加上源碼豐富,一路做來(lái)還算順利,沒(méi)遇到什么特別難的地方。
滿以為下周可以順利上線了,直到要放到微信端的時(shí)候出問(wèn)題了。
客戶需要在公眾號(hào)做一個(gè)這樣的菜單:

菜單的入口鏈接到單頁(yè)應(yīng)用的不同頁(yè)面。
而單頁(yè)應(yīng)用的內(nèi)部地址是通過(guò)#后面的內(nèi)容指引router跳轉(zhuǎn)的:
例如跳轉(zhuǎn)到關(guān)于我們的頁(yè)面
http://xxxx.xxx.com/#/about?appid=1&cpid=1
其中appid和cpid是必須的參數(shù),傳給單頁(yè)應(yīng)用后會(huì)賦值給vuex作為全局變量。
但是代碼打包發(fā)布之后,通過(guò)這個(gè)地址死活進(jìn)不去該進(jìn)的頁(yè)面,一律進(jìn)到根目錄下。據(jù)后端人員反映,經(jīng)過(guò)微信瀏覽器傳到服務(wù)器的頁(yè)面地址,#號(hào)連同后面的參數(shù)一并丟失。
不是吧

這是個(gè)重大bug,如果下周在上線時(shí)客戶那邊不能忽悠過(guò)去,他們一定不肯支付尾款,公司可能會(huì)炒我魷魚(yú),老婆發(fā)現(xiàn)下個(gè)月工資卡沒(méi)錢(qián)到賬,會(huì)把家里電腦里的cpu拆了讓我跪,我受苦不要緊,可憐了cpu呀!
遂百度之。
十分鐘之后,我放棄了。找不到合適的解決方案
也有提出類似問(wèn)題的
http://www.cnblogs.com/mingxinice/p/mingxin.html
但也沒(méi)有好的解決方案
還看到一個(gè)類似的但是看了半天他也不知道自己怎么解決的。
http://m.itdecent.cn/p/a1a31f9da272
或者是可以解決,將router模式改為history,但需要后端做一大堆事的,可這不就違背了前后端分離的真諦了嗎?
哎,為了平復(fù)我忐忑的心,我心中不禁念起了大悲咒:“南無(wú)·喝啰怛那·哆啰夜耶...”
...在電光火石之間,我的心中閃過(guò)了一個(gè)念頭,得到了一個(gè)解決辦法。
這個(gè)辦法也許隱世的前端高手也正在使用,但除了這里,你很難百度到,如果對(duì)你有幫助,請(qǐng)打賞一下。
如上所述,微信跳轉(zhuǎn)時(shí),瀏覽器會(huì)把#后面的hash值給搞掉,據(jù)說(shuō)在ios中還會(huì)在前面加點(diǎn)東西。但是在#之前加的get參數(shù)是不會(huì)丟失的。
那么我們?yōu)槭裁床荒馨褏?shù)前移呢?像這樣:
http://xxxx.xxx.com/?appid=1&cpid=1
頁(yè)面跳轉(zhuǎn)怎么辦?我可以加一個(gè)page參數(shù)呀
http://xxxx.xxx.com/?appid=1&cpid=1&page=/about
這樣寫(xiě)就完全丟棄了#
然后我們通過(guò)一個(gè)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;
}
我把這個(gè)方法放到了一個(gè)文件中,并作為全局對(duì)象引入,當(dāng)然,你也可以直接放到main.js里
在main.js里,router.beforeEach這個(gè)鉤子之前,將獲得的參數(shù)賦值給一個(gè)全局對(duì)象。
global.browserQuery = {
appid: getQueryString('appid'),
cpid: getQueryString('cpid'),
page: getQueryString('page')
}
在router.beforeEach的鉤子里這樣寫(xiě):
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不是空對(duì)象,說(shuō)明是從外面的鏈接點(diǎn)擊首次進(jìn)入的,這時(shí)候?qū)⑺枰膮?shù)寫(xiě)進(jìn)store中,剩下的page是要跳轉(zhuǎn)到的路由,賦值給p變量,然后清空browserQuery對(duì)象,使下次跳轉(zhuǎn)不再進(jìn)入這個(gè)條件代碼段,然后用return next(p)跳轉(zhuǎn)到該去的頁(yè)面。
可是一測(cè)試,問(wèn)題又來(lái)了。在本地測(cè)試(沒(méi)發(fā)布到微信)
鏈接:http://xxxx.xxx.com/?appid=1&cpid=1&page=/about
還是跳轉(zhuǎn)到根目錄,但后面加#/卻能正確跳轉(zhuǎn)到about頁(yè)面
http://xxxx.xxx.com/?appid=1&cpid=1&page=/about#/
為什么呢?唯一的解釋是,沒(méi)有hash值,router沒(méi)有準(zhǔn)備好,所以默認(rèn)跳轉(zhuǎn)到根目錄。但#/會(huì)被微信丟掉呀,加了也沒(méi)用,這不矛盾嗎?
能不能跳轉(zhuǎn)兩次?先跳到#/,這時(shí)router加載了,再跳轉(zhuǎn)到#/about
所以我寫(xiě)成這樣
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);
}
///其它代碼
})
頁(yè)面進(jìn)來(lái)后,刪掉appid,并讓它先跳轉(zhuǎn)到根目錄'/',這時(shí)appid已經(jīng)不在,所以不會(huì)進(jìn)入條件一,而會(huì)進(jìn)入else,因?yàn)閜age還在,清空browserQuery,并跳轉(zhuǎn)到該去的頁(yè)面about,因?yàn)樘D(zhuǎn)前為根目錄'/',hash值已在,應(yīng)當(dāng)不會(huì)出現(xiàn)上述問(wèn)題。
但一測(cè)試,還是不行。
我猜想這個(gè)router.beforeEach鉤子是不支持這樣瞬間跳轉(zhuǎn)兩次的。也就是在return next('/') 之后,不會(huì)立即執(zhí)行下一個(gè)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);//因?yàn)闆](méi)有next對(duì)象,直接調(diào)用router跳轉(zhuǎn)
}
})
當(dāng)router導(dǎo)航到根目錄'/'之后,馬上跳轉(zhuǎn)到about頁(yè)面,這時(shí)雖然是路由成功,但根目錄頁(yè)面還沒(méi)有渲染,其實(shí)看不出有什么差別。成功了。
對(duì)!就這么解決了困擾單頁(yè)的多入口問(wèn)題了。
如果你要將其中一個(gè)頁(yè)面分享出去,可以這樣組織鏈接,例如
http://xxxx.xxx.com/?appid=1&cpid=1&page=/detail/1
當(dāng)其他人點(diǎn)擊鏈接后,會(huì)被導(dǎo)航到
http://xxxx.xxx.com/#/detail/1頁(yè)面
從此你可以告別#的困擾,單頁(yè)應(yīng)用從此不再受微信的歧視。
吸了一口維他奶,感嘆啊。業(yè)界難題被我輕而易舉解決了,如果我不當(dāng)程序員,真實(shí)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);//因?yàn)闆](méi)有next對(duì)象,直接調(diào)用router跳轉(zhuǎn)
}
})