Vue中环境变量配置及axios二次封装
前一阵着急做一个vue项目,中间有很多需要注意的地方,今天简单做个总结
项目主要流程
一. vue项目脚手架正常构建代码,开发,属于常规操作,其中路由是核心内容
import Vue from 'vue'
import VueRouter from 'vue-router'
// ----相关页面----
import Layout from '@layout/layout.vue' // 是包含菜单标题的页面布局(里面有一个router-view)
import MonitorTask from '@pages/monitor-task/monitorTask.vue' //监测任务管理
import StreamList from '@pages/stream-inspect/streamList.vue' //数据监测列表
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: '/layout/stream/monitor-task' //页面重定向
},
{
"path": "/layout",
"name": "layout",
"component": Layout,
"children": [
{
"path": "stream/monitor-task",
"name": "monitortask",
"component": MonitorTask,
"meta": {
"title": "监测任务管理"
}
},
{
"path": "stream/data-list",
"name": "streamList",
"component": StreamList,
"meta": {
"title": "直播数据监测列表"
}
}
]
}
]
const router = new VueRouter({
base: process.env.BASE_URL,
routes: routes
})
export default router
<template lang="pug">
a-layout
a-layout(style="position:relative;overflow:hidden;")
a-layout-content(style="background-color:#f0f2f5;")
//动态切换的页面
router-view
</template>
二. 请求数据接口,这里就涉及到axios二次封装
每一个项目都会有一个request.js文件,专门用来处理请求接口
import axios from 'axios'
import qs from 'qs'
// axios封装 请求的实例
const instance = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000
})
// 前端请求
instance.interceptors.request.use(config => {
// 一般这里会加个请求头 token 或者authoration,本次暂不加
return config
}, error => {
return Promise.reject(error)
})
// 后端响应给前端
instance.interceptors.response.use(response => {
// 返回类型为blob文件时直接返回response.data
if (response.config.responseType === 'blob') {
const filename = response.headers['content-disposition'].split(';')[1].split('=')[1]
const [fileExt] = filename.split('.').slice(-1)
return {
fileExt,
filename,
blob: response.data
}
}
// 后端返回的文件流 excel等文件处理
if (response.headers['content-type'] === 'application/octet-stream' || response.headers['content-type'] === 'application/vnd.ms-excel') {
return response
}
return response.data
}, error => {
// 2xx以外的状态码进入这里
return Promise.reject(error)
})
// 封装的公共接口请求方法,注意一定是instance实例,
//若公共的请求方法写到另外一个单独的文件里,那需要把这个instance实例暴露出去,如:export default instance ,在单独的文件里引入暴露的实例,如: import {get, post...} from './request.js'
// 如果都在一个文件里面,那不需要暴露这个instance实例了,就像下面这样写
export default {
get (url, params) {
return instance({
method: 'GET',
url,
params
})
},
getFile (url, params){
return instance({
method: 'GET',
url,
params,
responseType: 'blob',
headers:{
'Content-Type': 'application/json'
}
})
},
getPDF (url, params) {
return instance({
method: 'GET',
url,
params,
responseType: 'blob'
})
},
post (url, params) {
return instance({
method: 'POST',
url,
data: qs.stringify(params)
})
},
// 线索编辑上传文件
postFile (url, params) {
const formData = new FormData()
formData.append('clue_id', params.clue_id)
for (let i = 0; i < params.operate_arr.length; i++) {
formData.append(`operate_arr[${i}][first_operate]`, params.operate_arr[i].first_operate !== null ? params.operate_arr[i].first_operate : '')
formData.append(`operate_arr[${i}][second_operate]`, params.operate_arr[i].second_operate !== null ? params.operate_arr[i].second_operate : '')
for (let j = 0; j < params.operate_arr[i].position.length; j++) {
formData.append(`operate_arr[${i}][position][${j}]`, params.operate_arr[i].position[j])
}
formData.append(`operate_arr[${i}][operate_content]`, params.operate_arr[i].operate_content !== null ? params.operate_arr[i].operate_content : '')
formData.append(`operate_arr[${i}][case_num]`, params.operate_arr[i].case_num !== null ? params.operate_arr[i].case_num : '')
if (params.operate_arr[i].file.length > 0) {
for (let j = 0; j < params.operate_arr[i].file.length; j++) {
formData.append(`operate_arr[${i}][file][${j}]`, params.operate_arr[i].file[j])
}
}
// formData.append(`operate_arr[${i}][file]`, params.operate_arr[i].file.length > 0 ? params.operate_arr[i].file[0] : '')
}
return instance({
method: 'POST',
url,
data: formData
})
},
postNoStringfy (url, params) {
return instance({
method: 'POST',
url,
data: params
})
},
postFileCommon (url, params) {
const formData = new FormData()
Object.keys(params).forEach(key => {
if (Array.isArray(params[key])) {
params[key].forEach(item => {
formData.append(`${key}[]`, item)
})
} else {
formData.append(key, params[key])
}
})
return instance({
method: 'POST',
url,
data: formData
})
}
}
三、在业务api接口,我这里举例业务api:monitor-task.js
// 引入一个实例名称axios(名称随便) 调用里面的封装方法 如:axios.get
import axios from '../request'
// 监测任务管理列表
export function getStreamListApi (option) {
const api = '/monitor-task/stream/task-list'
return axios.get(api, option)
}
// 监测任务管理-新增
export function setAddTaskApi (option) {
const api = '/monitor-task/stream/add-task'
return axios.post(api, option)
}
四、业务里去用api接口
import { getStreamListApi, setAddTaskApi} from '@api/monitor-task/monitor-task.js'
async fetchList () {
this.list = []
this.paginationItem.total = 0
let param = {
...this.queryParam,
pn: this.paginationItem.current,
ps: this.paginationItem.pageSize
}
const res = await getStreamListApi(param)
if (res.code === 0) {
this.list = res.data.list
this.paginationItem.total = res.data.rows
}
},
环境变量配置
目的:是为了根据不同的环境发布不同的代码
"scripts": {
"serve": "vue-cli-service serve", // 对应开发环境
"oldBuild": "vue-cli-service build", // 对应线上环境
"build": "node --max_old_space_size=4096 node_modules/@vue/cli-service/bin/vue-cli-service.js build",// 对应线上环境
"lint": "vue-cli-service lint",
"postinstall": "patch-package"
},
项目根目录下这两个文件就是根据命令自动执行的npm run serve,npm run build 开发和测试环境配置
// 开发环境的配置
ENV = 'development'
VUE_APP_BASE_API = 'http://localhost:8080/'
// 线上环境的配置
ENV = 'production'
VUE_APP_BASE_API = 'http://192.168.1.214/'
注意1: 打包之后请求的地址链接就是线上链接(接口服务) 比如http://xxx.xx.x.xx/live-monitor/stream/clue-list
注意2: request.js 请求的实例 process.env.VUE_APP_BASE_API可以获取到不同环境的请求接口地址
const instance = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000
})
代理 proxy
在本地开发环境,想让接口走通,是可以用代理的方式,注意:到线上环境根本不走这个,和这个一点关系没有,走的就是axios封装实例 VUE_APP_BASE_API
const trunkProxy = {
target: 'http://10.202.xxx.xxx/',
changeOrigin: true,
headers: {
Referer: 'xxx_yyy.com/',
Host: 'xxx_yyy.com'
}
}
const proxyConfig = trunkProxy
// 此处是代理 网关,每次请求这些首个单词,就代理到测试环境的接口地址10.201.xxx.xxx
const apis = 'user path ad role org module menu trad dic notice index knowledge outdoor site live-monitor detail-analysis monitor-task'
const proxy = apis.split(' ').reduce((conf, api) => {
conf[`/${api}/`] = proxyConfig
return conf
}, {})
module.exports = {
devServer: {
disableHostCheck: true,
proxy
}
}
打包 vue.config.js
1、文件单独打包,执行npm run build 比如打包后生成dist文件,里面要分别有js,css,img文件等
const path = require('path')
const Webpack4QCDNPlugin = require('@q/webpack4-qcdn-plugin')
module.exports = {
publicPath: './',
assetsDir: 'static',
productionSourceMap: false, //反编译
configureWebpack: config => {
Object.assign(config, {
resolve: {
alias: {
'@components': path.resolve(__dirname, 'src/components/'),
'@pages': path.resolve(__dirname, 'src/pages/'),
'@layout': path.resolve(__dirname, 'src/layout/'),
'@styles': path.resolve(__dirname, 'src/styles/'),
'@api': path.resolve(__dirname, 'src/api/'),
'@store': path.resolve(__dirname, 'src/store/'),
'@js': path.resolve(__dirname, 'src/scripts/'),
'@images': path.resolve(__dirname, 'src/assets/images/'),
'@test': path.resolve(__dirname, 'src/test/'),
'@utils': path.resolve(__dirname, 'src/utils/')
}
},
externals: {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios'
}
})
// 此处是打包成一个文件 index.html里
// if (env === 'production') {
// config.plugins.push(
// new Webpack4QCDNPlugin({
// // qcdn 配置,详见 http://qnpm.qiwoo.org/package/@q/qcdn
// qcdnOption: {
// image: {
// https: true,
// domains: ['p1.ssl.qhimg.com']
// },
// static: {
// https: true,
// domains: ['s0.qhres2.com', 's2.ssl.qhres2.com']
// }
// },
// concurrency: 5,
// maxRetryCount: 5
// // keepLocalFiles: true
// // 其他配置项见 https://github.com/AngusFu/webpack4-cdn-plugin
// })
// )
// config.mode = 'production'
// }
},
transpileDependencies: ['ant-design-vue'],
lintOnSave: false,
runtimeCompiler: true,
css: {
loaderOptions: {
less: {
javascriptEnabled: true
}
}
}
}
2、打包成一个文件 index.html里 就是上方注解的部分, 同时对应的public文件目录下的index页面用的外部链接 (下方注释的部分)打包成第一种方式就是下方未注解的部分
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta vue-app-version="<%=VUE_APP_VERSION %>">
<link rel="icon" type="image/png" href="./icon.png">
<title>xxx平台</title>
<link href="./font-awesome.min.css"></link>
<!-- <link href="//lib.baomitu.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> -->
</head>
<body>
<div id="app"></div>
<script src="./js/vue.js"></script>
<script src="./js/vue-router.min.js"></script>
<script src="./js/vuex.min.js"></script>
<script src="./js/axios.min.js"></script>
<!-- <script src="//lib.baomitu.com/vue/2.6.10/vue.js"></script> -->
<!-- <script src="//lib.baomitu.com/vue-router/3.1.3/vue-router.min.js"></script> -->
<!-- <script src="//lib.baomitu.com/vuex/3.1.2/vuex.min.js"></script> -->
<!-- <script src="//lib.baomitu.com/axios/0.18.0/axios.min.js"></script> -->
<!-- <script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak=853ca75f8f4366f584fc8c8fe607d9b1"></script> -->
</body>
</html>
至此,整个vue项目就可以从开发到发布串起来了,这是比较基础的地方,希望能帮助到有需要的小伙伴们~