浅谈Vue路由快照实现思路及其问题


Posted in Javascript onJune 07, 2018

前言:无论构建SPA还是MPA,组件的状态是无法被保存下来的,这对于开发过程中问题的重现是比较麻烦的,因为总是会失去上下文环境,导致重现过程变得繁琐。于是想到了将Vue Component相关信息动态绑定在路由上。本文将给出其实现思路以及相关问题。

场景重现

在使用Vue开发完应用后,应用上线进入了测试阶段。测试人员测试出现问题后会对页面进行截图,并将页面地址和截图内容发送给开发人员进行bug的确定和修改。这是比较常规的方式,但这对开发人员是非常不友好的,因为开发人员拿到的URL地址时,即没有测试人员的本地数据,也需要通过繁琐的操作重新按照测试人员所填写的内容进行上下文环境的重现。为什么我们不能将这些数据保存下载,测试人员将URL发送给开发人员之后,开发者能很容易定位到上下文环境并进行错误的重现及调试。

为什么是URL

无论你的数据是保存在内存还是Store,亦或是存放在WebDB中,都会遇到一个问题:你永远都无法拿到测试人员的数据。那么唯一的方式就是通过URL来传输数据。因此,我们的构想是:当界面加载组件后,将组件的部分属性的变化公开到URL上,同时,组件在渲染时,读取URL后将值解析还原到组件上去。这样,即使不断的刷新页面,组件的状态也不会发生改变。

实现

于是,我们为这个功能编写了一个Vue插件,取名路由快照(router-snapshot),其实现代码如下:

// 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});
   }
  });
 }
}

代码逻辑大致如下:

  1. 代码45行,注册该组件时,我们需要指定保存在URL query部分的键名,默认为_;同时指定绑定在组件上的拓展属性名,默认为routeShot;
  2. 代码21行,根据组件拓展属性,对这些拓展属性实施监听,将属性值的变化同步到路由中;
  3. 代码19行,在组件created阶段,获取路由参数并解析成组件属性,并将属性值同步到组件中;
  4. 代码13、25、31行对路由上的参数进行Base64的加密和解密;

组件的代码仅仅需要追加routeShot的配置即可:

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

经过这样,无论你怎么刷新页面,被快照的属性都不会发生改变。另外,除了data属性,prop、computed属性也是可以绑定到URL上的。

什么时候用最适合?

目前来说,应用场景中最多的还是非安全性表单以及不需要持久化的数据。举几个例子:

  1. 表格中筛选项有很多的情况下,用户进行了大量的选择和填写操作,结果因为网络原因导致请求失败。待网络恢复后,用户重新刷新页面,先前的操作必须重新执行;一般情况中,用户不会随意更改浏览器的URL,在这种条件下,用户的刷新不影响上下文的环境,能给用户带来一定便利;
  2. 之前代码示例中,开关组件的值不交予服务端进行持久化,也是可以使用这种方式来保存操作的;

存在的问题

