一次与客户端合作的走坑之旅!

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

  

posted @ 2019-03-19 14:28  雨吻蝶  阅读(1062)  评论(1编辑  收藏  举报