二维码项目中踩坑总结
1.组件先于接收的请求参数;
解决方案:
①子组件watch监听后进行数据相关操作;
watch: {
qrcodeList: {
deep: true,
handler(val) {
this.getLogos(val)
}
}
},
②父组件引用的时候使用v-if懒加载方式;
2.对数组中的item做数据转化(增加字段)
解决方案:在计算属性computed遍历做转化;
注意:computed是优于methods和watch执行的,如若使用到watch中的数据源,watch之后computed会再次执行,computed自由缓存机制;
renderList() {
return this.qrcodeList.map((item, index) => {
item.qrImg = this.logoImages[item.logoFileId] ?
this.logoImages[item.logoFileId] : '';
return item;
})
}
3.场景问题:根据数组中的某id通过接口去异步获取id对应的某个值,
每个item是否有值是不确定的,最后需要将id和值一一对应;
getLogos(list) {
let ids = []
list.map((item, index) => {
if (item.logoFileId) {
ids.push(item.logoFileId)
}
})
qrcodeService.getFiles(ids).then((res) => {
let [err, images] = res;
// this.logoImages = images
var imgObj = {};
var arr = images;
arr.map(item => {
imgObj[item.name] = item.src
})
this.logoImages = imgObj
})
},
4.场景问题:引用二维码渲染插件,参数为一个对象,对象中的数据需要做特殊处理,对象最后还需要处理的情况,插件还在数组中需要遍历;
<div class="code-item" v-for="(qrItem,index) in qrcodeList" :key="index">
<QRCode :options="options" />
</div>
实现思路:
① <QRCode :options="{
size: item.size,
data: item.url,
logo: item.logo
}" />
存在问题:
a.此处无直接的中间的logo url,需要做数据处理;
方案:对qrcodeList做处理,首先watch qrcodeList数据源,进行异步接口处理,得到相应的logo url,放入一个对象数组;再根据qrcodeList生成一个新的list包含一一对应的含log url的新字段;
①
watch: {
qrcodeList: {
deep: true,
handler(val) {
this.getLogos(val)
}
}
},
②
getLogos(list) {
let ids = []
list.map((item, index) => {
if (item.logoFileId) {
ids.push(item.logoFileId)
}
})
qrcodeService.getFiles(ids).then((res) => {
let [err, images] = res;
// this.logoImages = images
var imgObj = {};
var arr = images;
arr.map(item => {
imgObj[item.name] = item.src
})
this.logoImages = imgObj
})
},
③
computed: {
renderList() {
return this.qrcodeList.map((item, index) => {
item.qrImg = this.logoImages[item.logoFileId] ?
this.logoImages[item.logoFileId] : '';
return item;
})
}
},
b. 此处的options直接通过item相关的键值渲染,无法实现处理数据this.options的相关操作;
解决方案:
新增一个中间子组件qrcode,将处理好的item相关的数据传入到子组件,在子组件中拼装options;
父组件:
import qrcode from './qrcode';
<qrcode class="qrImg"
:logo="item.logourl"
:size="defaultSize"
:data="item.qrUrl" />
子组件:
<QRCode :options="options" />
data() {
return {
options: {
size: this.size,
data: this.data,
logo: this.logo
}
};
},
watch得到logo的url时对options进项后续操作;
watch: {
logo(src) {
const image = new Image();
image.src = src;
image.onload = () => {
this.options = Object.assign({}, this.options, {
logo: {
image
}
});
};
}
},
5.下载相关问题;
①vue-qr插件,生成二维码是通过将二维码图片和logo图片转化成blob的方式,如若logo的url地址不是同域,而是阿里云之类的,会出现logo跨域问题,暂未发现解决方式;(无法生成带logo二维码)
②qrcanvas-vue插件,生成二维码方式是通过canvas方式,跨域logo可直接生成,但是生成canvas图片本质上也有跨域下载问题,是否可转化为url或文件流等下载?待尝试(可生成带logo二维码,无法下载?)
③通过后端返回文件流形式的二维码图片;
存在问题:
a.接口返回的response非普通接口含有相应的errcode等字段,无法使用项目中统一封装的axios,需要重新实例化一个全新的axios;
b.请求传参问题;
https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/responseType
解决方案:传参时和header同级的地方添加responseType: 'blob',
return axios.post(${baseUrl}/download/qrCode
, pars, {
responseType: 'blob',
headers: byHeaders()
})
如若是文档等,需要responseType: 'arraybuffer',
文件流下载的两种方式:
let blob = new Blob([res.data], { type: 'image/jpeg' });
// const blob = res.data;
const reader = new FileReader();
reader.onload = (e) => {
const a = document.createElement('a');
a.download = item.qrName;
// 后端设置的文件名称在res.headers的 "content-disposition": "form-data; name=\"attachment\"; filename=\"20181211191944.zip\"",
a.href = e.target.result;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
let a = document.createElement('a');
let blob = new Blob([res.data], { type: 'image/jpeg' });
let objectUrl = URL.createObjectURL(blob);
a.setAttribute('href', objectUrl);
let downName = item.qrName
a.setAttribute('download', `${downName}(${size}x${size})`);
a.click();
下载中如果下载的文件名包含各种特殊字符,下载的文件会出现无后缀名,无法显示的问题;
// 处理特殊字符
const pattern = /[`~!@#$^&*()=|{}':;',\\\[\]\.<>\/?~!@#¥……&*()——|{}【】';:""'。,、?\s]/g;
downName = downName.replace(pattern, '');
待强化问题:
1.深刻理解computed,watch,filter等不同使用场景及区别;
2.深刻理解canvas,blob,base64,url和文件流等相关区别;