main.js
import { createApp } from 'vue'
import router from './router'
import App from './App.vue'

import 'element-plus/dist/index.css'
import ElementPlus from 'element-plus'

import axios from 'axios'
const app = createApp(App)
app.config.globalProperties.$axios = axios
app.use(router)
app.use(ElementPlus)
app.mount('#app')
App.vue
<template>
  <div>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  components: {},
};
</script>

<style>
* {
  margin: 0;
  padding: 0;
  border: 0;
}
</style>
store.js
import { createApp } from 'vue'
import App from '../App.vue'
import Vuex from 'vuex'

const app = createApp(App)
app.use(Vuex)

export default new Vuex.Store({
  state: {
    //这里放全局参数
    token: '111222'
  },

  mutations: {
    //这里是set方法
  },

  getters: {
    //这里是get方法
  },

  actions: {
    //这个部分我暂时用不上
  },
  modules: {
    //这里是方法
  }
})
router.js
import { createRouter, createWebHistory } from 'vue-router'

const routerHistory = createWebHistory()

// - 定义路由组件规则
import Login from "@/views/login/index";
import Registered from "@/views/registered/index";
import NotFound from "@/views/NotFound";

const routes = [
  // 路由重定向
  { path: "/", redirect: "/login" },
  { path: "/login", component: Login },
  { path: "/register", component: Registered },
  // // 嵌套路由
  // {
  //     path: "/users",
  //     component: Users,
  //     children: [
  //         {path:"list",name: "user_list",component: List,},
  //         { path: "add", component: Add },
  //         { path: "mod", component: Mod },
  //         { path: "del", component: Del },
  //     ],
  // },
  // 默认404路由
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
];

// - 创建路由实例
const router = new createRouter({
  history: routerHistory,
  routes,
});

// - 导出路由实例
export default router;
http.js
import axios from 'axios'
import router from '@/router/index'
import store from '@/store/index'
import { ElMessage } from 'element-plus'

console.warn(process.env.VUE_APP_API_URL)

/**
 * 跳转登录页
 * 携带当前页面路由,以期在登录页面完成登录后返回当前页面
 */
const toLogin = () => {
  router.replace({
    path: '/login',
    query: {
      redirect: router.currentRoute.fullPath
    }
  })
}

/**
 * 请求失败后的错误统一处理
 * @param {Number} status 请求失败的状态码
 */
const errorHandle = (status, other) => {
  // 状态码判断
  switch (status) {
    // 401: 未登录状态,跳转登录页
    case 401:
      toLogin()
      ElMessage({
        type: 'error',
        message: '请先登录'
      })
      break
    // 403 token过期
    // 清除token并跳转登录页
    case 403:
      ElMessage({
        type: 'error',
        message: '登录过期,请重新登录'
      })
      localStorage.removeItem('token')
      store.commit('loginSuccess', null)
      setTimeout(() => {
        toLogin()
      }, 1000)
      break
    // 404请求不存在
    case 404:
      ElMessage({
        type: 'error',
        message: '请求的资源不存在'
      })
      break
    case 500:
      ElMessage({
        type: 'error',
        message: '网络请求失败'
      })
      break
    default:
      console.log(other)
  }
}

// 创建axios实例
var instance = axios.create({
  baseURL: process.env.VUE_APP_API_URL,
  timeout: 5000, // 请求超过5秒即超时返回错误
  headers: {
    'Content-Type': 'application/json;charset=UTF-8'
  },
  withCredentials: true
})
/**
 * 请求拦截器
 * 每次请求前,如果存在token则在请求头中携带token
 */
instance.interceptors.request.use(
  config => {
    // 登录流程控制中,根据本地是否存在token判断用户的登录情况
    // 但是即使token存在,也有可能token是过期的,所以在每次的请求头中携带token
    // 后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码
    // 而后我们可以在响应拦截器中,根据状态码进行一些统一的操作。
    const token = store.state.token
    token && (config.headers.token = token)
    console.log(config);
    return config
  },
  error => Promise.error(error)
)

// 响应拦截器
instance.interceptors.response.use(
  // 请求成功
  res => (
    res.status === 200 ? Promise.resolve(res) : Promise.reject(res)
  ),
  // 请求失败
  error => {
    const { response } = error
    if (response) {
      // 请求已发出,但是不在2xx的范围
      errorHandle(response.status, response.data.message)
      return Promise.reject(response)
    } else {
      // 处理断网的情况
      // eg:请求超时或断网时,更新state的network状态
      // network状态在app.vue中控制着一个全局的断网提示组件的显示隐藏
      // 关于断网组件中的刷新重新获取数据,会在断网组件中说明
      if (!window.navigator.onLine) {
        ElMessage({
          type: 'error',
          message: '网络连接失败'
        })
        // store.commit('changeNetwork', false)
      } else {
        return Promise.reject(error)
      }
    }
  }
)

