一次与客户端合作的走坑之旅!
项目经理给客户端提出了个需求,加急做用户反馈界面,很急,安卓客户端项目开发就一个人,表示干不了,短时间内完成不了,于是商量了一番,把前端的我搅和进去了,让新增的用户反馈界面用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/