源本科技 | 码上会

Webpack 代理服务器

2026/04/22
37
0

为什么要代理

跨域问题

前端项目在本地开发时,会启动 webpack-dev-server 本地服务(默认 http://localhost:3000); 如果需要请求后端接口(比如 http://localhost:8080/api),浏览器会因为同源策略阻止请求,报跨域错误。

Webpack 代理

webpack-dev-server 内置了代理服务器功能: 本地请求 → 代理服务器转发 → 后端接口 代理服务器是服务端请求,不受浏览器同源策略限制,完美解决开发环境跨域问题。


配置位置

代理配置属于开发服务器配置,直接追加在配置文件的 devServer 对象中即可,无需安装新依赖!


修改配置

  • 找到原配置中的 devServer 节点,我们只需要在这个对象里新增 proxy 代理配置

  • devServer 中追加以下代理配置

proxy: [
    {
        // 所有以 /api 开头的接口请求,都会走这个代理
        context: '/api',
        // 后端接口的根地址(根据你的实际后端地址修改)
        target: 'http://localhost:8080',
        // 开启跨域允许(必须写,否则代理失效)
        changeOrigin: true,
        // 路径重写(可选):移除请求中的 /api 前缀
        // 例:前端请求 /api/user → 转发给后端的 /user
        pathRewrite: { '^/api': '' } // 可选:重写路径(移除 /api 前缀)
    }
]

完整配置

import path from 'path';
import { fileURLToPath } from 'url';
// ESM 模式导入 HTML 插件
import HtmlWebpackPlugin from 'html-webpack-plugin';

// 手动定义 __dirname(兼容 ESM 模式)
const __filename = fileURLToPath(import.meta.url); // 获取当前文件的绝对路径
const __dirname = path.dirname(__filename); // 获取当前文件所在目录的绝对路径

// Webpack 配置导出(ESM 模式)
export default {
    // 区分开发和生产环境
    mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
    // 入口文件
    entry: './src/index.ts',

    // 输出配置
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js',
        // 在开发环境中不强制输出ES模块,以确保样式加载正常
        ...(process.env.NODE_ENV === 'production'
            ? {
                module: true, // 生产环境输出 ES Module 格式产物
                clean: true   // Webpack5 内置:打包前自动清空 dist 目录
            }
            : {}
        )
    },

    // 开发环境下不需要实验性特性
    experiments: process.env.NODE_ENV === 'production'
        ? {
            outputModule: true
        }
        : {},

    // 模块解析
    resolve: {
        // 自动识别文件后缀,引入模块时可省略对应后缀名
        extensions: ['.js', '.ts', '.css', '.less', '.vue']
    },

    // TS 文件加载器
    module: {
        rules: [
            {
                test: /\.ts$/,
                use: 'ts-loader',
                exclude: /node_modules/
            },
            {
                // 匹配所有以 .css 结尾的文件
                test: /\.css$/,
                // Loader 执行顺序:从右向左 执行
                // 1. css-loader 解析 CSS 文件
                // 2. style-loader 将样式注入 DOM
                use: ["style-loader", "css-loader"]
            },
            {
                // 匹配所有以 .less 结尾的文件
                test: /\.less$/,
                // 执行顺序:less-loader → css-loader → style-loader
                // 1. less-loader 编译 Less 为 CSS
                // 2. css-loader 解析 CSS 模块
                // 3. style-loader 将样式注入页面 DOM
                use: ["style-loader", "css-loader", "less-loader"]
            },
            {
                // 匹配所有图片格式文件,i 表示不区分大小写
                test: /\.(png|jpg|gif|jpeg)$/i,
                // Webpack 5 内置资源类型,自动判断资源处理方式
                type: 'asset',
                // 自定义打包输出规则
                generator: {
                    // 输出到 dist/images 目录
                    // [name]:原文件名
                    // [hash:4]:4 位哈希值,防止缓存
                    // [ext]:文件后缀名
                    filename: 'images/[name].[hash:4][ext]'
                }
            },
            {
                test: /\.(eot|svg|ttf|woff|woff2)$/i,
                // 固定输出资源文件:不做 base64 转换,直接复制到打包目录
                type: 'asset/resource',
                generator: {
                    // 输出到 dist/fonts 目录
                    // [name] 原文件名,[hash:4] 4 位哈希值,[ext] 文件后缀
                    filename: 'fonts/[name].[hash:4][ext]'
                }
            }
        ]
    },

    // 插件配置:生成 HTML 并自动注入打包资源
    plugins: [
        new HtmlWebpackPlugin({
            template: './public/index.html', // HTML 模板路径
            filename: 'index.html', // 输出的 HTML 文件名
            inject: true // 自动注入 JS/CSS 资源
        })
    ],

    // 开发服务器配置
    devServer: {
        port: 3000,       // 服务监听端口 3000
        open: true,       // 自动打开浏览器
        hot: true,        // 启用热更新
        static: {
            directory: path.join(__dirname, 'public'), // 静态资源目录
        },
        client: {
            overlay: false,  // 是否显示编译错误在浏览器中
        },
        proxy: [
            {
                context: '/api', // 指定要代理的接口前缀
                target: 'http://localhost:8080', // 后端服务地址
                changeOrigin: true, // 解决跨域
                pathRewrite: { '^/api': '' } // 可选:重写路径(移除 /api 前缀)
            }
        ]
    },

    // 开发环境启用source map,便于调试
    devtool: process.env.NODE_ENV === 'production' ? false : 'eval-source-map'
};

代理配置参数

参数名

作用

必选

/api

匹配规则:所有以 /api 开头的请求,都会触发代理

target

目标服务器地址(后端接口的根路径)

changeOrigin

伪装请求来源,解决跨域(开发环境必须开启

pathRewrite

路径重写:替换请求路径中的字段

❌(可选)

常用场景

  1. 前端请求地址:/api/user/list

  2. 代理转发逻辑:

    • 匹配 /api → 启用代理

    • 拼接 targethttp://localhost:8080/api/user/list

    • 路径重写:移除 /api → 最终请求后端:http://localhost:8080/user/list


使用方法

  • 修改后端地址:将 target 改为你的真实后端接口地址(如 http://192.168.1.100:8080

  • 前端发起请求

// 前端代码中直接请求 /api 开头的接口即可
fetch('/api/user/list')
  .then(res => res.json())
  .then(data => console.log(data));
  • 启动项目npm run serve 代理自动生效


常见问题

  • 代理不生效:检查 changeOrigin: true 是否开启

  • 404 错误:检查 target 地址是否正确、pathRewrite 是否配置错误

  • 跨域依然报错:确认请求路径以 /api 开头,匹配代理规则


总结

  • 代理作用:解决开发环境跨域,无需后端配合

  • 配置位置:devServer.proxy

  • 核心参数:target(后端地址)+ changeOrigin: true(必开)