写完这个插件,面临了三个我认为比较重要的问题:

  1. 性能问题: 通过代码47-50行可以看出,早期设计是将插件应用在路由组件中的,但是在后期的测试和使用中,发现还有很多组件不是注册在路由中的,也就是父子组件,这样的组件无法被路由钩子拦截到,因此就将该函数混入到了所有组件的created函数中。当应用越来越大、组件越来越多的时候,这个性能未免有点令人担忧;
  2. 持久性问题: 当URL的query部分越来越大的时候,超过了URL的长度限制,那么组件属性的持久性将会被中断。但我们并不能保证该长度不会超过,这随着应用的增长是无法预料的。在前端中,我们没有找到对应的库能进行定长加密解密,如果能找到,这个或将被解决;
  3. 安全性问题: 一直找不到比较安全的加密解密方式,而且我觉得这样做是会有安全隐患,但不知道究竟哪种场景会让这种安全性问题暴露的最大;

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JQuery加载图片自适应固定大小的DIV
Sep 12 Javascript
JQuery实现表格动态增加行并对新行添加事件
Jul 30 Javascript
jQuery根据ID获取input、checkbox、radio、select的示例
Aug 11 Javascript
js选项卡的实现方法
Feb 09 Javascript
bootstrap下拉列表与输入框组结合的样式调整
Oct 08 Javascript
Vue.JS项目中5个经典Vuex插件
Nov 28 Javascript
解决vue打包之后静态资源图片失效的问题
Feb 21 Javascript
AngularJS与后端php的数据交互方法
Aug 13 Javascript
原生js+css调节音量滑块
Jan 15 Javascript
ES6 Object.assign()的用法及其使用
Jan 18 Javascript
基于Element封装一个表格组件tableList的使用方法
Jun 29 Javascript
vscode 调试 node.js的方法步骤
Sep 15 Javascript
JavaScript中click和onclick本质区别与用法分析
Jun 07 #Javascript
jQuery动态移除与增加onclick属性的方法详解
Jun 07 #jQuery
使用Vue-cli 3.0搭建Vue项目的方法
Jun 07 #Javascript
vue 国际化 vue-i18n 双语言 语言包
Jun 07 #Javascript
vue cli2.0单页面title修改方法
Jun 07 #Javascript
JS实现同一DOM元素上onClick事件与onDblClick事件并存的解决方法
Jun 07 #Javascript
JavaScript事件委托原理与用法实例分析
Jun 07 #Javascript
You might like
thinkphp5引入公共部分header、footer的方法详解
2018/09/14 PHP
jscript之Open an Excel Spreadsheet
2007/06/13 Javascript
一个用js实现控制台控件的代码
2007/09/04 Javascript
js操作textarea方法集合封装(兼容IE,firefox)
2011/02/22 Javascript
node.js中的emitter.on方法使用说明
2014/12/10 Javascript
json实现前后台的相互传值详解
2015/01/05 Javascript
jQuery简单设置文本框回车事件的方法
2016/08/01 Javascript
AngularJS入门教程之链接与图片模板详解
2016/08/19 Javascript
JS实现放大、缩小及拖拽图片的方法【可兼容IE、火狐】
2016/08/23 Javascript
详解能在多种前端框架下使用的表格控件
2017/01/11 Javascript
深入浅析JavaScript中的RegExp对象
2017/09/18 Javascript
360提示[高危]使用存在漏洞的JQuery版本的解决方法
2017/10/27 jQuery
javascript性能优化之分时函数的介绍
2018/03/28 Javascript
在vue中使用Autoprefixed的方法
2018/07/27 Javascript
详解Vue中使用插槽(slot)、聚类插槽
2019/04/12 Javascript
详解jQuery中的prop()使用方法
2020/01/05 jQuery
js实现QQ邮箱邮件拖拽删除功能
2020/08/27 Javascript
[53:15]2018DOTA2亚洲邀请赛3月29日 小组赛A组 KG VS OG
2018/03/30 DOTA
Python数据结构之图的应用示例
2018/05/11 Python
python 定义给定初值或长度的list方法
2018/06/23 Python
Python读取excel指定列生成指定sql脚本的方法
2018/11/28 Python
弄懂这56个Python使用技巧(轻松掌握Python高效开发)
2019/09/18 Python
Tensorflow中的dropout的使用方法
2020/03/13 Python
Clarisonic美国官网:科莱丽声波洁面仪
2017/10/12 全球购物
澳大利亚领先的优质葡萄酒拍卖会:Langton’s Fine Wines
2019/03/24 全球购物
美国伴娘礼服商店:Evening Collective
2019/10/07 全球购物
乌克兰巴士票购买网站:inBus
2021/03/12 全球购物
怎么样写好简历中的自我评价
2013/10/25 职场文书
电子信息毕业生自荐信
2013/11/16 职场文书
饲料采购员岗位职责
2013/12/19 职场文书
电子商务应届生自我鉴定
2014/01/13 职场文书
2014年财务部工作总结
2014/11/11 职场文书
2014矛盾纠纷排查调处工作总结
2014/12/09 职场文书
入团介绍人意见范文
2015/06/04 职场文书
2016年党员承诺书范文
2016/03/24 职场文书
Redis+AOP+自定义注解实现限流
2022/06/28 Redis