坏蛋格鲁坏蛋格鲁

【Function】JS 封装 Fetch 函数


import * as qs from './qs.js'

const isPlainObject = function isPlainObject(obj) {
    let proto, Ctor;
    if (!obj || toString.call(obj) !== "[object Object]") return false;
    proto = Object.getPrototypeOf(obj);
    if (!proto) return true;
    Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
    return typeof Ctor === "function" && Ctor === Object;
};

const getMethods = ['GET', 'HEAD', 'DELETE', 'OPTIONS'],
      postMethods = ['POST', 'PUT', 'PATCH']
const allMethods = [...getMethods, ...postMethods]

// 核心方法
const MyFetch = function myFetch(options = {}) {
    // 初始化配置项
    options = Object.assign({
        url: '',
        method: 'GET',
        body: null,
        params: null,
        headers: null,
        responseType: 'json',
        credentials:'include',
        mode: 'cors',
        cache: 'default',
        signal: null
    }, options)

    // 校验 URL
    if(!options.url) throw new TypeError('请求 URL 不能为空')
    const urlRegExp = /^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?$/
    if(!urlRegExp.test(options.url)) throw new TypeError('请求 URL 格式不正确')

    // 校验其他配置项
    if(!allMethods.includes(options.method)) throw new TypeError('请求方法不合法')
    if(!isPlainObject(options.headers)) options.headers = new Headers({})
    if(options.params !== null && !isPlainObject(options.params)) options.params = null

    // 格式处理:?传参
    if(options.params){
        const query = qs.stringify(options.params)
        if(options.url.includes('?')){
            options.url = url + '?' + query
        }else{
            options.url = url + '&' + query
        }
    }

    // 格式处理:body 数据(此处 body 数据封装的格式有很多,这里选择常规的表单提交格式: urlencoded)
    if(isPlainObject(options.body)){
        postMethods.includes(options.method) && (options.body = qs.stringify(options.body))
        options.headers.set('Content-Type', 'application/x-www-form-urlencoded')
    }

    // 请求拦截:给每一个请求设置共用的信息(例如 token)
    const token = localStorage.getItem('token')
    if(token){
        options.headers.set('Authorization', `Bearer ${token}`)
    }

    // 发送 fetch 请求
    const {url, ...config} = options
    return fetch(url, config)
        .then(response => {
            // 成功的请求,返回 2xx、3xx 等成功的状态码
            if(/^(2|3)\d{2}$/.test(response.status)){
                let result
                switch(config.responseType.toLowerCase()){
                    case 'text':
                        result = response.text()
                        break
                    case 'arraybuffer':
                        result = response.arrayBuffer()
                        break
                    case 'blob':
                        result = response.blob()
                        break
                    default:
                        result = response.json()
                }
                return result
            }
            // 失败的请求,返回 4xx、5xx 等其他失败的状态码
            // 这里的 reject 会被下边的 catch 中捕获到
            return Promise.reject({
                code: -1,
                status: response.status,
                statusText: response.statusText
            })
        })
        .catch(error => {
            console.error('Fetch Error: ', error)
            return Promise.reject(error)
        })
}

// 快捷方法
getMethods.forEach(item => {
    MyFetch[item.toLowerCase()] = function (url, options = {}) {
        return MyFetch.call(this, {...options, url, method: item})
    }
})
postMethods.forEach(item => {
    MyFetch[item.toLowerCase()] = function (url, body, options = {}) {
        return MyFetch.call(this, {...options, url, method: item, body})
    }
})

export default MyFetch
本原创文章未经允许不得转载 | 当前页面:坏蛋格鲁 » 【Function】JS 封装 Fetch 函数

评论