前端

webpack的env一般分为develop、test以及production,每个环境可能所需配置都不一样,比如请求的后端服务器等等

若每次打包单独修改,那可能太过繁琐,最重要可能打包时忘记,造成不必要的损失

故而,建议将webpack区分不同环境单独配置好,打包时使用不同命令即可

环境配置

新建 .env.dev 文件

NODE_ENV = 'dev'
VUE_APP_BASE_UPL='开发环境api'
// 其他所需配置都可在此定义

新建 .env.test 文件

NODE_ENV = 'test'
VUE_APP_BASE_UPL='测试环境api'
// 其他所需配置都可在此定义

新建 .env.prod 文件

NODE_ENV = 'prod'
VUE_APP_BASE_UPL='生产环境api'
// 其他所需配置都可在此定义

二:请求配置

新建 request.js 文件

主要是 const baseURL = process.env.VUE_APP_BASE_UPL 这句话。

// 此为移动端配置,引用了vant组件,如要换成其他组件请修改配置
// import Vue from 'vue'
import axios from 'axios'
import qs from 'qs'
import { isEmpty, isFormData, filterObj } from '@/utils/function.js'
import { Toast, Dialog } from 'vant'

let pending = [] // 声明一个数组用于存储每个ajax请求的取消函数和ajax标识
let CancelToken = axios.CancelToken

const TIMEOUT = 3000
const baseURL = process.env.VUE_APP_BASE_UPL

const getErrorString = (errorObj, defaultStr = '') => {
    let errStr = defaultStr
    // 对象
    if (typeof errorObj === 'object') {
        if (errorObj.response && errorObj.response.status) {
            errStr = `${errorObj.response.status}-${errorObj.response.statusText}`
        } else if (errorObj.message && typeof errorObj.message === 'string') {
            errStr = `${errorObj.message} 无返回或非正常操作`
        }
    } else if (typeof errorObj === 'string') {
        errStr = errorObj
    }
    return errStr
}

const removePending = ever => {
    for (const p in pending) {
        if (pending[p].u === ever.url + '&' + ever.method) {
            // 当当前请求在数组中存在时执行函数体
            pending[p].f() // 执行取消操作
            pending.splice(p, 1) // 把这条记录从数组中移除
        }
    }
}

