阅读 1144

最近实际项目中遇到的技术问题与解决思路

  距上一篇博客发布已经过去整整2个月。这两个月中发生了一些事情,比如离职,面试,入职等等,感触颇多。其实一次好的面试,即使没有成功入职也会有很多收获。

  这次面试面了三家公司,拿了两家公司的offer,但是最让我中意的面试却没拿到offer,原因是下午去面试,精神状况不太好,有点疲倦并且反应有点迟钝,导致整个面试很糟糕。不过面试官还是很nice的,给我梳理了一下头绪,拓展了一下思维,并且在后来的交流中给了我一些建议。准确的说这其实是一次成功的面试,长进很多。

  入职新公司20来天吧,跟进了一个项目,pc端的运营后台和移动端的混合开发app,算是现在项目组同时在做pc和移动端的唯一一个吧,有时候很懵逼,打开编辑器第一反应是看看这是哪一端的代码。在这两端的项目中帮同事改了几个bug。

  1.改进自己写过的一个城市选择组件

  我以前写过一篇博客《独立完成一个城市选择组件(阿里前端题目,内附知识点、思路)》,并且将这个组件的代码分享在了github上,地址是github.com/lunlunshiwo…。在这个项目中我引入了一个比较出色的插件better-scroll为我提供滚动事件,其中有一段代码是这样的:

// pos为位置参数
this.scroll.on('scroll', (pos) => {
   this.$emit('distance', Math.abs(pos.y))
   this.$emit('scrollStore', true)
})复制代码

  上面这段代码我的用意是触发滚动事件时应该向我返回滚动的位置,我在下面这段代码中用到了这个pos.y这个变量为我的组件在滚动时提供一个指示,比如点击右边的navList的A时,城市列表会滚动到A处,此时会有一个悬浮的卡片展示A。

    // 滚动到相应的dom节点
    singleLetter (dom) {
      this.$refs.suggest.scrollToElement(dom, 200, false, false)
    },
    // 根据滑动距离显示字母牌上的字
    distance (val) {
      for (let i = 0, len = this.arrHeight.length; i < len; i++) {
        if (val < this.arrHeight[i]) {
          this.flagText = this.cityIndexList[i]
          return false
        }
      }
    }复制代码

  以上为以前的代码,scrollToElement是组件提供的事件。

  理想很丰满,现实很骨感。直到端午节放假下班回家时的一天,一个人加了我的微信,给我说了一个bug:一定几率下,从上往下点击时会出现点击了E,列表滚动到E但是悬浮卡片只显示D的情况存在,而从下往上点击时不会存在。后来也得到了证实的确几率性的存在。但是作为我第一个发到github上的作品,我希望它是完美的。因此我花了端午假期在研究问题的所在,直到前几天偶尔想到了原因。我们在触发一下类似与mousemove的事件时,理想中应该一个像素触发一次,但是因为机器的性能的原因并不会这样。 “触发”这个行为,从硬件发中断,然后OS,产生消息,这个过程中,鼠标硬件本身判断自己移动了多少距离时产生硬件信号,800dpi之类的。主板上中断芯片的处理速度也会影响。也许每个版本的系统+硬件本身,这个时间片长短都不定。而程序本身由于GetMessage函数本身消耗的时间,与捕获 鼠标移动消息(switch分支的判断) 所需要的时间也不太确定。当你鼠标移动的比较慢时,可能每一个像素触发一次。鼠标比较快时,可能发现移动了n个像素才触发一次。

  这也就导致了上面bug的产生,当从上往下点击navList时,可能滚动到E这个列表的前10个像素时触发了一次,而滚动到E列表时也就有可能因为时间太短未触发,此时位置像素值实在D的最后10个像素上,忠诚的逻辑代码也就会因为这10个像素的误差不给我返回E这个字母。那我们要做的就是在滚动完成时让列表继续产生一个滚动事件,继续滚动一个像素即可。

  幸运的是同样是上面那个滚动事件scrollToElement为我提供了一个参数

  scrollToElement(el, time, offsetX, offsetY, easing)

  • 参数:返回值:无
    • {DOM | String} el 滚动到的目标元素, 如果是字符串,则内部会尝试调用 querySelector 转换成 DOM 对象。
    • {Number} time 滚动动画执行的时长(单位 ms)
    • {Number | Boolean} offsetX 相对于目标元素的横轴偏移量,如果设置为 true,则滚到目标元素的中心位置
    • {Number | Boolean} offsetY 相对于目标元素的纵轴偏移量,如果设置为 true,则滚到目标元素的中心位置
    • {Object} easing 缓动函数,一般不建议修改,如果想修改,参考源码中的 ease.js 里的写法
  • 作用:滚动到指定的目标元素。

  我们可以将上面的函数改为:

    // 滚动到相应的dom节点
    singleLetter (dom) {
      this.$refs.suggest.scrollToElement(dom, 200, false, 1)
    } 复制代码

  滚动到相关dom节点后继续向下滚动一个像素,再次触发一次滚动事件,解决。

  相关代码已更新,请放心使用,github.com/lunlunshiwo…,求个star。

  2.vue2.0s中eventBus的绑定与解绑

  在移动端项目中,有两个页面共用了一个我封装的列表组件,并且这两个页面都会有一个上拉获取更多的功能,因此,我做这个组件时使用eventBus作为兄弟组件传值的转接站。当页面滚动至底部并且触发上拉事件时向列表组件传递一个事件,在列表页面绑定一个获取更多的事件,并触发。代码如下:

