最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 我在Vue中是如何封装axios - 掘金

    正文概述 掘金(青莲使者)   2021-09-28   514

    小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

    我在Vue中是如何封装axios - 掘金

    前言

    axios的封装主要是帮助我们简化代码和利于后期的更新维护。

    安装

    npm install axios; // 安装axios
    

    引入

    import axios from 'axios'
    

    接口根地址

    const baseUrl = API_BASE_URL // 由webpack的插件DefinePlugin注入
    
    webpackConfig
        .plugin('define')
            .use(require('webpack/lib/DefinePlugin'), [{
                // NODE_ENV 环境变量,开发环境为: 'development', 为了保证测试环境打的包与生产环境一致,测试环境和生产环境都为'production'
                'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
                // 当前应用的环境(开发环境为: 'dev',测试环境为: 'test', 生产环境为: 'prod')
                'process.env.APP_ENV': JSON.stringify(process.env.APP_ENV),
                // 后台接口请求地址
                'API_BASE_URL': JSON.stringify(config.meshProp('apiBaseUrl')),
                // 首页路径
                'APP_INDEX_PATH': JSON.stringify(indexPath),
                // 路由模式
                'APP_ROUTER_MODE': JSON.stringify(config.meshProp('routerMode')),
                // 是否使用Element组件库
                'APP_USE_ELEMENT': JSON.stringify(config.meshProp('useElement')),
    
    }])
    

    config.js:配置一些系统名称,api根路径等

    const path = require('path')
    const os = require('os')
    const packageName = 'focm' // 项目包名称
    const localIP = getLocalIP() // 本地IP地址
    
    module.exports = {
        // 默认配置
        default: {
            // 系统名称,用于设置页面 head 中 title
            appName: 'xxxxx',
            // 是否为多页应用
            isMulti: false,
            // 是否支持移动端
            isMobile: false,
            // 是否使用Element组件库(https://element.eleme.cn/#/zh-CN/)
            useElement: true,
            // 路由模式(值为hash 或 history, 参考:https://router.vuejs.org/)
            routerMode: 'hash',
            // 接口请求根路径
            apiBaseUrl: '',
            ....
        },
        // 开发环境的配置
        dev: {
            apiBaseUrl: '/api',
            host: localIP,
            port: 8080,
            autoOpenBrowser: true, // 是否自动打开浏览器
            writeToDisk: false, // 是否将生成的文件写入磁盘
            proxyTable: {
                '/api': {
                    target: 'http://focm-web.focms.paas.test',
                    changeOrigin: true
                    }
                }
        },
        // 测试环境的配置
        test: {
            // 接口请求根路径
            apiBaseUrl: '/focm',
            outputRoot: path.resolve(__dirname, 'dist/test'),
            publish: {
                remoteHost: 'x.x.x.x',
                remotePort: '22',
                remoteUsername: 'qinglianshizhe',
                remotePassword: 'xxxxxx',
                remoteAppRoot: `/xxx/xxx/${packageName}`,
                webUrl: 'http://xxxxx.com/'
            }
        },
        // 生产环境的配置
        prod: {
            ...
        }
    }
    
    // 获取本地IP
    function getLocalIP () {
        let interfaces = os.networkInterfaces()
        for(let devName in interfaces){
            let iface = interfaces[devName]
            for(let i=0;i<iface.length;i++){
                let alias = iface[i];
                if(alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal){
                    return alias.address;
                }
            }
        }
        return 'localhost'
    }
    

    我们继续来封装axios

    /**
     * 业务异常类
     */
    
    class BusinessError extends Error {
        constructor (code, message, data) {
            super(message)
            this.code = code
            this.name = 'BusinessError'
            this.data = data
        }
    }
    /**
     * 系统异常类
     */
    class SystemError extends Error {
        constructor (code, message, data) {
            super(message)
            this.code = code
            this.name = 'SystemError'
            this.data = data
        }
    }
    // axios 配置
    axios.defaults.timeout = 10000
    axios.defaults.headers.post['Content-Type'] = 'application/json;charset=UTF-8'
    
    // 执行 POST 请求
    function post (option, vm) {
        option.method = 'POST'
        return http(option, vm)
    }
    
    // 执行 GET 请求
    function get (option, vm) {
        option.method = 'GET'
        return http(option, vm)
    }
    
    // 下载请求
    function download (option, vm) {
        option.method = option.method || 'GET'
        option.isDownload = true
        option.responseType = 'blob'
        return http(option, vm)
        }
    
    /**
    * 请求后台接口
    * @param option 参数
    * url: 请求路径(会拼接到baseUrl后面,“/” 开头)
    * data: 请求参数对象
    * timeout: 请求超时时间(默认为:10000,即10秒)
    * toastError: 自动提示业务异常信息,默认为true,为false时不自动提示
    * @param vm Vue对象(用于异常时自动toast提示异常信息)
    * @return {Promise} Promise对象
    */
    
    function http (option, vm) {
        return new Promise((resolve, reject) => {
            let method = option.method || 'POST'
            let url = baseUrl + option.url
            let timeout = option.timeout || 10000
            let headers = option.headers || {}
            let responseType = option.responseType
            let data = {} // 可以在此设置默认值
            if (option.data) {
                if (option.data instanceof FormData) {
                    headers['Content-Type'] = 'multipart/form-data'
                    let formData = option.data
                    Object.keys(data).forEach((key) => {
                        formData.append(key, data[key])
                    })
                    data = formData
                } else {
                    data = { ...data, ...option.data }
                }
            }
    
            let requestOptions = { method, url, headers, timeout, responseType }
            if (method.toUpperCase() === 'GET') {
                requestOptions.params = data
            } else {
                requestOptions.data = data
            }
            axios(requestOptions).then( (res) => {
                const contentDisposition = res.headers['content-disposition']
                // 文件下载
                if (contentDisposition &&
            (/filename\*=UTF-8''(.*)/.test(contentDisposition) || /filename="(.*)"/.test(contentDisposition))) { // 如果是文件下载
                    const utf8Match = contentDisposition.match(/filename\*=UTF-8''(.*)/) // 匹配UTF-8的文件名
                    const normalMatch = contentDisposition.match(/filename="(.*)"/) // 匹配普通英文文件名
                    const filename = utf8Match ? decodeURIComponent(utf8Match[1]) : normalMatch[1]
                    const blob = new Blob([res.data])
                    const downloadElement = document.createElement('a')
                    const href = window.URL.createObjectURL(blob)
                    downloadElement.href = href
                    downloadElement.download = filename
                    document.body.appendChild(downloadElement)
                    downloadElement.click()
                    document.body.removeChild(downloadElement)
                    window.URL.revokeObjectURL(href)
                    resolve(res)
                } else { // JSON信息
                    getResponseInfo(res).then((resInfo) => {
                        responseInfoHandle(resInfo, resolve, reject, option, vm, requestOptions)
                    })
                }
            }, err => {
                errorhandle(err, reject, option, vm)
            }).catch(function (err) {
                errorhandle(err, reject, option, vm)
            })
        })
    
    }
    
    // 处理响应信息
    function responseInfoHandle (resInfo, resolve, reject, option, vm) {
        // 请求是否成功
        let isSuccess = resInfo.retCode === '200'
        // 状态码
        let code = resInfo.retCode
        // 描述信息
        let message = resInfo.retMsg || '请求失败!'
        // 数据
        let resData = resInfo.data || {}
        if (isSuccess) { // 请求成功
            console.log(`[${option.method || 'POST'}]${option.url} 请求成功!\n请求参数:`, option.data, '\n响应结果:', resInfo)
            resolve(resData)
        } else { // 业务异常
            console.error(`[${option.method} || 'POST']${option.url} 请求失败!\n请求参数:`, option.data, '\n响应结果:', resInfo)
            let err = new BusinessError(code, message, resData)
            errorhandle(err, reject, option, vm)
        }
    }
    
    // 获取响应信息json对象
    
    function getResponseInfo (res) {
        return new Promise((resolve, reject) => {
            // 返回的信息
            let resInfo = res.data
            if (resInfo instanceof Blob) {
                const reader = new FileReader()
                reader.readAsText(resInfo, 'utf-8')
                reader.onload = () => {
                    resInfo = JSON.parse(reader.result)
                    resolve(resInfo)
                }
            } else {
            resolve(resInfo)
            }
        })
    }
    
    /* 异常处理 */
    function errorhandle (err, reject, option, vm) {
        let error = null
        if (err.name === 'BusinessError') {
            error = err
        } else {
            console.error(option.url, '请求失败!', err.code, err)
            error = new SystemError(500, '非常抱歉,系统出现错误,请稍后重试!')
        }
        console.log('error = ', error)
        if (vm) {
            if (error.name === 'BusinessError') { // 业务异常
                // 没有权限
                if (error.code === 'xxx') {
                    error.ignore = true
                    if (!isShowUnauthorized) {
                        vm.$popupAlert({
                            title: '提示',
                            message: '未登录或者会话已过期,请重新登录!',
                            width: 330,
                            height: 180,
                            btnText: '重新登录',
                            onOK: () => {
                                isShowUnauthorized = false // 是否显示重新登录弹框设为true
                                // 跳转到登录页面,登录成功后还跳转到原路径
                                vm.$router.push({ name: 'login', params: { fromPath: vm.$route.fullPath } })
                                vm.$eventBus.$emit('NO_AUTH_EVENT')
                            }
                        })
                        isShowUnauthorized = true // 是否显示重新登录弹框设为true
                    }
                    error.ignore = true
                } else if (option.toastError !== false) {
                    vm.$toast({ type: 'error', message: error.message })
                }
            } else { // 系统异常
                vm.$toast('网络异常!')
            }
        }
        reject(error)
    }
    
    export default {
        baseUrl,
        http,
        post,
        get,
        download
    }
    

    apiPlugin.js,封装成plugin插件

    import Vue from 'vue'
    import api from '@/assets/js/api.js'
    
    export default {
        install () {
            Vue.prototype.$api = api
        }
    }
    

    main.js,注入插件

    import ApiPlugin from './plugins/apiPlugin.js'
    
    // 后台接口插件
    Vue.use(ApiPlugin)
    

    使用事例:

    • 下载
    this.$api.download({
        url: '/xxx/xxx/xxx',
        data: params
    }, this)
    
    
    • get
    this.$api.get({
        url: `/xxx/xxx/xx`,
        data: params
    }, this).then((res) => {
        console.log(res)
    })
    
    • post
    this.$api.post({
        url: '/api/basicList/query',
        data: params
    }, this).then(res => {
    })
    

    axios的封装基本就完成了


    起源地下载网 » 我在Vue中是如何封装axios - 掘金

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元