最近实际项目中遇到的技术问题与解决思路
距上一篇博客发布已经过去整整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。