function handleResponse (response, options) {
    if (!response || !options) return
    if (options.showLoading && options.data && !options.data.hideLoading) {
        Toast.clear()
    }
    if (response.status === 200) {
        const data = response.data
        const contentType = response.headers['content-type']
        if (data.code === '40001') {
            Dialog.alert({
                message: data.message || '内部错误'
            }).then(() => {
            })
            return
        }
        if (contentType && contentType.indexOf('json') !== -1) {
            if (response.data.constructor === window.Blob) {
                const reader = new FileReader()
                reader.readAsText(response.data)
                reader.addEventListener('loadend', function () {
                    const res = JSON.parse(reader.result)
                    Toast(`${response.data.message || '未知异常'}`)
                })
            } else {
                const data = eval(response.data)
                const { success, message } = data
                if (!success && !options.noToast) {
                    Toast(`${response.data.message || '未知异常'}`)
                }
            }
        }
    } else if (response.status === 401 && options.showLoading) {
        Toast(`${response.data.message || '未知异常'}`)
    } else if (response.status > 401) {
        Toast(`${response.data.message || '未知异常'}`)
    } else if (response.status === '504') {
        Toast('请求错误 网络异常')
    }
}
const fetch = options => {
    const {
        method = 'get',
        data,
        url,
        headers,
        config
    } = options
    // const { sessionId } = store.state.login.user
    const sessionId = '0lYo8du8U7KWFRhl_m1Oi9HfpAQ_9HeQnzkNlGr6lCjlQNT6BcDC!-1900339258!1628479544252'

    const axiosConfig = {
        timeout: TIMEOUT,
        baseURL,
        withCredentials: true,
        headers: {
            ...headers,
            'session': sessionId
        }
    }
    const instance = axios.create(axiosConfig)
    instance.interceptors.request.use(
        config => {
            // 判断是否需要显示满屏loading遮罩
            if (options.showLoading && options.data && !options.data.hideLoading) {
                Toast.loading({
                    mask: true,
                    loadingType: 'spinner',
                    // message: '加载中...',
                    duration: 30000,
                    getContainer: '#app'
                })
            }
            removePending(config) // 在一个ajax发送前执行一下取消操作
            config.cancelToken = new CancelToken(c => {
                // 这里的ajax标识我是用请求地址&请求方式拼接的字符串,当然你可以选择其他的一些方式
                pending.push({ u: config.url + '&' + config.method, f: c })
            })

            return config
        },
        err => {
            return Promise.reject(new Error(getErrorString(err, 'request错误')))
        }
    )

    instance.interceptors.response.use(
        response => {
            try {
                handleResponse(response, options)
                // ------------------------------------------------------------------------------------------
                removePending(options.config) // 在一个ajax响应后再执行一下取消操作,把已经完成的请求从pending中移除
                // -------------------------------------------------------------------------------------------
            } catch (err) { }
            return response
        },
        err => {
            handleResponse(err.response, options)
            return Promise.reject(new Error(getErrorString(err, 'response错误')))
        }
    )
    const filterData = isFormData ? data : filterObj(data)
    switch (method.toLowerCase()) {
        case 'get':
            return instance.get(
                `${url}${!isEmpty(data) ? `?${qs.stringify(data)}` : ''}`,
                config
            )
        case 'delete':
            return instance.delete(url, { data: filterData })
        case 'head':
            return instance.head(url, filterData)
        case 'post':
            return instance.post(url, filterData, config)
        case 'put':
            return instance.put(url, filterData, config)
        case 'patch':
            return instance.patch(url, filterData)
        default:
            return instance(options)
    }
}

export default function request (options) {
    return new Promise((resolve, reject) => {
        fetch(options)
            .then(response => {
                resolve(response.data)
            })
            .catch(err => {
                reject(err)
            })
    })
}

// function.js文件配置
export let isEmpty = obj => {
    for (const el in obj) {
        return false
    }

    return true
}
// 过滤调对象中的一些值为空的属性
export let filterObj = obj => {
    let newObj = {}
    for (const key in obj) {
        if (obj[key] === 'undefined' || obj[key] === null || obj[key] === '') {
            continue
        } else {
            newObj[key] = obj[key]
        }
    }
    return newObj
}
// 是否为formdata
export let isFormData = v => {
    return Object.prototype.toString.call(v) === '[object FormData]'
}

三:打包配置

package.json 中修改配置

"scripts": {
    "serve": "vue-cli-service serve --open",
    "start": "npm run serve",
    "dev": "npm run start",
    "build": "vue-cli-service build",
    "build:dev": "vue-cli-service build --mode dev", // 开发环境打包
    "build:test": "vue-cli-service build --mode test", // 测试环境打包
    "build:prod": "vue-cli-service build --mode prod", // 生产环境打包
    "lint": "vue-cli-service lint"
},

主要是指定环境。

四:相关知识:获取环境配置

// 在index.html中获取环境配置相关
'<%= VUE_APP_BASE_UPL %>'
// 在其他文件中获取环境配置相关,例如上面request中
process.env.VUE_APP_BASE_UPL

拓展阅读

1:webpack优化系列一:webpack不同环境打包配置

2:webpack优化系列二:Vue配置compression-webpack-plugin实现Gzip压缩

3:webpack优化系列三:vue子目录路径更改—publicPath

4:webpack优化系列四:vue打包后生成的chunk-vendors文件过大,利用SplitChunks插件,分离chunk

ps: 知道这个技巧即可,后续实战使用。

参考资料

webpack优化系列一:webpack不同环境打包配置

[Vue中.env .env.development .env.production文件说明](https://blog.csdn.net/weixin_46872121/article/details/124231380)