项目中遇到的难题
/
技术-项目中遇到的难题
# 1. 关于 for 循环中有异步操作时的 BUG
//例
for (var i = 0; i < 22; i++) {
console.log('i', i) // 这里i打印出来 是0-21
var arr = [points[i]]
var convertor = new BMap.Convertor()
//下面是一个异步操作
convertor.translate(arr, 1, 5, (data) => {
console.log('i', i) // 这里i打印出来 是22,22,22,22...22.因为在整个for循环运行完毕后才会开始运行异步操作。FOR循环最后一论是21+1=22
//坐标转换完之后的回调函数
if (data.status === 0) {
baiDuPoints.push(...data.points)
if (i === 21) {
//因为i都是22,所以这里不会运行
this.drawMap(baiDuPoints)
}
}
})
}
- 在 FOR 循环中有异步操作时,如果 i 是 var 申明,异步操作中的回调函数会在整个 FOR 循环结束后才会执行
- 因为 var 出来的 i ,在整个 for 循环里是同一个,所以最后打印出的都是 22.如果换成 let,有了作用域,i 就会每次循环独立,打印出 0-21。
- 但是,由于 convertor.translate 是异步操作,返回的时间不一定是按运行顺序,所以打印出的并不是 0-21,有可能是 2,1,0,3....
解决办法:
- 在每一次循环里面,将返回值和 index 组成对象,塞到数组里。然后判断数组长度和循环的次数是否相等,相等的话就是最后一次循环了,就去根据 index 做一个排序,拍完序再 map 单独返回出数据
let baiDuPointsOBJ = []
for (let i = 0; i < points.length; i++) {
console.log(i) //0-21
var arr = [points[i]]
var convertor = new BMap.Convertor()
convertor.translate(arr, 1, 5, (data) => {
console.log(i) //0-21,但是顺序是乱的
//坐标转换完之后的回调函数
if (data.status === 0) {
//连index一起组成对象插入数组
baiDuPointsOBJ.push({ index: i, points: data.points[0] })
//当新组成的数组长度等于原数组长度(所有异步操作都已经执行完)
if (baiDuPointsOBJ.length === points.length) {
//先利用index排序,然后取出points,得到一个正确顺序的需要的数组
let baiDuPoints = baiDuPointsOBJ
.sort((a, b) => {
return a.index - b.index
})
.map((item) => item.points)
this.drawMap(baiDuPoints)
}
}
})
}
# 2. 2 个可增删树结构
- 带有与或非关系的,可增删不定层级的树形组件 难点:带有与或非的关系、根节点是个动态表单。
- 样式特殊的可增删树形结构。 难点:样式为顶部居中,且无论中间有多少层,最后根结点都在最右边对齐。无法套用 echarts 或其他图表插件表格去画。原组员的任务,搞不定换我来的
- 方案:递归整理数据结构,利用边框、伪元素等绘制线条和符号。动态组件渲染动态表单
# 3.vuex 缓存时多次加解密导致的加载缓慢
加了防抖
# 在百度地图里自定义标注嵌入 vue 组件
需求是在百度地图放入路口的标注,要求显示当前的相位图。是用 canvas 画的,逻辑很复杂。有已经实现好的 vue 组件,但是百度地图的标注只接受 html。如果改成 html 和 js 在写一遍很麻烦。 想想能不能用 vue 渲染出来,然后获取到 html 插到地图上。
- 注意,以下方法都只能展示。click 事件是没有用的。因为贴进去的只有 html。如果需要 click 事件,需要手动获取元素然后绑定
办法 1
再页面中渲染出来,隐藏显示。然后用 ref 获取到 html,传到百度地图 api 中
setTimeout(() => {
let html = this.$refs['bampPhaseChart1'][0].$el.outerHTML
let label = new BMap.Label(html, {
position: new BMap.Point(item.longitude, item.latitude), // 指定文本标注所在的地理位置
offset: new BMap.Size(16, -16), // 设置文本偏移量
})
_this.ICMap.addOverlay(label)
// this.drawCanvas(index, phaseList, crossInfo)
}, 0)
办法 2
利用 vue.extend
let Content = Vue.extend({
//自定义模板继承
template: `<phaseChart :index="connectIndex" :phaseData="phaseData" :channelList="crossInfo.LampgroupInfo"></phaseChart>`,
// name: 'child',
components: {
phaseChart: phaseChart, //弹框用子组件包裹
},
data() {
return {
connectIndex: connectIndex,
phaseData: phaseData,
crossInfo: crossInfo,
}
},
})
let component = new Content().$mount()
let label = new BMap.Label(component.$el.outerHTML, {
position: new BMap.Point(cross.longitude, cross.latitude), // 指定文本标注所在的地理位置
offset: new BMap.Size(20, -140), // 设置文本偏移量
})
_this.ICMap.addOverlay(label)
# 根据渠化信息画真实的路口图
利用canvas,先设定以区域中心点设定坐标系,根据路口数据,利用数学函数计算,画出道路情况以及人行道、相位图。路口处利用贝塞尔曲线画出弧度。并且会更具数据更新重新绘画。利用canvas的缩放解决各个地方引用时大小不一致的问题。
# 封装了哪些组件
1.常见的公共组件,比如 table、菜单、头部、按钮、标题。可拖拽的弹窗。因为一个系统的风格、功能往往接近,所以封装后统一使用,维护更加方便 2.复杂业务性组件:树形结构、路口渠化图(canvas 画的比较复杂的图,很多地方用到)、动态表单。 3.各种 echarts、map 的图表和地图组件 3.做过一个 npm 插件包。里面封装了一些常见的组件,还有各种 echarts 图表、百度地图的各种效果图
# 组件的封装要注意什么
- 传入数据的验证。数据从父组件传入时应在 props 中加一些限制或验证
- 事件处理尽量抛出到父组件处理,组件本身只做中转
- 尽量提高可扩展性,利用插槽、render 函数、v-html 等方法。比如表格组件里面单元格内容要支持传入各种定制化的内容
- 二次封装的时候,利用$listeners和$attrs 保留原组件的方法和属性
- 公共样式不要写在组件内,组件独特样式加 scoped 保持独立