vue-router中scrollBehavior的妙用

1. keep-alive

  • 問題: 使用keep-alive標籤後部分安卓機返回緩存頁位置不精確問題html

  • 解決方案:vue

<div id="app">
    <keep-alive>
      <router-view v-if="$route.meta.keepAlive"></router-view>
    </keep-alive>
    <router-view v-if="!$route.meta.keepAlive"></router-view>
</div>
複製代碼
const router = new Router({
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition && to.meta.keepAlive) {
      return savedPosition;
    }
    return { x: 0, y:0 };
  },
});
複製代碼

2. 頁面返回出現空白屏問題

  • 問題
【前提】:iOS設備
【步驟】: 頁面A是個列表很長-->滑到頁腳的時候點擊跳轉以後到頁面B--->再返回A頁面
         --->屏幕會出現空白遮罩層--->手指輕觸屏幕滑動--->遮罩層消失
複製代碼

問題圖片

解決方案一

在接口請求成功後的回調操做完成後進行該操做,例如緩存

// fetchCourseList是一個封裝好的Promise請求
fetchCourseList().then(({ data: courses }) => {
  this.courses = courses;
}).then(() => {
    setTimeout(() => {
        window.scrollTo(0, 1);
        window.scrollTo(0, 0);
    });
});
複製代碼

該方案的弊端: 每一個頁面都須要作這樣的處理,不推薦使用。bash

解決方案二(推薦)

使用scrollBehavior中的異步滾動操做app

const router = new Router({
  scrollBehavior(to, from, savedPosition) {
    // keep-alive 返回緩存頁面後記錄瀏覽位置
    if (savedPosition && to.meta.keepAlive) {
      return savedPosition;
    }
    // 異步滾動操做
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve({ x: 0, y: 1 });
      }, 0);
    });
  },
});
複製代碼

該方案直接在路由進行處理,兼容每一個頁面而且頁面加載完後並也不會產生1px的滾動位置。異步

這裏爲何不能直接return而必須使用異步滾動操做呢?如下是我的的一些看法歡迎你們來探討指正。

  1. 首先咱們要先去了解scrollBehavior函數究竟在組件的哪一個生命週期後纔開始執行。這裏我對組件的每一個生命週期和scrollBehavior函數進行alert,經排查結果:scrollBehavior函數在組件的生命週期mounted後beforeUpdate前執行。函數

  2. 在scrollBehavior函數中直接return{ x:0, y:100},進入頁面仍在頂部。爲何不會滾動到100px處?猜想:mounted中的異步請求回來的數據賦值給data中的變量a,變量a由於vue的雙向綁定更新了view層而引發滾動失效?fetch

  3. 驗證下以上的猜想,設置一個靜態頁面數據都已經在html上寫死。scrollBehavior函數中直接return { x:0, y: 100},結果:進入頁面都會滾動到100px處。證實:確實與異步請求回來後的操做有關係。ui

  4. 接着瞭解vue的mounted和beforeUpdate時期都作了些什麼。mounted時期:data數據已經掛在到頁面上。beforeUpdate和updated時期:當vue發現data中的數據發生了改變,會觸發對應組件的從新渲染。this

  5. 根據步驟4.mounted時期發起的異步請求並不會阻礙主線程的後續操做,因此請求回調事件未觸發(對data中的變量a賦值操做未執行)便繼續去執行scrollBehavior函數。若是此時直接return{ x:0, y:100}。此時至關於在異步請求回調事件未執行前進行了滾動。等到滾動後異步請求回調事件開始執行,對data中的變量a被賦值,引發組件從新渲染又回到了頂部。這整個流程滾動是針對data的初始數據頁進行滾動的,因此遮罩層仍會出現。

  6. 綜合上述:必須使用異步滾動,利用setTimeout跳出主線程將回調事件放到隊列中。因爲mouted比scrollBehavior函數早執行,因此異步請求的回調事件優先進入隊列,接下去纔是setTimeout的回調事件。根據隊列 先進先出的原理。先執行了異步請求回調事件對data中的變量a作賦值操做。此時至關於這已是個靜態頁面了,接着我只要執行return { x:0, y: 100 }。這樣就已經觸發了頁面滾動到100px的效果。可是因爲data數據發生改變,頁面從新渲染又回到頂部。這時整個輕觸滾動效果已經暗中執行完成,不會再出現遮罩層了。

相關文章
相關標籤/搜索