国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

在移動端使用vue-router和keep-alive的方法示例

2019-11-19 12:25:53
字體:
來源:轉載
供稿:網友

對于web開發和移動端開發,兩者在路由上的處理是不同的。對于移動端來說,頁面的路由是相當于棧的結構的。vue-router與keep-alive提供的路由體驗與移動端是有一定差別的,因此常常開發微信公眾號的我想通過一些嘗試來將兩者的體驗拉近一些。

目標

問題

首先一個問題是keep-alive的行為。我們可以通過keep-alive來保存頁面狀態,但這樣的行為對于類似于APP的體驗是有些奇怪的。例如我們的應用有首頁、列表頁、詳情頁3個頁面,當我們從列表頁進入詳情頁再返回,此時列表頁應當是keep-alive的。而當我們從列表頁返回首頁,再次進入列表頁,此時的列表頁應當在退出時銷毀,并在重新進入時再生成才比較符合習慣。

第二個問題是滾動位置。vue-router提供了 scrollBehavior 來幫助維護滾動位置,但這一工具只能將頁面作為滾動載體來處理。但我在實際開發中,喜歡使用flex來布局頁面,滾動列表的載體常常是某個元素而非頁面本身。

使用環境

對于代碼能正確運行的環境,這里嚴格假定為微信(或是APP中內嵌的web頁面),而非通過普通瀏覽器訪問,即:用戶無法通過直接輸入url來跳轉路由。在這樣的前提下,路由的跳轉是代碼可控的,即對應于vue-router的push、replace等方法,而唯一無法干預的是瀏覽器的回退行為。在這樣的前提下,我們可以假定,任何沒有通過vue-router觸發的路由跳轉,是 回退1個記錄 的回退行為。

改造前

這里我列出改造前的代碼,是一個非常簡單的demo,就不詳細說了(這里列表頁有兩個列表,是為了展示改造后的滾動位置維護):

// css* { margin: 0; padding: 0; box-sizing: border-box;}html, body { height: 100%;}#app { height: 100%;}
// html<div id="app"> <keep-alive>  <router-view></router-view> </keep-alive></div>
// jsconst Index = { name: 'Index', template: `<div>  首頁  <div>   <router-link :to="{ name: 'List' }">Go to List</router-link>  </div> </div>`, mounted() {  console.warn('Main', 'mounted'); },};const List = { name: 'List', template:  `<div style="display: flex;flex-direction: column;height: 100%;">  <div>列表頁</div>  <div style="flex: 1;overflow: scroll;">   <div v-for="item in list" :key="item.id">    <router-link style="line-height: 100px;display:block;" :to="{ name: 'Detail', params: { id: item.id } }">     {{item.name}}    </router-link>   </div>  </div>  <div style="flex: 1;overflow: scroll;">   <div v-for="item in list" :key="item.id">    <router-link style="line-height: 100px;display:block;" :to="{ name: 'Detail', params: { id: item.id } }">     {{item.name}}    </router-link>   </div>  </div> </div>`, data() {  return {   list: new Array(10).fill(1).map((_,index) => {    return {id: index + 1, name: `item${index + 1}`};   }),  }; }, mounted() {  console.warn('List', 'mounted'); }, activated() {  console.warn('List', 'activated'); }, deactivated() {  console.warn('List', 'deactivated'); },};const Detail = { name: 'Detail', template: `<div>  詳情頁  <div>   {{$route.params.id}}  </div> </div>`, mounted() {  console.warn('Detail', 'mounted'); },};const routes = [ { path: '', name: 'Main', component: Index }, { path: '/list', name: 'List', component: List }, { path: '/detail/:id', name: 'Detail', component: Detail },];const router = new VueRouter({ routes,});const app = new Vue({ router,}).$mount('#app');

當我們第一次從首頁進入列表頁時, mounted 和 activated 將被先后觸發,而在此后無論是進入詳情頁再回退,或是回退到首頁再進入列表頁,都只會觸發 deactivated 生命周期。

keep-alive

includes

keep-alive有一個 includes 選項,這個選項可以接受一個數組,并通過這個數組來決定組件的保活狀態:

