vue頁面緩存的實現(xiàn)方以及與原生的部分效果對比

前言

作為移動開發(fā)者,相信大家對于使用html開發(fā)的App和使用原生Api開發(fā)的App,或多或少都有所體驗。他們有各自的優(yōu)勢與劣勢,今天我們就參考原生App的效果,通過使用vue.js框架來實現(xiàn)一款類似原生效果的應(yīng)用。

項目簡介

聯(lián)華會員線上退貨流程的實現(xiàn)。主要功能點:選店、定位、上傳圖片、售后查詢。實現(xiàn)方案:vue.js

效果預(yù)覽

23.gif

項目過程中遇到的難點與解決方案

1、原生的頁面push跳轉(zhuǎn)是一個從右邊往左進(jìn)入的動畫,pop操作是一個從左往右淡出的動畫。下面介紹如何使用vue的Router和transition 來達(dá)到這種效果。

  • 所有的頁面都通過vue-router進(jìn)行管理,給router-view加一個動畫。
<transition :name="transitionName">
      <keep-alive>
        <router-view  class="Router"></router-view>
      </keep-alive>
</transition>
  • 如何獲取當(dāng)前頁面是push操作還是pop操作?監(jiān)聽瀏覽器的popstate 事件。通過在入口文件中監(jiān)聽popstate事件來標(biāo)記當(dāng)前的頁面是不是pop操作,如果是pop操作就給當(dāng)前的路由添加一個屬性isBack
window.addEventListener('popstate', function (e) {
  router.isBack = true
}, false)
  • 監(jiān)聽路由的isBack屬性,給當(dāng)前頁面設(shè)置push動畫還是pop動畫。至此,這種動畫效果就實現(xiàn)了。
watch: {
   $route(to, from) {
        // 切換動畫
        let isBack = this.$router.isBack
        if (isBack) {
          this.transitionName = 'slide-left'
        } else {
          this.transitionName = 'slide-right'
        }
        this.$router.isBack = false
   }
}

2、頁面的緩存與銷毀問題
原生的App的push操作會緩存已經(jīng)加載的頁面,pop操作會銷毀頁面。h5的跳轉(zhuǎn)使用vue-router進(jìn)行管理,如果不對頁面進(jìn)行特殊的緩存處理,h5的頁面會在push時銷毀當(dāng)前頁面,重新創(chuàng)建新頁面,這種體驗明顯的不如原生體驗,下面我們介紹下如何使用vue的頁面緩存機(jī)制來達(dá)到原生的這種效果。

  • keep-alive是Vue提供的一個抽象組件,用來對組件進(jìn)行緩存,從而節(jié)省性能。他有2個常用屬性include、exclude屬性。include屬性表示只有name屬性為xxx的組件會被緩存,(注意是組件的名字,不是路由的名字)。exclude屬性表示除了name屬性為xxx的組件不會被緩存,其它組件都會被緩存。這里我們使用include屬性,將push操作的頁面緩存起來。

1)第一步:必須設(shè)置頁面的name屬性。

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Main',
      component: Main,
      meta: {
      }
    }
  ]
})

2)第二步:在main.js入口文件中監(jiān)聽router.isBack屬性,判斷是push還是pop,如果是push就將頁面的name存到一個數(shù)組中,如果不是就從數(shù)組中移除該頁面,同時這個數(shù)組我們通過vuex存儲起來。這個數(shù)組中存儲的頁面就是我們需要緩存的頁面。

router.beforeEach((to, from, next) => {
  let isBack = router.isBack
  let arr = store.state.keepAlivePages.slice()
  if (isBack) {
    // 從數(shù)組中移除
    let index = arr.indexOf(from.name)
    if (index !== -1) {
      arr.splice(index, 1)
    }
  } else {
    // 加入數(shù)組,push操作都要加入緩存數(shù)組
    let index = arr.indexOf(from.name)
    if (index === -1) {
      arr.push(from.name)
    }
    let indexTo = arr.indexOf(to.name)
    if (indexTo === -1) {
      arr.push(to.name)
    }
  }
  store.commit('SET_KEEPALIVEPAGES', arr)
  next()
})

3) 最后在主頁面中設(shè)置我們需要緩存的頁面

 <transition :name="transitionName">
      <keep-alive :include="keepAlivePages">
        <router-view class="Router"></router-view>
      </keep-alive>
