Vue路由快照實現思路及其問題

前言:不管構建SPA仍是MPA, 組件的狀態是沒法被保存下來的,這對於開發過程當中問題的重現是比較麻煩的,由於老是會失去上下文環境,致使重現過程變得繁瑣。因而想到了將Vue Component相關信息動態綁定在路由上。本文將給出其實現思路以及相關問題。

場景重現

在使用Vue開發完應用後,應用上線進入了測試階段。測試人員測試出現問題後會對頁面進行截圖,並將頁面地址和截圖內容發送給開發人員進行bug的肯定和修改。這是比較常規的方式,但這對開發人員是很是不友好的,由於開發人員拿到的URL地址時,即沒有測試人員的本地數據,也須要經過繁瑣的操做從新按照測試人員所填寫的內容進行上下文環境的重現。爲何咱們不能將這些數據保存下載,測試人員將URL發送給開發人員以後,開發者能很容易定位到上下文環境並進行錯誤的重現及調試。javascript

爲何是URL

不管你的數據是保存在內存仍是Store,亦或是存放在WebDB中,都會遇到一個問題:你永遠都沒法拿到測試人員的數據。那麼惟一的方式就是經過URL來傳輸數據。所以,咱們的構想是:當界面加載組件後,將組件的部分屬性的變化公開到URL上,同時,組件在渲染時,讀取URL後將值解析還原到組件上去。這樣,即便不斷的刷新頁面,組件的狀態也不會發生改變。前端

實現

因而,咱們爲這個功能編寫了一個Vue插件,取名路由快照(router-snapshot),其實現代碼以下:vue

// router-snapshot.js
// https://github.com/dankogai/js-base64
import { Base64 } from 'js-base64';

function beforeRouteEnterHandler (vm, {key, ext}) {
  // 獲取路由綁定字段
  const routeBindKeys = vm.$options[ext] || [];
  // 獲取路由綁定部分的加密字符串
  const routeParamsString = vm.$route.query[key];
  // 解密並轉換爲JSON
  let routeParamsJSON;
  try {
    routeParamsJSON = JSON.parse(Base64.decode(routeParamsString));
  }catch (e) {
    routeParamsJSON = {};
  }
  routeBindKeys.forEach(attr => {
    // 使用vue的是指方式,若瀏覽器沒有緩存值,則獲取組件默認值
    vm.$set(vm, attr, routeParamsJSON.hasOwnProperty(attr) ? routeParamsJSON[attr] : vm[attr]);
    // 追加屬性反向監聽,監聽到的屬性變化都會呈如今路由上
    vm.$watch(attr, (value) => {
      const query = vm.$route.query;
      let routeSnapshotValueJSON;
      try {
        routeSnapshotValueJSON = JSON.parse(Base64.decode(query[key]));
      }catch (e) {
        routeSnapshotValueJSON = {};
      }
      routeSnapshotValueJSON[attr] = value;
      const extendQuery = {};
      extendQuery[key] = Base64.encodeURI(JSON.stringify(routeSnapshotValueJSON));
      vm.$router.push({
        query: { 
          ...query,
          ...extendQuery
        }
      })
    }, {
      deep: true
    });
  })
}

export default {
  install (Vue, {key = '_', ext = 'routeShot'} = {}) {
    Vue.mixin({
      // beforeRouteEnter (to, from, next) {
      //   console.log('beforeRouteEnter', to, from)
      //   next(beforeRouteEnterHandler)
      // }
      created () {
        beforeRouteEnterHandler(this, {key, ext});
      }
    });
  }
}

代碼邏輯大體以下:java

  1. 代碼45行,註冊該組件時,咱們須要指定保存在URL query部分的鍵名,默認爲_;同時指定綁定在組件上的拓展屬性名,默認爲routeShot
  2. 代碼21行,根據組件拓展屬性,對這些拓展屬性實施監聽,將屬性值的變化同步到路由中;
  3. 代碼19行,在組件created階段,獲取路由參數並解析成組件屬性,並將屬性值同步到組件中;
  4. 代碼1三、2五、31行對路由上的參數進行Base64的加密和解密;

組件的代碼僅僅須要追加routeShot的配置便可:git

<template>
    <!-- 使用的iview庫的Switch組件 -->
    <Switch v-mode="switchValue"></Switch>
</template>
<script>
  export default {
    // 配置routeShot,指定該組件的switchValue屬性映射到URL中
    routeShot: ['switchValue'],
    data () {
      return {
        switchValue: false
      }
    }
  }
</script>

通過這樣,不管你怎麼刷新頁面,被快照的屬性都不會發生改變。另外,除了data屬性,propcomputed屬性也是能夠綁定到URL上的。github

何時用最適合?

目前來講,應用場景中最多的仍是非安全性表單以及不須要持久化的數據。舉幾個例子:瀏覽器

  1. 表格中篩選項有不少的狀況下,用戶進行了大量的選擇和填寫操做,結果由於網絡緣由致使請求失敗。待網絡恢復後,用戶從新刷新頁面,先前的操做必須從新執行;通常狀況中,用戶不會隨意更改瀏覽器的URL,在這種條件下,用戶的刷新不影響上下文的環境,能給用戶帶來必定便利;
  2. 以前代碼示例中,開關組件的值不交予服務端進行持久化,也是可使用這種方式來保存操做的;

存在的問題

寫完這個插件,面臨了三個我認爲比較重要的問題:緩存

  1. 性能問題: 經過代碼47-50行能夠看出,早期設計是將插件應用在路由組件中的,可是在後期的測試和使用中,發現還有不少組件不是註冊在路由中的,也就是父子組件,這樣的組件沒法被路由鉤子攔截到,所以就將該函數混入到了全部組件的created函數中。當應用愈來愈大、組件愈來愈多的時候,這個性能未免有點使人擔心;
  2. 持久性問題: 當URL的query部分愈來愈大的時候,超過了URL的長度限制,那麼組件屬性的持久性將會被中斷。但咱們並不能保證該長度不會超過,這隨着應用的增加是沒法預料的。在前端中,咱們沒有找到對應的庫能進行定長加密解密,若是能找到,這個或將被解決;
  3. 安全性問題: 一直找不到比較安全的加密解密方式,並且我以爲這樣作是會有安全隱患,但不知道究竟哪一種場景會讓這種安全性問題暴露的最大;
相關文章
相關標籤/搜索