前言
作為移動開發(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ù)覽
項目過程中遇到的難點與解決方案
1、原生的頁面push跳轉(zhuǎn)是一個從右邊往左進入的動畫野崇,pop操作是一個從左往右淡出的動畫称开。下面介紹如何使用vue的Router和transition 來達到這種效果。
- 所有的頁面都通過vue-router進行管理,給router-view加一個動畫鳖轰。
<transition :name="transitionName">
<keep-alive>
<router-view class="Router"></router-view>
</keep-alive>
</transition>
- 如何獲取當前頁面是push操作還是pop操作清酥?監(jiān)聽瀏覽器的popstate 事件。通過在入口文件中監(jiān)聽popstate事件來標記當前的頁面是不是pop操作蕴侣,如果是pop操作就給當前的路由添加一個屬性isBack
window.addEventListener('popstate', function (e) {
router.isBack = true
}, false)
- 監(jiān)聽路由的isBack屬性焰轻,給當前頁面設(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進行管理,如果不對頁面進行特殊的緩存處理客冈,h5的頁面會在push時銷毀當前頁面旭从,重新創(chuàng)建新頁面,這種體驗明顯的不如原生體驗场仲,下面我們介紹下如何使用vue的頁面緩存機制來達到原生的這種效果和悦。
- keep-alive是Vue提供的一個抽象組件,用來對組件進行緩存渠缕,從而節(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)弃锐,達到不影響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)航欄的標題支竹、返回按鈕的顯示等功能旋廷?
- 在定義路由的時候,我們需要通過meta定義一些可配置的字段礼搁,比如是否顯示導(dǎo)航欄饶碘、導(dǎo)航欄標題、是否顯示返回按鈕馒吴、是否顯示Tabbar等等扎运。
{
path: '/',
name: 'Home',
meta: {
title: '推薦', // 導(dǎ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)航欄的標題取的是一個變量navTitle饮戳,為什么要通過變量取值豪治?是因為有這樣一種場景:比如進入商品詳情頁面,導(dǎo)航欄的標題是服務(wù)器返回的商品名稱扯罐,這種情況頁面已經(jīng)渲染完成负拟,再通過meta設(shè)置標題是沒有效果的。所以目前的方案是用通知的方式歹河,把標題傳遞過來掩浙,然后賦值。
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)容哦矾麻,是不是和原生很像.......