//滚动组件
pullup(event) {  
   Bus.$emit('getMore');   
} 

//列表组件
created() {  
   Bus.$on('getMore', this.getMoreList);  
},
methods: {  
   getMoreList() { 
    //
    // 
    //
   }  
}   复制代码

  很不幸的是,这两个列表组件我共用了一个组件,导致了下面这个问题:当这两个列表切换几次之后,上拉刷新时会触发多次getMoreList事件,并且切换多少次就会出发多少次。百思不得骑姐之后我想到Bus.$on('getMore', this.getMoreList)比较类似原生Js的事件监听:

//伪代码
相关列表组件.addEventListener("getMore",getMoreList);
function getMoreList(){
 alert("hello world!");
}复制代码

  在上述的问题中,假如事件getMore与组件中的getMoreList绑定,即使组件销毁了,但是这个绑定关系还是存在的。等再次渲染组件时,created生命周期又会绑定一次事件,并且以前的绑定关系还是存在的,现在组件中有两个绑定关系,而且相同。因此,在组件销毁时,我们应清除组件中的这个绑定关系:

destroyed() {  
   Bus.$off('getMore', this.getMoreList);  
}复制代码

  3.路由前进后退时的切换动态

  打开手机app页面,当页面前进时,切换效果一般为从右向左滑动。当后退时,我们会希望他是从左向右退出的,但是vue提供的过渡效果只允许我们有一种的效果的存在,除非根据路由的切换来改变过渡效果绑定的name值。在实现这个效果的时候,我对原有的方法进行改进。我首先想到改写router.back()这个事件,但是因为觉得这样不太好,因此对这个进行了一层封装:

//router文件
Router.prototype.goBack = function () {
  store.commit("changeIsBack",true)
  this.back(-1)
}
//vuex文件
const state = {
  isBack:false
}
const mutations = {
  changeIsBack(state, flag) {
    state.isBack = flag
  }
}
export default {
  state,
  mutations
}复制代码

  这个封装中的this.back(-1)就是this.$router.back(-1)。在router中引入vuex的store文件,使用commit改变state的值。

//pageMain.vue文件
methods: {
    ...mapMutations([
      'changeIsBack'
    ])
}
beforeRouteUpdate (to, from, next) {
    let isBack = this.$store.state.routerState.isBack
    if (isBack) {
      this.transitionName = 'enter-right'
    } else {
      this.transitionName = 'enter-left'
    }
    this.changeIsBack(false)
    next()
}
//其余单页返回上一级
this.$router.goback()复制代码

  在页面展示页使用beforeRouteUpdate这个钩子函数检测store中isBack的值。当页面返回时调用this.$router.goback()这个方法,此时会改变store中isBack的值。当路由更新时,检测isBack是否为true,如果是则为返回的页面,此时更新过渡transition的name,达到更新的目的。方案地址:github.com/lunlunshiwo…

  以上就是最近遇到的问题与改进解决思路。

  当初因为觉得很酷炫选择了这条路,就要好好地走下去,晚安。


关注下面的标签,发现更多相似文章
评论