vue3+vite+ts打包后在根目录生成一个app.config.js文件用于部署后切换axios请求地址

1. 创建配置文件类型定义

首先在src/types目录下创建配置文件类型:

// src/types/config.ts
export interface AppConfig {
  apiBaseUrl: string;
  timeout: number;
  env: string;
  [key: string]: any;
}

2. 创建配置管理工具

// src/utils/config.ts
import type { AppConfig } from '@/types/config';

// 默认配置
const defaultConfig: AppConfig = {
  apiBaseUrl: '/api',
  timeout: 10000,
  env: 'production'
};

class ConfigManager {
  private config: AppConfig = defaultConfig;
  private configLoaded = false;

  // 加载配置
  async loadConfig(): Promise<AppConfig> {
    if (this.configLoaded) {
      return this.config;
    }

    try {
      // 尝试从外部配置文件加载
      const response = await fetch('/app.config.js?t=' + Date.now());
      if (response.ok) {
        const configText = await response.text();
        const configMatch = configText.match(/window\.APP_CONFIG\s*=\s*({[^}]+})/);
        
        if (configMatch && configMatch[1]) {
          const externalConfig = JSON.parse(configMatch[1]);
          this.config = { ...defaultConfig, ...externalConfig };
        }
      }
    } catch (error) {
      console.warn('Failed to load external config, using defaults:', error);
    }

    this.configLoaded = true;
    return this.config;
  }

  // 获取配置
  getConfig(): AppConfig {
    return this.config;
  }

  // 获取API基础URL
  getApiBaseUrl(): string {
    return this.config.apiBaseUrl;
  }
}

export const configManager = new ConfigManager();

3. 创建Vite插件生成配置文件

// src/plugins/generateConfigPlugin.ts
import { Plugin } from 'vite';

interface GenerateConfigOptions {
  outputPath?: string;
  config?: Record<string, any>;
}

export function generateConfigPlugin(options: GenerateConfigOptions = {}): Plugin {
  const {
    outputPath = 'dist/app.config.js',
    config = {
      apiBaseUrl: '/api',
      timeout: 10000,
      env: 'production'
    }
  } = options;

  return {
    name: 'generate-config-plugin',
    apply: 'build',
    generateBundle() {
      const configContent = `// 应用配置文件 - 自动生成
// 修改此文件中的配置项后,刷新页面即可生效
window.APP_CONFIG = ${JSON.stringify(config, null, 2)};
`;

      this.emitFile({
        type: 'asset',
        fileName: 'app.config.js',
        source: configContent
      });
    }
  };
}

4. 配置Vite插件

vite.config.ts中引入插件:

// vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { generateConfigPlugin } from './src/plugins/generateConfigPlugin';

export default defineConfig({
  plugins: [
    vue(),
    generateConfigPlugin({
      config: {
        apiBaseUrl: 'http://localhost:3000/api',
        timeout: 10000,
        env: process.env.NODE_ENV || 'production'
      }
    })
  ],
  // 其他配置...
build: {
    chunkSizeWarningLimit: 1000,
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            // Element Plus 单独分包(通常是大头)
            if (id.includes('element-plus')) return 'element-plus'
            // Vue 核心单独分包
            if (id.includes('/vue/') || id.includes('@vue')) return 'vue-vendor'
            // 其他第三方统一打包
            return 'vendor'
          }
        },
      },
    },
  },

server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000/api', // 目标服务器地址
        changeOrigin: true, // 是否改变源地址
        rewrite: (path) => path.replace(/^\/api/, ''), // 重写路径
        // 可以配置更多选项,如 logLevel, timeout 等
      },
    }
  }

});

5. 创建Axios实例

// src/api/axios
import axios from 'axios';
import { configManager } from "./config"

// 创建axios实例
const request = axios.create({
  timeout: 20000,
  headers: {
    'Content-Type': 'application/json'
  }
});

// 请求拦截器
request.interceptors.request.use(
  async (config) => {
    // 确保配置已加载
    await configManager.loadConfig();
    const apiBaseUrl = configManager.getApiBaseUrl();
    // 可以在这里添加请求头、认证token等
    console.log('拦截器:', localStorage.getItem('token'));
    config.headers['Authorization'] =  localStorage.getItem('token');

    // 如果是绝对路径,直接使用;否则拼接基础URL
    if (config.url?.startsWith('http')) {
      return config;
    }
    
    if (config.url && !config.url.startsWith('/')) {
      config.url = '/' + config.url;
    }
    
    config.baseURL = apiBaseUrl;

    return config;
  },
  (error: any) => {
    // 请求错误处理
    return Promise.reject(error);
  }
);

// 响应拦截器
request.interceptors.response.use(
  (response) => {
    return response;
  },
  (error: any) => {
    // 响应错误处理
    return Promise.reject(error);
  }
);

function checkStatus(response: any): Promise<any> {
  return new Promise((resolve, reject) => {
    if (!response) {
      reject(new Error('响应对象为空'));
      return;
    }

    const { status, data } = response;

    try {
      if (status === 200 || status === 304) {
        // 成功状态
        resolve(data as any);
      } else if (status >= 400 && status < 500) {
        // 客户端错误
        reject(new Error(`客户端错误: ${status}`));
      } else if (status >= 500) {
        // 服务端错误
        reject(new Error(`服务器错误: ${status}`));
      } else {
        // 其他状态码
        reject(new Error(`未知状态码: ${status}`));
      }
    } catch (error) {
      reject(error);
    }
  });
}

export default {
  post(url: any, params?: any) {
    return request({
      method: "post",
      url,
      data: params
    }).then(response => {
      return checkStatus(response);
    });
  },
  get(url: any, params?: any) {
    return request({
      method: "get",
      url,
      params
    }).then(response => {
      return checkStatus(response);
    });
  }
};

 

6. 在Vue应用中使用

// src/main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { configManager } from './api/config';

const app = createApp(App);

// 在应用启动时加载配置
configManager.loadConfig().then(() => {
  app.mount('#app');
});

7. 生成的配置文件示例

打包后会在dist目录生成app.config.js文件:

// 应用配置文件 - 自动生成
// 修改此文件中的配置项后,刷新页面即可生效
window.APP_CONFIG = {
  "apiBaseUrl": "http://localhost:3000/api",
  "timeout": 10000,
  "env": "production"
};

使用说明

  1. 开发环境:配置通过Vite的proxy处理

  2. 生产环境:

    • 打包后会在根目录生成app.config.js

    • 修改该文件中的apiBaseUrl即可改变API请求地址

    • 无需重新打包部署

 

posted @ 2025-11-28 15:55  龙卷风吹毁停车场  阅读(47)  评论(0)    收藏  举报