1. 程式人生 > >最近實際專案中遇到的技術問題與解決思路

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

  距上一篇部落格釋出已經過去整整2個月。這兩個月中發生了一些事情,比如離職,面試,入職等等,感觸頗多。其實一次好的面試,即使沒有成功入職也會有很多收穫。

  這次面試面了三家公司,拿了兩家公司的offer,但是最讓我中意的面試卻沒拿到offer,原因是下午去面試,精神狀況不太好,有點疲倦並且反應有點遲鈍,導致整個面試很糟糕。不過面試官還是很nice的,給我梳理了一下頭緒,拓展了一下思維,並且在後來的交流中給了我一些建議。準確的說這其實是一次成功的面試,長進很多。

  最近一個月有點懶,說著努力進步的話做著混吃等死的事。因此給自己定了一個目標,每個月保證有20天晚上堅持學習,一週至少一篇部落格,完成之後給自己一個小獎勵,比如一雙喜歡的AJ11之類的。

  入職新公司20來天吧,跟進了一個專案,pc端的運營後臺和移動端的混合開發app,算是現在專案組同時在做pc和移動端的唯一一個吧,有時候很懵逼,開啟編輯器第一反應是看看這是哪一端的程式碼。在這兩端的專案中幫同事改了幾個bug。

  1.改進自己寫過的一個城市選擇元件

// 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節點後繼續向下滾動一個畫素,再次觸發一次滾動事件,解決。

  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

  以上就是最近遇到的問題與改進解決思路。

  當初因為覺得很酷炫選擇了這條路,就要好好地走下去,晚安。