2023年12月

问题背景

在一次使用Chrome开发者工具调试中,发现找不到接口参数了,但仔细检查代码后以及结合接口响应数据来看确确实实是传参数了的,并且后端也正常接收到了请求数据,当时没有深入探究原因,现在对这个问题进行一下复盘。

接口数据传输的方式

PayloadForm Data是接口数据传输的两种方式,浏览器通过Content-Type来进行区分使用哪种方式:

  • 当值为application/x-www-form-urlencodedmultipart/form-data时,是Form Data方式
  • 当值为application/json时,是Request Payload方式

    Request Payload

    如下代码所示,使用axios发起post请求,设置请求头'Content-Type':'application/json',成功发起请求后可在调试工具Payload Tab面板中看到请求参数。Request Payload通常用于发送JSONXML格式的数据。

    axios.post(host + '/payload', {
      username: 'admin',
      password: 'admin',
    }, {
      headers: {
        'Content-Type': 'application/json'
      }
    }).then((res) => {
      console.log(res)
    }).catch((err) => {
      console.log(err)
    })

    Form Data

    如下代码所示,设置请求头'Content-Type':'application/x-www-form-urlencoded',请求参数使用Qs插件进行序列化后使用axios发送,在调试工具Payload Tab面板中可看到请求参数为Form Data类型。Form Data类型通常用于发送表单数据,并且可用来进行上传文件

    axios.post(host + '/payload', Qs.stringify({
      username: 'admin',
      password: 'admin',
    }), {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    }).then((res) => {
      console.log(res)
    }).catch((err) => {
      console.log(err)
    })


    如上图所示,再点击view source后,可看到实际上传输的数据是一个字符串,由于设置了正确的Content-Type,可以被浏览器正常识别。但如果请求参数不进行序列化,还是用Form Data方式进行请求,浏览器依然是能够正常发送出请求的,并且浏览器的Payload将会显示:

    但是如果Server端如果仍按照Form Data方式对请求参数处理,那么可能会出错。所以,请求参数一定要和Content-Type是对应的,否则可能会影响Server端对请求参数的正常解析。

    Query String Parameters

    另外在Chrome中,Query参数也会被放到Payload中,如下图所示:

    Chrome 96版本前,Request PayloadForm Data信息是在HeadersTab中的,96版本之后它们都被放到了PayloadTab中。

    问题复现

    所发生问题的接口参数是Request Payload格式的,并且注意到其中有个对象数据量是很大的,因此根据这一特征构造一个请求尝试复现问题:

    如图所示,通过循环构造了一个长字符串作为对象data属性值,经过调整不同的循环次数进行测试,确定了当Content-Length超过10000000字节时Payload Tab就会消失,并且PreviewResponse显示“Failed to load response data: request content was evicted from inspector cache”。

    这句话翻译为中文意思是“未能加载响应数据:请求内容已从检查器缓存中收回”,大概意思就是由于超出缓存限制,请求参数以及响应参数都不能正常显示了。通过观察后端接口的请求以及浏览器请求状态码(200 OK)可知,虽然调试工具不显示请求以及响应数据,但实际上是没有影响接口的正常请求以及响应的,并且前端也能够正常接收到后端响应数据,也就是说这个问题是不影响项目的正常运行的。
    另外通过测试发现,当Server端的响应数据过长时,在浏览器调试工具中也会发生同样的事情,而且也不会实际影响接口数据的响应。

    问题原因

    通过查询发现以下一些情况都可能导致此问题:

  • 请求的请求数据超过了浏览器的缓存限制
  • 请求的响应数据超过了浏览器的缓存限制
  • 在浏览器的开发者工具中查看了大量的请求,并超过了浏览器的缓存限制
  • 电脑可用内存不足

背景

有个移动端项目用了Vant组件库进行开发,其中有个需求要实现一个轮播图的效果,但是Vant组件库自带的轮播图组件功能有限,只能实现一些很基础的效果,而且在其基础上只进行一些样式的调整也难以实现想要的效果,所以就考虑使用其它的插件。
Swiper是很著名的一个用于轮播图及移动动画的插件,面向手机、平板电脑等移动终端,并且开源免费。在网上进行搜索之后发现大多数的用法都是用原生js方式所实现的,并且Swiper官方在7版本之前也是没有适配Vue的,因此为了在Vue中能以组件方式进行调用Swiper,还诞生了一个12.7k star的第三方开源项目vue-awesome-swiper。另外在Vant的官网上也提到,如果需要更复杂的轮播效果,可以使用vue-awesome-swiper,因此就开始了一系列的踩坑之旅。

遇到的问题(坑)

安装模块

点击进入vue-awesome-swiper的github主页,就可看到黄色的提示以及官方介绍,说是由于Swiper官网对vue的支持,这个库已经弃用了,当时看到这个提示的时候并没有细想(确实是有点急),看了如下的版本兼容性列表后发现 vue-awesome-swiper是支持项目所使用的Vue2的,于是就想着那就用它了。

点击进去vue-awesome-swiper@4.1.1的github主页之后,看到了官方给的安装指令,心想真好,一顿CV执行了命令yarn add swiper vue-awesome-swiper,甚至都没有多看一眼但是很顺利安装上了vue-awesome-swiper。

组件调用

