需求描述
最近加班加点完成了一个上传文件的需求,听起来很简单想着也很简单,但是知易行难实际操作起来,还是遇到了很多问题,现在就来总结一下,使用vue来构建一个上传文件的页面中会遇到哪些问题,其中的一些问题也可能是用别的框架也会遇到的问题。
平台:主要是在安卓和苹果的微信webview中运行。
先来看下主要的技术点:
- 父子组件的动态渲染
- 初始化数据和判断数据
- input表单隐藏联动
- 上传图片后可以预览
遇到的问题:
- 在iPhone中,click()事件无效
- 在微信安卓webview中,img标签使用background属性无效
- 预览上传图片保证宽高比
- 在某些安卓机型用rem单位不显示边框
设计图大概是这样的:
完成后的效果是这样:
- 首先登陆后,如果没有选择两个选项的话提交按钮点击不了并会有提示
- 当选择图片后,下面会显示选择图片的缩略图
- 当点击提交后,会显示loading成功后会刷新页面
- 提交过的部分不会显示示意图会改为一张提示图片,刚上传过所有图片时会提示已经上传过所有图片
大概就是这么一个步骤流程吧。
技术点分析
这个页面主要使用的UI包括:提示、loading、选项栏等都是从WEUI样式库中扒出来的,因为是在微信公众号中使用的,体验上会一致。上传部分使用的是post表单上传,我把form表单隐藏了起来,通过模拟上传按钮,当点击上传按钮的时候同步触发表单中的input type=‘file’标签,调起原生的选择图片的界面,选择图片后,点击上传按钮的同时触发一个post请求把图片传到后台,完成整个功能。
父子组件的动态渲染
可以从设计图看出上传图片的部分基本上设计是一样的,所以用一个vue组件来复用是最好的,顺便说一下这个页面用到了三个组件,一个是dialog提示的组件,一个是loading的组件,还有一个就是上传这部分用到的组件。
主要说一下上传部分的组件,在vue中,子父组件要传递的参数需要在子组件的props中声明,下面是我的组件中的模板,我主要用到了以下参数:
1 | <template> |
然后在父组件中我们可以引入这个组件,并声明它,然后在标签中把子组件需要用到的参数传递进去就好了。我这里是使用了v-for
去遍历了一个result_data
的数组,把所有需要上传的组件遍历渲染了出来。如下:
1 | <template> |
这样子父组件就可以传递数据了,具体的大家可以去看一下官方的文档。
初始化数据和判断数据
这是这个任务里最坑的一个部分。因为后台只会把已经上传过图片的字段告诉我,所以我需要在前端去造一些初始化的数据,去和后台返回回来的比较,再去判断哪些上传过需要展示出来,那些没上传需要上传。
注意:可以造数据的前提是,数据量不是很大,且数据是固定的。其实最好是后台可以完成这些判断,因为前端本就不应该去处理数据方面的逻辑。
我初始化的数据大概是这个样子的:
1 | init_data: [ |
然后我去遍历后台返回的那个数组和我自己初始化得数组,用name
这个字段产生关联去比较,因为后台只返回了上传过图片的字段,所以我就可以判断出哪些是没有上传的部分。判断的逻辑如下:
1 | compareArr (oldArr, newArr) { |
我会给这个方法传入两个数组。oldArr是后台的数组,newArr是我自己初始化的数组。
循环遍历比较两个数组,把我自己初始化的数据做出一些修改,主要是提示图片和是否上传标识字段的更改。
input表单隐藏联动
这个逻辑也不是很复杂,就是在点击上传按钮的时候,调一个方法,在方法中选择到对应的input
标签出发一个click()
事件,主要是在iphone上面,可能会不支持input type=‘file’的标签,这里有一个坑,在网上找到了一些解决办法,但是试了一下没有效果,最后自己试验了一下找到了一个解决办法,在下面遇到的问题中会叙述。
第二是要注意在form表单中要禁止事件冒泡,不然你点击一次input表单就会提交一次。
上传图片后可以预览
可以我是汲取了广大人民的智慧。。。
主要思路就是把图片读取成一个base64格式,然后再把他装到一个img标签中。实现预览功能,这里我给几个参考的资料,有的demo直接就可以跑,改一改就可以运用到自己的项目中。
大概看完他们的思路以后,就能够实现这个功能了,但是他并不能保持宽高比适配屏幕,后面我优化了一下这部分。
遇到的问题
遇到的这四个问题我们来一一解决:
- 在iPhone中,click()事件无效
- 在微信安卓webview中,img标签使用background属性无效
- 预览上传图片保证宽高比
- 在某些安卓机型用rem单位不显示边框
在iPhone中click()事件无效
这个问题在网上也有类似问题,地址,但是我依照网上的办法都不行,但是我发现显式的在标签内通过onclick=”document.querySelector(‘input’).click()这种方式是可以调用的,所以我把所有的我需要隐式调用input的地方都绑定了这个onclick="document.querySelector('input').click()
,然后逻辑方面我又用vue的@click绑定了一个方法来处理逻辑方面。
img标签使用background属性无效
这个可能是安卓的微信webview支持的不好,因为在iphone和别的手机浏览器当中都可以支持,但是在安卓的微信webview不能显示,这个是兼容性的问题,所以最后我选择在div
中加background。
预览上传图片保证宽高比
这个其实也不难优化,因为上面的方法是把base64的图片放到一个img标签当中,所以没法去适配这个宽高比,如果把这个放到div
的background-image
属性当中,并把background-size
设置成contain
,把background-color
设置成页面背景一样的颜色,这样就能模拟出浏览图片的功能了。
在某些安卓机型用rem单位不显示边框
因为移动端使用的是rem为单位,用postcss去处理(就是在css中写px,后处理器去处理成rem),在1px时转换为rem单位可能不足屏幕显示的1px,则不会显示边框。解决办法就是当写border
时加注释去避免处理器去处理这句css(/*px*/
)
整个过程大概就是这样吧,我把这个页面放到了github上,但是没有vue的环境跑不起来,如果有需要可以拷到自己vue项目下去尝试。代码垃圾,轻喷。。。。。。
顺嘴说一句,那天晚上加班到两点,然后坐车回家,到地方付款的时候,发现银行维护没法付款,而且微信里也没有余额,正在想怎么办司机师傅说,没事赶紧回家吧等能付款的时候付了就好了,一句赶紧回家吧真实一阵感动啊。。。