export function get(url, fromdata) {
  return axios({
    method: 'get',
    url: url,
    params: fromdata,
  });
  return instance.get(url, params)
}
export function post(url, params) {
  return instance.post(url, params)
}

export default instance
vue.config.js
// 这里的webpack配置会和公共的webpack.config.js进行合并
module.exports = {
  // 执行 npm run build 统一配置文件路径(本地访问dist/index.html需'./')
  // NODE_ENV:Node.js 暴露给执行脚本的系统环境变量。通常用于确定在开发环境还是生产环境
  publicPath: process.env.NODE_ENV === 'production' ? '' : '/',
  outputDir: 'dist', // 输出文件目录
  assetsDir: 'static', // 放置静态资源
  // indexPath: 'index.html', // 可以不设置一般会默认
  // filenameHashing:true, // 文件命名
  lintOnSave: false, //设置是否在开发环境下每次保存代码时都启用 eslint验证
  productionSourceMap: false, //如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建
  // runtimeCompiler: false, // 是否使用带有浏览器内编译器的完整构建版本
  configureWebpack: { // 别名配置
    resolve: {
      alias: {
        //'src': '@', 默认已配置
        'api': '@/api',
        'assets': '@/assets',
        'common': '@/common',
        'components': '@/components',
        'views': '@/views',
        'plugins': '@/plugins',
        'utils': '@/utils',
        'store': '@/store',
      }
    }
    // 使用前面可加~
  },
  css: { // css相关配置
    // 是否将组件中的 CSS 提取至一个独立的 CSS 文件中,生产环境下是 true,开发环境下是 false
    extract: process.env.NODE_ENV === "production",
    // 是否为 CSS 开启 source map。设置为 true 之后可能会影响构建的性能。
    sourceMap: false,
    // 启用 CSS modules for all css / pre-processor files.(预加载)
    requireModuleExtension: true,
    loaderOptions: {
      sass: {
        // data: `@import "@/assets/css/variables.scss";`
      }
    }
  },
  devServer: {
    open: true, // 是否自动打开浏览器
    port: 3000, // 设置端口号
    overlay: false,  // 如果你开始了eslint,不要让eslint在页面中遮罩,它错误会在console.log控制台打印
    // host: 'localhost:3000', // ip
    disableHostCheck: true, //是否关闭用于 DNS 重绑定的 HTTP 请求的 HOST 检查
    hotOnly: false, // 热更新
    https: false, // https:{type:Boolean}配置前缀
    proxy: { //目的是解决跨域,若测试环境不需要跨域,则不需要进行该配置
      '/game': { // 拦截以 /api 开头的url接口
        target: 'http://localhost:9099/', //目标接口域名
        changeOrigin: true, //是否跨域
        ws: true, //如果要代理 websockets,配置这个参数
        secure: false, // 如果是https接口,需要配置这个参数
        // 标识替换
        // 原请求地址为 /api/getData 将'/api'替换''时,
        // 代理后的请求地址为: http://xxx.xxx.xxx/getData
        // 若替换为'/other',则代理后的请求地址为 http://xxx.xxx.xxx/other/getData 
        pathRewrite: { // 标识替换
          '^/api': '/'   //重写接口 后台接口指向不统一  所以指向所有/
          // '^/api': '/api/mock'
        }
      }
    }
  },
  // 这个插件中添加的service worker只在生产环境中启用(例如,只有当你运行npm run build或yarn build时)。
  // 不推荐在开发模式中启用service worker,因为它会导致使用以前缓存的资产而不包括最新的本地更改的情况。
  pwa: {
    // serviceWorker:false,
    // 允许您从一个现有的service worker文件开始,并创建一个该文件的副本,并将“预缓存清单”注入其中。
    // workboxPluginMode:'InjectManifest',
    // workboxOptions: {
    //   //swSrc: './app/sw.js', /* Empty file. */
    // },
    iconPaths: {
      favicon32: "favicon.ico",
      favicon16: "favicon.ico",
      appleTouchIcon: "favicon.ico",
      maskIcon: "favicon.ico",
      msTileImage: "favicon.ico"
    }
  }
}
api.js
import { get, post } from '@/api/http'

export const login = params => get('game/login', params)
export const register = params => get('/game/register', params)

一沙一世界,一花一天堂。君掌盛无边,刹那成永恒。