美文网首页
vue页面缓存的实现方以及与原生的部分效果对比

vue页面缓存的实现方以及与原生的部分效果对比

作者: Darren151666 | 来源:发表于2019-04-17 16:06 被阅读0次

    前言

    作为移动开发者,相信大家对于使用html开发的App和使用原生Api开发的App,或多或少都有所体验。他们有各自的优势与劣势,今天我们就参考原生App的效果,通过使用vue.js框架来实现一款类似原生效果的应用。

    项目简介

    联华会员线上退货流程的实现。主要功能点:选店、定位、上传图片、售后查询。实现方案:vue.js

    效果预览

    23.gif

    项目过程中遇到的难点与解决方案

    1、原生的页面push跳转是一个从右边往左进入的动画,pop操作是一个从左往右淡出的动画。下面介绍如何使用vue的Router和transition 来达到这种效果。

    • 所有的页面都通过vue-router进行管理,给router-view加一个动画。
    <transition :name="transitionName">
          <keep-alive>
            <router-view  class="Router"></router-view>
          </keep-alive>
    </transition>
    
    • 如何获取当前页面是push操作还是pop操作?监听浏览器的popstate 事件。通过在入口文件中监听popstate事件来标记当前的页面是不是pop操作,如果是pop操作就给当前的路由添加一个属性isBack
    window.addEventListener('popstate', function (e) {
      router.isBack = true
    }, false)
    
    • 监听路由的isBack属性,给当前页面设置push动画还是pop动画。至此,这种动画效果就实现了。
    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操作会缓存已经加载的页面,pop操作会销毁页面。h5的跳转使用vue-router进行管理,如果不对页面进行特殊的缓存处理,h5的页面会在push时销毁当前页面,重新创建新页面,这种体验明显的不如原生体验,下面我们介绍下如何使用vue的页面缓存机制来达到原生的这种效果。

    • keep-alive是Vue提供的一个抽象组件,用来对组件进行缓存,从而节省性能。他有2个常用属性include、exclude属性。include属性表示只有name属性为xxx的组件会被缓存,(注意是组件的名字,不是路由的名字)。exclude属性表示除了name属性为xxx的组件不会被缓存,其它组件都会被缓存。这里我们使用include属性,将push操作的页面缓存起来。

    1)第一步:必须设置页面的name属性。

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

    2)第二步:在main.js入口文件中监听router.isBack属性,判断是push还是pop,如果是push就将页面的name存到一个数组中,如果不是就从数组中移除该页面,同时这个数组我们通过vuex存储起来。这个数组中存储的页面就是我们需要缓存的页面。

    router.beforeEach((to, from, next) => {
      let isBack = router.isBack
      let arr = store.state.keepAlivePages.slice()
      if (isBack) {
        // 从数组中移除
        let index = arr.indexOf(from.name)
        if (index !== -1) {
          arr.splice(index, 1)
        }
      } else {
        // 加入数组,push操作都要加入缓存数组
        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) 最后在主页面中设置我们需要缓存的页面

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

    3、如何实现原生界面的侧滑返回效果?

    • 通过判断手势的滑动可以实现侧滑返回的效果,但是和原生的侧滑相比还是有一些效果差距的。需要注意的是在主页面的手势响应可能会影响到滑块Slider的滑动,我们可以在通过修饰符@touchmove.stop 阻止手势的响应,达到不影响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() {
            // 滚动视图可能会导致左滑,所以要判断y方向的距离
            if (this.distance > 100 && Math.abs(this.distanceY) < 50) {
              this.$refs.navigation.clickBack()
            } else {
            }
            this.distance = 0
          }
    }
    

    4、如何实现全局的导航栏?以及导航栏的标题、返回按钮的显示等功能?

    • 在定义路由的时候,我们需要通过meta定义一些可配置的字段,比如是否显示导航栏、导航栏标题、是否显示返回按钮、是否显示Tabbar等等。
    {
        path: '/',
        name: 'Home',
        meta: {
          title: '推荐', // 导航栏标题
          showTabbar: true, // 是否显示Tabbar
          showBack: false
        },
        component: resolve => require(['../views/home.vue'], resolve)
    },
    
    • 然后在主页面(一般是App.vue)中设置导航栏等配置
    <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中导航栏的标题取的是一个变量navTitle,为什么要通过变量取值?是因为有这样一种场景:比如进入商品详情页面,导航栏的标题是服务器返回的商品名称,这种情况页面已经渲染完成,再通过meta设置标题是没有效果的。所以目前的方案是用通知的方式,把标题传递过来,然后赋值。
    mounted() {
          // 动态改变详情页面的title
          this.bus.$on('changeDetailTitle', (title) => {
            this.$set(this.$route.meta, 'title', title)
            this.navTitle = title
          })
    }
    

    综述

    原生App的体验效果确实优于H5,但是通过以上的各种效果的优化,目前我们的H5项目已经能够更加接近原生的体验效果,特别是页面缓存这一块意义重大。

    最后附上效果图,大家注意pop后页面的内容哦,是不是和原生很像.......

    24.gif

    相关文章

      网友评论

          本文标题:vue页面缓存的实现方以及与原生的部分效果对比

          本文链接:https://www.haomeiwen.com/subject/itagwqtx.html