根据官方的引导进入了示例网站Examples (Vue2),在里面找到了一个与想要效果类似的示例,并将代码复制到项目中测试运行,首先是导入组件:

import { Swiper, SwiperSlide } from 'vue-awesome-swiper'
import 'swiper/css/swiper.css'

然后IDE报错,说是swiper.css找不到,其实这时就应该有所察觉的,但当时就想着怎么官网实例都有问题呢,可真不靠谱。在晚上一顿搜索说是,Swiper不同的版本swiper.css的位置可能是不一样的(这会竟然还没意识到Swiper的版本有问题),然后修改了正确的地址,没报错了。

<template>
  <swiper class="swiper" :options="swiperOption">
    <swiper-slide>Slide 1</swiper-slide>
    <swiper-slide>Slide 2</swiper-slide>
    <swiper-slide>Slide 3</swiper-slide>
    <swiper-slide>Slide 4</swiper-slide>
    <swiper-slide>Slide 5</swiper-slide>
    <swiper-slide>Slide 6</swiper-slide>
    <swiper-slide>Slide 7</swiper-slide>
    <swiper-slide>Slide 8</swiper-slide>
    <swiper-slide>Slide 9</swiper-slide>
    <swiper-slide>Slide 10</swiper-slide>
    <div class="swiper-pagination" slot="pagination"></div>
  </swiper>
</template>
<script>
  export default {
    data() {
      return {
        swiperOption: {
          slidesPerView: 3,
          spaceBetween: 30,
          pagination: {
            el: '.swiper-pagination',
            clickable: true
          }
        }
      }
    }
  }
</script>

接着把上面的代码复制进行了项目中,也成功运行了,并且没有任何报错,但是出来的效果没有和官网效果图一样,代码中slidesPerView设置3含义是容器中同时会出现3个swiper-slide,如下图所示。但实际上运行出的效果只有一个swiper-slide,之后又对比了与官方代码的区别,并没有什么不同,这就导致对官方代码暂时失去了信任。

初步实现效果

于是就在网上搜索了vue-awesome-swiper的相关用法,最终找到了能实现类似效果的一篇文章,那篇文章中的代码和官方代码相比,除了swiperOption配置外,最大的区别就是添加了一些自定义的css样式,使其实现了想要的效果。但是一个最大的问题就是分页器不显示,配置中的pagination属性就描述了分页器的相关信息,最初以为是配置写错了,但是经过一番检查后还是没发现问题,而且还用了旧版本的pagination写法也还是不行。
通过Chrome的调试工具可发现,模板中写的paginationdiv里面是空的,但是官方运行的实例里面是有元素的,也就是说pagination根本就没有生成,所以这就不是单单的样式问题了,就像是我写的配置没有生效一样,然后我还尝试修改了其它的一些配置发现都是没有效果的。

新的思路

这时想这应该还是仔细看下官方文档比较好,于是又回到了vue-awesome-swiper的github主页,才重视了"Swiper官方已对vue进行了支持"这句话,又心想既然已经有了官方的支持,那么就不用这个作为中间桥梁的第三方插件了吧,免不了这个插件会有什么莫名其妙的bug呢。
Swiper官方在从7版本开始就对各大流行组件库进行了支持,包括Vue、React、Angular等。但是Swiper Vue组件的文档中有一句红色提示,说Swiper Vue组件以后很可能就不再支持了,推荐使用Swiper ElementSwiper78为适配Vue、React、Angular,都要为他们单独各写一套代码,随着Web Component技术的发展与流行,Swiper就提倡开发者使用Web Component组件进行开发。
经过这一番考量,想着既然这样就直接用最新的吧(完全把兼容性抛在了脑后),又是一顿安装运行,发现总是报错,最终发现是不支持Vue2的。接着又尝试了Swiper Vue组件,甚至是退回到了7版本,都是不支持Vue2的(官网并没有看到醒目的兼容性提示)。
这才醒悟了为什么vue-awesome-swiper为什么重点适配到了Swiper6,因为Swiper6之前是没有官网支持的组件的,而vue-awesome-swiper恰恰就干了这个事情,并且也支持Vue2。那为什么刚开始我运行是有问题的呢,总不能用原生js方式去用吧?

恍然大悟

干了这么多问题也没解决,但让我突然想到了是不是我最开始安装的vue-awesome-swiper版本有问题了,这时又去看了一下最开始安装命令yarn add swiper vue-awesome-swiper发现没有加版本号,这就导致我安装的Swiper是最新的11版本!!!怪不得出现了乱七八糟的问题呢。

最终解决

调整了安装命令为yarn add swiper@5 vue-awesome-swiper@4,又一次成功安装了Swiper。运行了代码,分页器的DOM结构终于出来了,但样式还有点问题。通过审查发现官网实例上的分页器一些样式,在我的项目里是没有的,所以肯定就是引入的样式有问题了,去看了node_modules里的css文件,重新引入了一下,问题终于解决。
这时调整Swiper配置swiperOption,都是有效果的,这也证实了刚开始的配置根本没生效的猜想。

反思

本来没有多难得事情浪费了那么久,实属不应该,其中暴露出来的问题主要有:

  • 在不熟悉插件的情况下没有认真看文档
  • 习惯性的运行安装插件的代码,没有进行思考
  • 用新的模块/插件,没有考虑兼容性的问题
  • 做事不能乱着急,所谓心急吃不了热豆腐呀