項目經理給客戶端提出了個需求,加急做用戶反饋界面,很急,安卓客戶端項目開發就一個人,表示干不了,短時間內完成不了,於是商量了一番,把前端的我攪和進去了,讓新增的用戶反饋界面用H5開發。甩鍋於前端,經理說趕緊做,當天要!於是那天臨近午飯時間,通知這個事情讓我干,我心里是真的不是滋味,心里mmp。隨即給我UI,告訴我說UI會裁好圖給我。這圖一直到下午4點,離下班還有兩個小時的時候才給我的。這種做事效率讓人心塞啊!但是也顧不得抱怨了,三下五除二,使用vue+vue-router+vant足以滿足需求開發了。最終在下班之前還是打包交給服務器端了。
服務器端部署項目之后,打開界面,白屏界面不出來,而且還不報錯!這就尷尬了,沒報錯那是真的難找問題。他們服務器的人也倒騰了半天,沒效果,我就不信邪,我說我以前打包的項目都是這樣的,沒出現過這樣的問題。我就把這個項目部署在了自己的阿里雲服務器上,試試看,卻能看到界面效果!這就摸不着頭腦了,問題還是要解決的。試了幾次,終於找到了問題,router的history模式導致的問題。因為我自己的服務器的nginx配置時對這個mode進行了處理的,所有是沒有問題,而這個項目的服務器端時沒有對這個模式進行匹配處理的。那就先來說說vue-router的history模式了。
HTML5 History模式
vue-router 默認 hash 模式 —— 使用 URL 的 hash 來模擬一個完整的 URL,於是當 URL 改變時,頁面不會重新加載。如果不想要很丑的 hash,我們可以用路由的 history 模式,這種模式充分利用 history.pushState API 來完成 URL 跳轉而無須重新加載頁面。
const Router = require('vue-router') const router = new Router({ mode:'history', routes: [...] })
當使用history模式時,url就像正常的url如https://i.cnblogs.com/EditPosts.aspx?opt=1。不過這種模式要玩好,還需要后台配置支持。因為我們的應用是個單頁客戶端應用,如果后台沒有正確的配置,當用戶在瀏覽器直接訪問 http://oursite.com/user/id
就會返回 404,這就不好看了。所以呢,你要在服務端增加一個覆蓋所有情況的候選資源:如果 URL 匹配不到任何靜態資源,則應該返回同一個 index.html
頁面,這個頁面就是你 app 依賴的頁面。
后台服務器nginx的配置需加上:
location / {
try_files $uri $uri/ /index.html;
}
完整的vue-router 的history模式可以點此學習:https://router.vuejs.org/zh/guide/essentials/history-mode.html
由於當時服務器沒有對這個history模式進行nginx配置,我只好把router處的mode:'history'去掉。后面那些路由不好看的啥子東西服務器那邊配置的時候自行解決了。
本以為都告一段落了,誰知這才剛剛開始。測試站測試人員提bug了。客戶端測試人員用的安卓機,可不是正常的手機,是用那種低端機進行測試的,所以與我們在稍好一點的手機上看到的效果差別還是很明顯的。測試人員說:進入H5頁面會有閃屏出現,那個banner圖片會閃一下再出來,體驗很不好。
我看了一下效果之后,我那是就有點底了,h5剛出來的時候,本應該是banner占據的位置,被下面的內容占據了,我心中一想可能是沒有給圖片的容器進行高度設置,才讓這種閃屏出現。我覺得還應該對圖片進行壓縮處理,因為在移動端,對圖片的壓縮還是非常有必要的。雖然有時候有些損失圖片的質量,但是其實在移動端,還是很難覺察到差別的。經過了對圖片的高度設置和壓縮之后,這種閃屏好了很多。
解決了這個閃屏bug,滿以為可以了。可是客戶端的需求是不斷改變的。客端戶需要在無網絡狀態下,進入h5首頁,然后連網之后,可以在頁面上進行交互。
我當時腦子是蒙的,我是拒絕的,我說這個應該是你們客戶端解決的事情吧,客戶端人員說需要三份代碼,因為我們坐的這個用戶反饋h5是有三種不同的語言的,中文、印尼語和英文,所以還是需要打包三份代碼,用以針對三種不同語言的需求。以保存在客戶端本地。打包好三份代碼給客戶端之后,客戶端在無網絡的情況下,打開用戶反饋app,會自動調取本地存儲的文件,這個時候問題就出來了,里面的icon圖片位置有破圖出現,而且點擊進行交互沒有反應,客戶端顯示的h5發起的請求地址居然是本地地址。這兩個bug我是這樣解決的:
1、icon圖片破圖
當圖片不存在時,出觸發onerror事件,這個時候我們就可以處理一下來讓網頁美觀一些,有兩個方法
- 讓這個圖片元素隱藏
<img src="圖片的url地址" alt="圖片XX" onerror="this.style.display='none'"/>
- 用默認的圖片替換
<img src="圖片的url地址" alt="圖片XX" onerror="this.src='默認圖片的url地址'"/>
我使用的第二種方法,在vue中我們可以這樣使用:
data() { return { defaultImage: "this.src="+require('./assets/error.png') } }
<img src="圖片的url地址" alt="圖片XX" onerror="defaultImage"/>
2、無網絡狀態下調用客戶端本地的文件,之后打開網絡后,進行頁面交互發起的請求地址不對
使用vue+webpack打包上線的的文件,是需要部署在線上服務器的,能夠以http等協議進行訪問,在項目里面進行api請求,在線上都是以域名進行訪問請求的,比如域名是:https://olqa.faceworld.top/,進行的api請求就是 https://olqa.faceworld.top/api/getCatogryList/lang=zh#/。當使用客戶端本地的文件時,進行的請求訪問地址就變成了 file://XXX/api/getCatogryList/lang=zh#/,這樣的請求交互當然是不生效的。后來服務器開發人員給了我一個建議,讓我寫絕對地址,就是在交互請求的地址上把域名加上去。如下:
axios({
url: 'https://olqa.faceworld.top/olqa/api/faq/getList.do',//把域名加上去
params: data,
method: 'POST',
}).then(res => {
if(res.status == 200 && res.data.data.length > 0) {
this.page ++;
this.searchList = this.searchList.concat(res.data.data);
}else if(res.status != 200){
this.loading = false
}else {
console.log('沒有結果');
this.finished = true;
}
this.loading = false
}).catch(err => {
console.log('net timeout');
this.loading =false;
})
之后進行打包交給客戶端存儲在本地。這也算是一種解決方法吧。
任何項目完成上線都不是一帆風順的。這不,測試人員說在低端機上,h5的首屏加載時間有點長,讓我去研究研究改善用戶體驗。vue的SPA頁面都是通過打包后的js文件進行解析后生成dom進行頁面渲染的,如果對於js文件的解析比較慢,是會導致首屏加載時間較長。想着如果進行預渲染,可能會有出奇效果。找到了一款插件:prerender-spa-plugin,
可以把頁面單獨打包出來,而且打包出來的index.html文件可以直接訪問打開,如果放在客戶端本地的話,效果應該不錯。於是就試試:
1、安裝
npm install prerender-spa-plugin --save-dev
2、webpack.prod.conf.js增加部分代碼
const PrerenderSPAPlugin = require('prerender-spa-plugin') //引用插件 const Renderer = PrerenderSPAPlugin.PuppeteerRenderer const webpackConfig = merge(baseWebpackConfig, { plugins: [ // vue-cli生成的配置中就已有這個了,不要動 new HtmlWebpackPlugin({ filename: config.build.index, template: 'index.html', inject: true, minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true }, chunksSortMode: 'dependency' }), // 配置PrerenderSPAPlugin new PrerenderSPAPlugin({ // 生成文件的路徑,也可以與webpakc打包的一致。 staticDir: path.join(__dirname, '../dist'), // 對應自己的路由文件,比如index有參數,就需要寫成 /index/param1。 routes: ['/', '/detail','/search'], // 這個很重要,如果沒有配置這段,也不會進行預編譯 renderer: new Renderer({ inject: { foo: 'bar' }, headless: false, // 在 main.js 中 document.dispatchEvent(new Event('render-event')),兩者的事件名稱要對應上。 renderAfterDocumentEvent: 'render-event' }) }) ] })
3、在main.js中加入以下代碼
new Vue({ el: '#app', router, render: h => h(App), mounted () { document.dispatchEvent(new Event('render-event')) } })
4、使用npm run build打包,dist文件里面有每個路由對應的包
而且這里面直接打開index.html文件能夠進行訪問。
把這種打包后的文件給了客戶端,確實讓首屏的加載速度上去了。只是這樣打包的文件要比以前打包的文件體積稍大一點,不過綜合比較,覺得這樣的體驗稍好一些,因為都是本地資源,加載速度還是比較快的。
與客戶端合作之旅暫告一段落!
個人博客地址:https://www.zengfanping.com/