距上一篇博客发布已经过去整整 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://www.cnblogs.com/lunlunshiwo/p/9222456.html