// keep-aliverender () { const slot = this.$slots.default const vnode: VNode = getFirstComponentChild(slot) const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions if (componentOptions) {  const name: ?string = getComponentName(componentOptions)  const { include, exclude } = this  if (   (include && (!name || !matches(include, name))) ||   (exclude && name && matches(exclude, name))  ) {   return vnode  }  const { cache, keys } = this  const key: ?string = vnode.key == null   ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')   : vnode.key  if (cache[key]) {   vnode.componentInstance = cache[key].componentInstance   remove(keys, key)   keys.push(key)  } else {   cache[key] = vnode   keys.push(key)   if (this.max && keys.length > parseInt(this.max)) {    pruneCacheEntry(cache, keys[0], keys, this._vnode)   }  }  vnode.data.keepAlive = true } return vnode || (slot && slot[0])}

這里我注意到,可以動態的修改這個數組,來使得本來處于保活狀態的組件/頁面失活。

afterEach

那我們可以在什么時候去維護/修改includes數組呢?vue-router提供了 afterEach 方法來添加路由改變后的回調:

updateRoute (route: Route) { const prev = this.current this.current = route this.cb && this.cb(route) this.router.afterHooks.forEach(hook => {  hook && hook(route, prev) })}

在這里雖然 afterHooks 的執行是晚于路由的設置的,但組件的 render 是在 nextTick 中執行的,也就是說,在keep-alive的render方法判斷是否應當從緩存中獲取組件時,組件的保活狀態已經被我們修改了。

劫持router.push

這里我們將劫持router的push方法:

let dir = 1;const includes = [];const routerPush = router.push;router.push = function push(...args) { dir = 1; routerPush.apply(router, args);};router.afterEach((to, from) => { if (dir === 1) {  includes.push(to.name); } else if (dir === -1) {  includes.pop(); } dir = -1;});

我們將router.push(當然這里需要劫持的方法不止是push,在此僅用push作為示例)和瀏覽器的回退行為用不同的 dir 標記,并根據這個值來維護includes數組。

然后,將includes傳遞給keep-alive組件:

// html<div id="app"> <keep-alive :include="includes">  <router-view></router-view> </keep-alive></div>// jsconst app = new Vue({ router, data() {  return {   includes,  }; },}).$mount('#app');

維護滾動

接下來,我們將編寫一個 keep-position 指令(directive):

Vue.directive('keep-position', { bind(el, { value }) {  const parent = positions[positions.length - 1];  const obj = {   x: 0,   y: 0,  };  const key = value;  parent[key] = obj;  obj.el = el;  obj.handler = function ({ currentTarget }) {   obj.x = currentTarget.scrollLeft;   obj.y = currentTarget.scrollTop;  };  el.addEventListener('scroll', obj.handler); },});

并對router進行修改,來維護position數組:

const positions = [];router.afterEach((to, from) => { if (dir === 1) {  includes.push(to.name);  positions.push({}); } ...});

起初我想通過指令來移除事件偵聽(unbind)以及恢復滾動位置,但發現使用unbind并不方便,更重要的是指令的幾個生命周期在路由跳轉到保活的頁面時都不會觸發。

因此這里我還是使用 afterEach 來處理路由維護,這樣在支持回退多步的時候也比較容易去擴展:

router.afterEach((to, from) => { if (dir === 1) {  includes.push(to.name);  positions.push({}); } else if (dir === -1) {  includes.pop();  unkeepPosition(positions.pop({}));  restorePosition(); } dir = -1;});const restorePosition = function () { Vue.nextTick(() => {  const parent = positions[positions.length - 1];  for (let key in parent) {   const { el, x, y } = parent[key];   el.scrollLeft = x;   el.scrollTop = y;  } });};const unkeepPosition = function (parent) { for (let key in parent) {  const obj = parent[key];  obj.el.removeEventListener('scroll', obj.handler); }};

最后,我們分別給我們的列表加上我們的指令就可以了:

<div style="flex: 1;overflow: scroll;" v-keep-position="'list1'"> <!-- --></div><div style="flex: 1;overflow: scroll;" v-keep-position="'list2'"> <!-- --></div>

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 苏州市| 江安县| 五常市| 滨海县| 云和县| 民权县| 治县。| 眉山市| 日喀则市| 兴文县| 龙泉市| 五指山市| 平阳县| 莱阳市| 大渡口区| 安龙县| 汉川市| 伊吾县| 滦南县| 丰顺县| 西乡县| 象州县| 东海县| 华蓥市| 曲麻莱县| 柳江县| 顺义区| 石狮市| 南靖县| 泗水县| 浪卡子县| 德江县| 资兴市| 宁阳县| 女性| 古蔺县| 抚顺县| 郓城县| 平昌县| 高阳县| 桐柏县|