目录

vue-cli + es6 + axios 项目踩坑

优质
小牛编辑
128浏览
2023-12-01

最近新做了一个项目,因为完全是另起炉灶,可以抛开历史问题,重新尝试新的思路与解决方案。也兢兢业业的踩坑俩月,把项目初版跑上线了。这一版主要是保证功能流程没问题,下一版会对开发流程、性能、错误监控等问题进行优化。截至目前记录的一些问题先抽空整理下。

如题,项目采用vue-cli + es6 + axios这三个作为基础跑起来的,依然是移动端,考虑兼容性 安卓4.1 & ios7.1,刚开始引入了jq,后续发现完全没必要,就引入了axios的ajax库,然后其他采用原生JavaScriptES6进行开发,也没遇到什么大的问题。

Axios

github地址:https://github.com/axios/axios

在此之前一直用的JQ的$.ajax,引入axios后还是有一些不一样的坑要慢慢习惯。

  • 请求参数方式不一致

    axios中,get请求和post请求携带参数的方式不一样,具体如下:

          axios.get(url, {
                  params: {
                      id: 123456
                  }
              }).then(res => {})
          axios.post(url, {
                  id: 123456
              }).then(res => {})
    

    解决方案是基于axios简单封了一个fetch.js,以简化、统一调用

  • 返回值更多信息

    在jq的回调函数中,我们后端返回的数据直接放在参数中,我们可以直接取res来用,在axios中,回调函数的参数,包含了更多的信息:

    • status: 请求状态码
    • statusText: 请求状态描述
    • headers: 响应头相关信息
    • config: 请求的相关配置
    • request: 当次请求相关信息
    • data: 后端返回的数据

    也就是说,在axios的回调函数中,res.data和$.ajax回调函数的res是一致的,而大部分时间,我们只需要知道res.data而忽略更多信息,这一点在fetch.js中也有优化

  • 发起一次请求却抓到两个请求

    两次请求出现在跨域的前提下,jq中解决跨域问题是通过jsonp的方式,而在浏览器的标准中,预检请求是更优雅的解决方案。简单说,就是在发生跨越的非简单请求时,浏览器会先发送预检请求,同服务端确认是否允许接下来的正式请求,如果被允许,则再发送正式请求。

    因此我们可能发现我们只发送了一次post请求,但却抓到两次请求,别担心,这不是bug,是个feature。

  • 跨域请求不带cookie

    跨域请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段:Access-Control-Allow-Credentials: true,另一方面,开发者在发起ajax请求时设置withCredentials为true,这一点也在fetch.js中做了处理。

    在这里,当我们的服务器设置Access-Control-Allow-Credentials: true时,会产生新的问题,在浏览器标准中,当服务器中设置Access-Control-Allow-Credentials为true时,Access-Control-Allow-Origin不能设置为*,而Access-Control-Allow-Origin: *是我们常用的解决跨域问题的设置。

    此问题的解决方案有两种,第一种方案是简单的设置一个白名单;另一种方案,如果之前设置Access-Control-Allow-Origin: *,此时可以在服务器配置文件进行设置:先获取发起跨域请求的源域,然后设置Access-Control-Allow-Origin的值为获取到的源域。当然这个设置可能在后端某些配置文件里,也可能直接在服务器配置文件设置。但思路大概相似。

  • 附:fetch.js

    简单封装,主要就是对上面几个问题进行了处理。

      import axios from 'axios'
    
      const fetch = (
          url, 
          params = {},
          options
      ) => {
    
          let _options = Object.assign({
              method: 'get',
              toastInfo: true,
              withCredentials: true
          }, options)
    
          let [ _params, _data ] = _options.method === 'get' ? [ params, ''] : [ '', params]
    
          return axios({
                  method: _options.method,
                  url: url,
                  params: _params,
                  data: _data,
                  withCredentials: _options.withCredentials
              })
              .then(res => {
                  let _res = res.data
    
                  //doSomething
    
                  return _res
              })
              .catch(e => {
    
                  //doSomething
                  //错误上报
    
              })
      }
    
      export default fetch
    

上面这些问题参考HTTP访问控制(CORS)大抵都能找到合理解释。

兼容性

  • Promise

    Promise兼容性一般,vue-cli脚手架中默认没有对Promise进行pollyfill,在目前的项目中,引用了es6-promise进行兼容处理,大致也就是在不兼容的情况下自定义实现一个Promise

  • Array.prototype.findIndex

    这个属性在开发过程中多次用到,个人觉得很好用,但兼容性也堪忧,就在base.js中添加了pollyfill,pollyfill中还用到了Object.defineProperty,所以如果vue无法兼容的,这个pollyfill也无法兼容啦

  • Input[type=date]

    原生的日期选择组件用起来不管是在安卓上还是ios上体验都很棒,但是安卓4.3及以下不识别,此处通过ua判断了系统版本,安卓4.3以下采用底部弹窗的方式让用户输入日期,牺牲一部分用户的体验。

  • 开发及部署

    这个相关的问题另起一篇吧,此次项目采用的是前端路由加多个单页应用,后端只提供接口及静态文件服务器,具体开发流程和部署:vue-cli + es6 多页面项目开发及部署

附:vue-cli + es6 多页面项目开发及部署