项目中遇到的难题

技术-项目中遇到的难题

# 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 个可增删树结构

  1. 带有与或非关系的,可增删不定层级的树形组件 难点:带有与或非的关系、根节点是个动态表单。
  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 图表、百度地图的各种效果图

# 组件的封装要注意什么

  1. 传入数据的验证。数据从父组件传入时应在 props 中加一些限制或验证
  2. 事件处理尽量抛出到父组件处理,组件本身只做中转
  3. 尽量提高可扩展性,利用插槽、render 函数、v-html 等方法。比如表格组件里面单元格内容要支持传入各种定制化的内容
  4. 二次封装的时候,利用$listeners和$attrs 保留原组件的方法和属性
  5. 公共样式不要写在组件内,组件独特样式加 scoped 保持独立

遇到的坑

遇到的坑

日常项目中遇到的坑记录