1. token 续签
提一嘴
客户端通过配置 axios 的拦截器,获取 和 发送 token
// 请求拦截器
instance.interceptors.request.use(req=>{}, err=>{});
// 响应拦截器
instance.interceptors.reponse.use(req=>{}, err=>{});
token 时限设置为多少合理(参考)
- 面对极度敏感的信息,如钱或银行数据,那就根本不要在本地存放 token,只存放在内存中。这样,随着 app 关闭,token 也就没有了,此外将 token 的时限设置成较短的时间(如1小时)
- 对于那些虽然敏感但跟钱没关系,如健身 app 的进度,这个时间可以设置得长一点,如 1 个月
- 对于像游戏或社交类App,时间可以更长些,半年或 1 年
续签方法
场景:超过2个小时后,用户没有请求,则需要重新登录
- 方法一:每次请求都返回新 token
- 假设一个 token 的需求为 2h 未进行请求即过期。则设置有效期 2h,那么每次请求都会把一个 token 换成一个新 token。如果 2h 没有进行请求,那么上一次请求的到的 token 就会过期,需要重新登录。不断签就能一直使用下去
- 这种方式实现思路很简单,但开销比较大
- 问题一:每次都刷新 token,带来的性能影响如何?
- 以前每次请求,需要进行一次 token 签名校验,而现在是要签发一个新 token,进行的都是一次签名运算,那么运算量即从 n 变成 2n
- 其次,每次刷新都要把旧 token 加入黑名单,会导致黑名单特别大
- 问题二:每次都刷新 token,并发请求时会不会因为 token 刷新而导致只有一个请求成功?
- 答案是确实会导致这个问题,怎么解决呢?设置一个宽限时间,每次 token 刷新后,原来逻辑应该是立刻不可用,现在设置一个宽限时间,让其在 n 秒之内仍然可用即可
- 方法二:用户登录返回两个 token
- 第一个是 accessToken ,它的过期时间 token 本身的过期时间2个小时,另外一个是 refreshToken 它的过期时间更长一点比如为1天。客户端登录后,将 accessToken和refreshToken 保存在本地,每次访问将 accessToken 传给服务端。服务端校验 accessToken 的有效性,如果过期的话,就将 refreshToken 传给服务端。如果有效,服务端就生成新的 accessToken 给客户端。否则,客户端就重新登录即可
- 该方案的不足是:
- 需要客户端来配合
- 用户注销的时候需要同时保证两个 token 都无效
- 重新请求获取 token 的过程中会有短暂 token 不可用的情况(可以通过在客户端设置定时器,当accessToken 快过期的时候,提前去通过 refreshToken 获取新的accessToken)
2. 如何定位项目的性能问题(前端性能测试)
前端性能指标
以前衡量一个网站的好坏,所使用的指标很多,而且各有不同。所以Google推出了 Web Vitals, 定义了指标集,旨在简化和统一衡量网站质量的指标。在 Web Vitals 指标中,Core Web Vitals (核心网络指标)是其中最核心的部分,包含三个指标:
- LCP(测验加载性能):
根据页面开始加载的时间报告可视区域内可见的最大图像或文本块完成渲染的计算时间,用于测验加载性能,衡量网站初次载入速度。 我们应该控制该值在 2.5 秒以内(最大其实就是指元素的尺寸大小,这个大小不包括可视区域之外或者是被裁剪的不可见的溢出。也不包括元素的 Margin / Padding / Border 等) - FID(测试交互性):
首次输入延迟时间,主要为了测量页面加载期间响应度,测量交互性。为了提供良好的用户体验,页面的 FID 应为100 毫秒或更短,测量用户第一次与页面交互(单击链接、点按按钮等等)到浏览器对交互作出响应,并实际能够开始处理事件处理程序所经过的时间。FID只关注不连续操作对应的输入事件,例如点击,轻触,按键等。一般只考虑测量首次输入的延迟。FID 只考虑事件处理过程的延迟,不考虑事件处理花费的时间或者事件处理完成更新页面花费的时间 - CLS(测试视觉稳定性):
累积布局偏移,测量视觉稳定性。为了提供良好的用户体验,页面的 CLS 应保持在 0.1 或更少。CLS 即是测量整个页面生命周期内发生的所有意外布局偏移中最大一连串的布局偏移分数
每当一个可见元素从一个已渲染帧变更到另一个已渲染帧时,就是发生了布局偏移。所谓一连串布局偏移,是指一个或者多个的布局偏移,这些偏移相隔少于 1 秒,总持续时间最大为 5 秒。而最大一连串就是所有的一连串布局偏移中偏移累计分数最大的一连串
除此之外还有其他比较常用的指标:
- TTFB:
首包时间,资源请求到获取第一个字节之间的时间,包括以下阶段的总和 - FCP:
首屏时间,首次内容绘制的时间,指页面从开始加载到页面内容的任何某一部分在屏幕上完成渲染的时间 - FP:
白屏时间,首次渲染的时间点。FP 和 FCP 有点像,但 FP 一定先于 FCP 发生,例如一个页面加载时,第一个 dom 还没绘制完成,但是可能这时页面的背景颜色已经出来了,这时 FP 指标就被记录下来了。而 FCP 会在页面绘制完第一个 dom 内容后记录。 - SI:
速度指数衡量页面加载期间内容的视觉显示速度,也就是页面填充快慢的指标。良好的SI应该控制在 3.4 以内。 - TTI:可交互时间,指标测量页面从开始加载到主要子资源完成渲染,并能够快速、可靠地响应用户输入所需的时间。良好的TTI应该控制在 5 秒以内。
- TBT:总阻塞时间,也就是从 FCP 到 TTI 之间的时间
前端性能测试分析
(既然上面说了前端性能有哪些方面,那么怎么测性能)
- 通过代码:
可以使用谷歌官方的 web-vitals 库衡量核心网页指标并生成报告 - 性能分析工具(在开发者工具里):
Lighthouse 是谷歌官方开发的性能分析工具,目前已经嵌入到 chrome 开发者工具的选项卡中,不需要额外安装,可以直接使用 - 性能测试网站(输入网址就可以测试性能的网站):
很多,比如 PageSpeed Insights(谷歌开发的),WebPageTest,Speedcurve 等 - 通过 npm 或 docker(使用 npm 的话,你就可以在浏览器窗口中看到发生了什么,所以推荐 npm):
sitespeed.io 是一款可监视和衡量网站前端性能的开源工具
简易通过 npm 测试: - 安装
npm install -g sitespeed.io
- 使用
sitespeed.io https://www.sitespeed.io/ -b chrome
- 查看报告
在生成报告中,有一个网址,可以打开看到更适合观察的报告
3. 请求选择用 xml 还是 json
(我以前开发的话,一直觉得 xml 只适合写文档,例如配置文件那种,对于请求,一定是无脑选择 json,平常也没怎么看人请求用过 xml,但是在实习的时候,看到公司请求用了 xml,这里说一下我认为开发中请求选择 xml 或者 json 的优缺点)
- xml 可以通过在标签中添加属性这简单的方法来存储元数据,就是说 xml 可以简单区分,标签内的数据,是该数据的值,而标签的属性,是用来描述这个数据的数据,这么听还有点抽象,看个例子:
// def 属性表示 data 的取值范围,用来描述 data,而 85 则是 data 的值
85
如果用 json 来代替,需要创建一个对象,把元数据当作对象的成员来存储,但是这样容易发生歧义,该情况 xml 更优 - ① 在包含相同的信息的情况下,大部分情况下 json 要小于 xml,这意味着更快的传输和处理速度,② 且 json 执行序列化和反序列化的速度显著优于使用 xml,尤其解释 xml 时,尤其标签存在属性使得这些编程语言需要重新组织它们的数据结构才能进行操作,而大部分语言对于 json 都很容易进行转换,③ json 支持数组,而 xml 不支持,写起来更繁琐
【序列化:是将对象的状态信息转换为可以存储或传输的形式的过程】
【反序列化:是将可以存储或传输的形式转换为对象的状态信息的过程】
4. base64 编码
(以前一直只知道 base64 用在图片传送存储上,实习的时候接触项目才知道原来用很多地方可以用到)
【Base64是为了解决各系统以及传输协议中二进制不兼容的问题而生的】
- 用在图片传送存储上:
- 优点
- 网页中使用 base64 格式的图片时,不用再请求服务器调用图片资源,减少了服务器访问次数
- base64 编码的字符串,更适合不同平台、不同语言的传输
- 把 css 中的图片使用成 base64 编码的,css 是在 html 头部引用的,要优先于下面的内容被加载,所以在网速不好的时候就会出现先加载出 base64 图片
- 缺点
- base64 格式的文本内容较多,存储在数据库中增大了数据库服务器的压力
- 网页加载图片虽然不用访问服务器了,但因为 base64 格式的内容太多,导致文件变大,所以加载网页的速度会降低,可能会影响用户的体验, CSS 文件的体积直接影响渲染,导致用户会长时间注视空白屏幕。HTML 和 CSS 会阻塞渲染,而图片不会
- base64 无法缓存,要缓存只能缓存包含 base64 的 css 文件,这比直接缓存图片要差很多,而且一般 HTML 改动比较频繁,所以等同于得不到缓存效益
- 图片转成 base64,体积会变大 33%,所以大点的图片也不建议转换,base64 的含义即用 64 个(2 的 6 次方)可见字符来表示,所以一个以前 3 个字节的内容(3 * 8),现在需要 4 个可见字符表示(4 * 6),由于每个可见字符都只占了六位,但是确实一个字节,所以会多出来两位,用 2 个 0 来填满,最终结果就是,以前三个字节的二进制,现在需要四个字节来表示,所以体积会变大
- 可能某些场合并不能传输或者储存二进制流:
二进制文件和文本文件的存储和传输方式是不一样的。比如,如果一个传输协议是基于 ascii 文本的,那么它就不能传输二进制流,那你要将二进制流传输就得编码。常见的诸如 http 协议的 url 就是纯文本的,不能直接放二进制流。并且大多数语言的 string 类型,都不能直接储存二进制流,但可以储存 base64 编码的字符串。如果希望用 string 类型操作一切数据,那就没法直接用二进制流 - base64 编码还包含了抵御含 sql 注入在内的各种注入的功能:
base64 编码中不包含任何符号和空格,因此(近乎很难构造成)不会被解析成任何攻击语句。任何要存入的数据先进行 base64 编码再往后端传或者后端收到后先进行 base64 转码再进行sql 拼接,这样可以有效的抵御 sql 注入等各种攻击。但是缺点也有一些,一旦 base64 化,那么就不能进行高效搜索,以及储存空间要占用更大的地方,也不能方便的进行字符串截取等操作
(这个真的很妙,我就说怎么公司里的代码总是给一些可见的字符,比如用户密码,用户名这种可见字符,用 base64 编码处理一遍,反而扩大了传输的体积,这么一想,应该是为了抵御 sql 注入,是我愚昧了,之前一直没搞懂,恍然大悟)