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

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

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

  最近一个月有点懒,说着努力进步的话做着混吃等死的事。因此给自己定了一个目标,每个月保证有20天晚上坚持学习,一周至少一篇博客,完成之后给自己一个小奖励,比如一双喜欢的AJ11之类的。

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

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

  我以前写过一篇博客《独立完成一个城市选择组件(阿里前端题目,内附知识点、思路)》,并且将这个组件的代码分享在了github上,地址是https://github.com/lunlunshiwo/ChooseCity。在这个项目中我引入了一个比较出色的插件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节点后继续向下滚动一个像素,再次触发一次滚动事件,解决。

  相关代码已更新,请放心使用,https://github.com/lunlunshiwo/ChooseCity,求个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,达到更新的目的。方案地址:https://github.com/lunlunshiwo/pageChange。

 

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

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

  掘金地址:https://juejin.im/post/5b304c65f265da59702de4c8。