</transition>

3、如何實現(xiàn)原生界面的側(cè)滑返回效果?

  • 通過判斷手勢的滑動可以實現(xiàn)側(cè)滑返回的效果,但是和原生的側(cè)滑相比還是有一些效果差距的。需要注意的是在主頁面的手勢響應(yīng)可能會影響到滑塊Slider的滑動,我們可以在通過修飾符@touchmove.stop 阻止手勢的響應(yīng),達(dá)到不影響Slider的滑動問題。
<template>
  <div id="app" v-on:touchstart="bodyTouchStart" v-on:touchmove="bodyTouchMove" v-on:touchend="bodyTouchEnd">
    <transition :name="transitionName">
      <keep-alive :include="keepAlivePages">
        <router-view class="Router" :style="routerHeightStyle"></router-view>
      </keep-alive>
    </transition>
  </div>
</template>

methods: {
      bodyTouchStart(event) {
        this.touchStartPoint = event.targetTouches[0].pageX
        this.touchStartPointY = event.targetTouches[0].pageY
      },
      bodyTouchMove(event) {
        // 實時計算distance
        this.distance = event.targetTouches[0].pageX - this.touchStartPoint
        this.distanceY = event.targetTouches[0].pageY - this.touchStartPointY
      },
      bodyTouchEnd() {
        // 滾動視圖可能會導(dǎo)致左滑,所以要判斷y方向的距離
        if (this.distance > 100 && Math.abs(this.distanceY) < 50) {
          this.$refs.navigation.clickBack()
        } else {
        }
        this.distance = 0
      }
}

4、如何實現(xiàn)全局的導(dǎo)航欄?以及導(dǎo)航欄的標(biāo)題、返回按鈕的顯示等功能?

  • 在定義路由的時候,我們需要通過meta定義一些可配置的字段,比如是否顯示導(dǎo)航欄、導(dǎo)航欄標(biāo)題、是否顯示返回按鈕、是否顯示Tabbar等等。
{
    path: '/',
    name: 'Home',
    meta: {
      title: '推薦', // 導(dǎo)航欄標(biāo)題
      showTabbar: true, // 是否顯示Tabbar
      showBack: false
    },
    component: resolve => require(['../views/home.vue'], resolve)
},
  • 然后在主頁面(一般是App.vue)中設(shè)置導(dǎo)航欄等配置
<template>
  <div id="app" v-on:touchstart="bodyTouchStart" v-on:touchmove="bodyTouchMove" v-on:touchend="bodyTouchEnd">
    <!--nav-->
    <navigation
                ref="navigation"
                v-if="!$route.meta.hiddenNav"
                :title="navTitle"
                :showBack="showBack"
                :background="$route.meta.navBackground"
                :showLine="$route.meta.showLine"></navigation>
    <transition :name="transitionName">
      <keep-alive :include="keepAlivePages">
        <router-view  class="Router" :style="routerHeightStyle"></router-view>
      </keep-alive>
    </transition>
    <tabBar v-show="$route.meta.showTabbar"></tabBar>
  </div>
</template>
  • 需要注意的是,在上面的demo中導(dǎo)航欄的標(biāo)題取的是一個變量navTitle,為什么要通過變量取值?是因為有這樣一種場景:比如進(jìn)入商品詳情頁面,導(dǎo)航欄的標(biāo)題是服務(wù)器返回的商品名稱,這種情況頁面已經(jīng)渲染完成,再通過meta設(shè)置標(biāo)題是沒有效果的。所以目前的方案是用通知的方式,把標(biāo)題傳遞過來,然后賦值。
mounted() {
      // 動態(tài)改變詳情頁面的title
      this.bus.$on('changeDetailTitle', (title) => {
        this.$set(this.$route.meta, 'title', title)
        this.navTitle = title
      })
}

綜述

原生App的體驗效果確實優(yōu)于H5,但是通過以上的各種效果的優(yōu)化,目前我們的H5項目已經(jīng)能夠更加接近原生的體驗效果,特別是頁面緩存這一塊意義重大。

最后附上效果圖,大家注意pop后頁面的內(nèi)容哦,是不是和原生很像.......

24.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容