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. 安全性问题: 一直找不到比较安全的加密解密方式,并且我以为这样作是会有安全隐患,但不知道究竟哪一种场景会让这种安全性问题暴露的最大;
相关文章
相关标签/搜索