實踐場景需求
- 產品列表中,滾動到一定位置的時候,點擊查看產品信息,后退之后,需要回到原先的滾動位置,這是常見的需求
- 所有頁面均在router-view中,暫時使用了keep-alive來緩存所有頁面,所以進入不同分類的產品列表,和不同的產品詳情頁面,需要更新數據
首先注意:
- 本次實踐測試環境為pc端的webkit內核瀏覽器,手機暫時不測試
- 使用$router.back(-1) 和瀏覽器后退按鈕效果一樣
- 必須使用keep-alive緩存路由頁面才能記住上次的位置,否則使用瀏覽器后退或$router.back后退都會重新加載數據,使原來的內容高度改變
- 使用keep-alive之后,路由頁面才能使用activated事件
記錄上次列表滾動的高度位置,后退時恢復
思路很簡章,在路由元信息中設置一個變量:scrollToTop,即標記是否要回到頂部,而我們的產品頁面productList是要恢復上次滾動高度的,不回到頂部,所以設置為false
當離開路由的時候,還是判斷這個變量是否為false,是則記錄滾動的高度到vuex中 (所以我們這個變量有2個作用,你要維護2個也可以)
然后每當進入路由頁面的時候,如果本路由的scrollToTop為false,則從vuex中讀取上次記錄的高度,並恢復
首先要注意一點,vue單頁應用,切換路由時,滾動高度不會變,因為切換路由時只是切換了頁面的內容,與滾動高度
當你在某路由頁面的時候,滾動了100像素,然后切換了新的路由頁面(改變了頁面中的內容),只要新的路由頁也可以滾動出100像素, 后退時,滾動高度不變, 這可以說是"BUG"
所以下面代碼我們默認把 scrollToTop為true的設置滾動高度為0
我路由中的配置:
routes: [{ path: '/', name: 'home', component: Home, meta: { title: "鳳凰旅游" ,scrollToTop:true} }, { path: '/product', name: 'product', component: () => import('./views/Product.vue'), meta: { title: "旅游" ,scrollToTop:true} }, { path: '/productList/:id', name: 'productList', component: () => import('./views/productList.vue'), meta: { title: "列表" ,scrollToTop:false} }, { path: '/productShow/:id', name: 'productShow', component: () => import('./views/productShow.vue'), meta: { title: "旅游產品顯示" ,scrollToTop:true} } ]
接下來在store.js中,維護一個變量用於記錄
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { scrollTop :0 }, mutations: { recordScrollTop(state,n){ state.scrollTop = n } }, actions: { } })
在main.js中的相關路由事件里
router.beforeEach(function(to,from,next){ document.title = to.meta.title // 要離開頁面如果設置為不滾回到頂部,則本頁是要記住上滾動高度到vuex中,以便下次進來恢復高度 if(from.meta.scrollToTop==false) { store.commit('recordScrollTop', document.documentElement.scrollTop) } next() }) router.afterEach((to, from) => { // 如果進入后的頁面是要滾動到頂部,則設置scrollTop = 0 //否則從vuex中讀取上次離開本頁面記住的高度,恢復它 if(to.meta.scrollToTop==true){ setTimeout(()=>{ document.documentElement.scrollTop = 0 },10) }else{ setTimeout(()=>{ document.documentElement.scrollTop = store.state.scrollTop },50) } });
/*
* 讀取上次記錄的滾動高度並且設置,必須放在router.afterEach里面
* 由於緩存了產品列表頁面,每次進入會判斷如果進入的是否為新的類別,則清空數據再重新加載數據,這時想設置scrollTop就無效,應該是視圖渲染更新是異步的原因
* 看官方文檔說調用afterEach之后,才觸發 DOM 更新
* 所以afterEach里面的有涉及到DOM的操作,放在setTimeout里面,否則我試出了亂子..
*/
使用了keep-alive緩存了頁面之后,當參數不同的時候,更新數據
當進入不同分類的產品列表頁面,或不同id的產品頁面,由於緩存了上次的結果,當然要我們來處理更新
首先上面有說到,使用了keep-alive,路由頁面便可以使用activated事件,因為使用了keep-alive其它普通的生命周期只執行了一次,而activated每次顯示頁面都會激活(類似小程序的onShow),必須使用這個來更新
思路:在頁面data中維護一個id,默認為0 ,每次進這個頁面時調用activated事件,在事件時面,判斷這個id是否與路由url中的參數一致
如果不一致,則根據這個id更新相關數據,並且 把data中的id,更新為新的id ,別忘了還要清空上次分類的產品數據
當然別忘了,你應該初始讀取一次數據,比如在created里面, 而activated第一次創建頁面時不會激活,緩存之后,第二次進入才會開始激活(我想應該是如此。。)
上代碼:
activated(){ //由於緩存了本頁面,每次激活頁面都要判斷是否重置相關參數,並重新加載數據 if(this.id !== this.$route.params.id){this.id = this.$route.params.id //更新分類id this.curpage = 1 //初始化頁面為1 this.product = [] //清空上次不同分類的產品數據 this.getProduct('/api/productList.php',this.id,this.curpage).then((res)=>{ this.ptotal = res.total res.products.forEach((item)=>{ this.product.push (item) }) this.loading = false }) } }
如果更新或更正再補充。