最近實際項目中遇到的技術問題與解決思路


  距上一篇博客發布已經過去整整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

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM