浏览器相关 - 浏览器请求

ajax 及 fetch API详解

  1. XMLHttpRequest

     const xhr = new XMLHttpRequest();
     xhr.open('GET', 'http://domain/serivce');
     // 需要注意的是 在请求发送前建立对状态的监听 有可能请求响应非常快,状态监听还没有完成
     xhr.onreadystatechange = function (){   
         if(xhr.readyState !== 4){
             return;
         }
         if(xhr.status === 200){
             console.log(xhr.responseText)
         }else{
             console.error(`HTTP error, status=${xhr.status},errorText=${xhr.statusText}`);
         }
     }
    
     // 超时
     xhr.timeout = 3000;
     xhr.ontimeout = () => {
         console.log('当前请求超时啦')
     }
    
     // 文件上传进度
     xhr.upload.onprogress = p => {
         const precent = Math.round((p.loaded / p.total) * 100) + '%';
     }
    
     xhr.send();
  2. fetch
  • 默认不带cookie
  • 错误不会reject
  • 不支持超时设置
  • 需要通过AbortController终止fetch
    fetch('http://domain/serivce', {
        method: 'GET',
        credentials: 'same-origin',  // omit: 默认 不带cookie same-origin:同域携带cookie  include:即可同域携带,也可跨域携带
    }).then(response => {
        if(response.ok){
            // 请求成功
            return response.json;
        }
        throw new Error('http error');
    }).then(json => {
        console.log(json)
    }).catch(error => {
        //  接收整体fetch错误   throw new Error错误
        console.log(error);
    })

封装一个fetch 超时

    function fetchTimeout(url, init, timeout = 3000){
        return new Promise((resolve, reject) => {
            fetch(url, init).then(resolve).catch(reject);
            setTimeout(reject, timeout);
        })
    }
    // 尝试一下:封装一个通用的异步函数的超时
    // function (fn, timeout){ }

中断fetch

    const controller = new AbortController();
    fetch('http://domain/serivce', {
        method: 'GET',
        credentials: 'same-origin',
        siginal: controller.siginal
    }).then(response => {
        if(response.ok){
            // 请求成功
            return response.json;
        }
        throw new Error('http error');
    }).then(json => {
        console.log(json)
    }).catch(error => {
        console.log(error);
    })

    // 取消
    // 会中断所有 controller.siginal 的 fetch
    controller.abort();

常见的浏览器请求/响应头/错误码解析

request header

method
path

cookie

referer: 判断浏览器来自哪个页面,标识访问路径
user-angent:经常用来判断各种各样的环境,比如做投放业务

response header

access-control-allow-origin: 制定具体域名 或者 * 不做限制
content-encoding: gzip
set-cookie

status

200 get 成功
201 post 成功
301 永久重定向
302 临时重定向
304 协商缓存 服务器文件未修改
400 客户端请求有语法错误,不能被服务器识别
403 服务器受到请求,但是拒绝提供服务,可能是跨域
404 请求的资源不存在
405 请求的method不允许
500 服务器发生不可预期的错误

强缓存
max-age 毫秒数,接收cookie多少毫秒后失效
expired 代表什么时候过期,时间改变之后cookie有效性会发生偏差

协商缓存
last-modified 判断当前文件是否被修改 缺点:在服务器打开不作任何修改last-modified还是会改变
eTag 把整体文件内容进行了hash判断文件是否有过修改 更加精确 但是更加消耗性能

问:为什么常见的 cdn 域名和业务域名不一样?


  1. 安全问题 cookie 携带了身份信息,域名相同的话会把不必要的用户信息传给cdn厂商,公司不想暴露
  2. cdn request header 会携带 cookie ,无畏的增加了更多的带宽占用、资源消耗

问:常见的vue react spa应用,都会存在index.html文件,也就是所谓的单页,针对index.html要做缓存的话,适合做什么缓存?

如果一定要做的话,协商缓存。
spa应用的html文件非常简单,可能根本没有什么内容,编译产出的script css文件会变成 script link标签插入html里面,而这些文件本身是有hash命名的,是为了防止js css的缓存,但是index.html是没有hash的。
js文件更新非常频繁,如果做了强缓存,更新不能实时下发到用户端上去;如果做了协商缓存,每次打包文件都会发生改变,就可以正常拉去更新代码。
正常的的话是 no-cache no-store

发送请求的示例,以及封装一个多浏览器兼容的请求函数

interface IOption{
    url: string,
    type?: 'GET' | 'POST',
    data: any,
    timeout?: number
}

function formatUrl(object){
    const dataArr = [];

    for(let key in object){
        dataArr.push(`${key}=${encodeURIComponent(object[key])}`)
    }

    return dataArr.join('&');
}

export function ajax(options: IOption = {
    url: '',
    type: 'GET',
    data: {},
    timeout: 3000
}) {
    return new Promise((resolve, reject) => {
        if(!options.url){
            return ;
        }

        const queystring = formatUrl(options.data);
        let timer;
        let xhr;

        const onStateChange = () => {
            xhr.onreadystatechange = () => {
                if(xhr.readyState === 4){
                    if(xhr.status >= 200 && xhr.status <= 300 || xhr.status === 304){
                        resolve(xhr.responseText);
                    }else{
                        reject(xhr.status);
                    }
                }
            }
        }

        if((window as any).XMLHttpRequest){
            xhr = new XMLHttpRequest();
        }else{
            xhr = new ActiveXObject("Microsoft.XMLHTTP");
        }

        if(options.type.toLowerCase() === 'GET'){
            xhr.open('GET', `${options.url}?${queystring}`);
            xhr.send();
        }else if(options.type.toLowerCase() === 'POST'){
            xhr.open('POST', options.url);
            xhr.setRequestHeader(
                'ContentType', 'application/json'
            )
            xhr.send(options.data);
        }

        if(options.timeout){
            timer = setTimeout(() => {
                xhr.abort();
                reject('timeout')
            }, options.timeout)
        }
    })
}

你可能感兴趣的:(javascript)