Skip to content

webpack跨域问题

本节来学习怎么用webpack解决跨域的问题。

我们先来用express来自己写一个服务端,来模拟跨域。

我们在启动webpack服务本地访问项目时,其实就是使用的express在本地起的服务。

所以webpack本身自己就带了一个express

模拟跨域,写个接口

新建一个server.js

js
// webpack-dev-2\server.js
// express
const express = require('express')

let app = express()

app.get('/api/user', (req, res) => {
    res.json({
        name: 'cheny'
    })
})


app.listen(3001, () => {
    console.log('服务启动成功 在 3001 端口', 'http://localhost:3001/');
})

运行一下,然后再浏览器就能访问到这个接口,http://localhost:3001/api/user

image-20220123194130462

模拟跨域

修改index.js,发送一个请求,然后模拟跨域

js
// webpack-dev-2\src\index.js
let xhr = new XMLHttpRequest()

// 默认访问 http://localhost:8080/ webpack-dev-server的服务
xhr.open('GET', '/api/user', true) // true表示开启异步

// 把成功后的结果打印出来
xhr.onload = function () {
    console.log(xhr.response);
}

// 发送这个Ajax
xhr.send()

然后我是运行一下 npm run dev

因为默认访问的是8080端口,但是我们写的服务端的端口是3001端口,所以就跨域的,显示接口访问不到

image-20220123195231956

解决跨域1 本地webpack-dev-server 配置代理 proxy

因为接口会默认访问的webpack-dev-server的8080端口发送出去,8080访问3001肯定是访问不到的,所以我们可以这么干

  1. 先将服务发送到本地的webpack-dev-server的express上
  2. 然后通过本地的express来去访问接口
  3. 最后再转发给前端

就是配置一个本地的代理。http-proxy

修改配置文件

js
// webpack-dev-2\webpack.config.js
let path = require('path')

let HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html模板,将打包后的js塞进模板文件中

module.exports = {

    mode: 'development', // 模式,默认两种模式 production 和 development

    devServer: { // 开发服务器的配置
        port: 8080, // 默认端口是8080,这里可以改
        progress: true, // 打包时候的进度条
        contentBase: './build', // 以哪个文件夹作为服务的根目录 
        open: true, // 服务启动完毕后,直接打开浏览器
        compress: true, // 启动gzip压缩

        // 配置代理服务器,解决跨域
        proxy: {
            // 如果是 /api 开头的服务,就使用devServer去发送请求,发送到 http://localhost:3001 上
            '/api': 'http://localhost:3001', // 配置了一个代理
        }
    },

    // 入口
    entry: {
        home: './src/index.js', // 入口1 home
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },

    // 配置loader
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env'],
                    }
                }
            }
        ]
    },

    // 插件
    plugins: [
        // 生成html模板插件,将打包后的js以外链的形式塞到模板中
        new HtmlWebpackPlugin({
            template: './index.html',
            filename: 'index.html',
        }),
    ],
}

配好以后重启服务,npm run dev,发现就可以访问到了

image-20220123195704661

特殊情况1 重写接口

我们在写接口的时候,有时候接口不会带api开头,比如请求的接口为 http://localhost:3001/user,但是我们在写的时候习惯性的加上api,这时候该怎么办呢。

其实webpack在发服务的时候可以重写接口地址,把某一块给干掉。

修改server.js

js
// webpack-dev-2\server.js
// express
const express = require('express')

let app = express()

// http://localhost:3001/user
app.get('/user', (req, res) => {
    res.json({
        name: 'cheny & xzz'
    })
})


app.listen(3001, () => {
    console.log('服务启动成功 在 3001 端口', 'http://localhost:3001/');
})

修改配置文件

js
// webpack-dev-2\webpack.config.js
let path = require('path')

let HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html模板,将打包后的js塞进模板文件中

module.exports = {

    mode: 'development', // 模式,默认两种模式 production 和 development

    devServer: { // 开发服务器的配置
        port: 8080, // 默认端口是8080,这里可以改
        progress: true, // 打包时候的进度条
        contentBase: './build', // 以哪个文件夹作为服务的根目录 
        open: true, // 服务启动完毕后,直接打开浏览器
        compress: true, // 启动gzip压缩

        // 配置代理服务器,解决跨域
        proxy: { // 重写的方式,把请求代理到express服务器上
            // 如果是 /api 开头的服务,就使用devServer去发送请求,发送到 http://localhost:3001 上
            '/api': {
                target: 'http://localhost:3001',
                pathRewrite: { '/api': '/' }, // 把接口地址的 /api 替换为空
            },
        }
    },

    // 入口
    entry: {
        home: './src/index.js', // 入口1 home
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },

    // 配置loader
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env'],
                    }
                }
            }
        ]
    },

    // 插件
    plugins: [
        // 生成html模板插件,将打包后的js以外链的形式塞到模板中
        new HtmlWebpackPlugin({
            template: './index.html',
            filename: 'index.html',
        }),
    ],
}

重启服务,发现成功了,虽然访问的是 /api/user,但是最后也能准确的访问到后台的接口/user

image-20220123201400400

前端mock一些数据

修改配置文件

js
// webpack-dev-2\webpack.config.js
let path = require('path')

let HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html模板,将打包后的js塞进模板文件中

module.exports = {

    mode: 'development', // 模式,默认两种模式 production 和 development

    devServer: { // 开发服务器的配置
        port: 8080, // 默认端口是8080,这里可以改
        progress: true, // 打包时候的进度条
        contentBase: './build', // 以哪个文件夹作为服务的根目录 
        open: true, // 服务启动完毕后,直接打开浏览器
        compress: true, // 启动gzip压缩

        // (1)配置代理服务器,解决跨域
        /* proxy: {
            // 如果是 /api 开头的服务,就使用devServer去发送请求,发送到 http://localhost:3001 上
            '/api': {
                target: 'http://localhost:3001',
                pathRewrite: { '/api': '/' }, // 把接口地址的 /api 替换为空
            },
        } */

        // (2) 我们前端只想单纯来模拟一些接口数据
        before(app) { // webpack-dev-server提供的一个钩子,调用的时候会传一个app,其实就是express的app
            app.get('/user', (req, res) => {
                res.json({
                    name: 'cheny & xzz ❤'
                })
            })
        },
    },

    // 入口
    entry: {
        home: './src/index.js', // 入口1 home
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },

    // 配置loader
    module: {
        rules: [
            {
                test: /\.js$/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env'],
                    }
                }
            }
        ]
    },

    // 插件
    plugins: [
        // 生成html模板插件,将打包后的js以外链的形式塞到模板中
        new HtmlWebpackPlugin({
            template: './index.html',
            filename: 'index.html',
        }),
    ],
}

修改index.js

js
// webpack-dev-2\src\index.js
let xhr = new XMLHttpRequest()

// 默认访问 http://localhost:8080/
xhr.open('GET', '/user', true) // true表示开启异步

// 把成功后的结果打印出来
xhr.onload = function () {
    console.log(xhr.response);
}

// 发送这个Ajax
xhr.send()

重启服务,访问成功

image-20220123202035400

解决跨域2 在服务端启动webpack,端口用服务端端口

相当于前端代码和服务端代码启动在了一个端口上,这样就没有跨域了。

相当于在服务端启动了一个页面,在这个页面访问服务端的接口。

因为是在服务端express中启动前端页面,需要一个中间件

shell
yarn add webpack-dev-middleware@^3.7.0 -D

修改server.js

js
// webpack-dev-2\server.js
// express
const express = require('express')

let app = express()

// 在服务端使用webpack启动前端页面
let webpack = require('webpack')


// 使用中间件,引入webpack
let middle = require('webpack-dev-middleware')
/* 
1. 先通过webpack拿到配置文件,交给webpack来处理
2. 会产生一个编译后的结果
3. 然后将编译后的结果 仍给中间件就可以了
*/
let config = require('./webpack.config.js') // 1
let compiler = webpack(config) // 2
app.use(middle(compiler)) // 3

// 这样的话,我们运行服务的时候,会连带着把webpack也启动开

// http://localhost:3001/user
app.get('/user', (req, res) => {
    res.json({
        name: 'cheny & xzz'
    })
})


app.listen(3001, () => {
    console.log('服务启动成功 在 3001 端口', 'http://localhost:3001/');
})

我们使用node .\server.js 启动这个node服务,会发现连带着也启动了一个webpack

image-20220123203249747

我们试着访问接口,没问题

image-20220123203319137

如果我们直接访问 http://localhost:3001/,发现也能拿到这个项目了

image-20220123203421580

这就相当于后端和前端启动到了一起。

总结

webpack解决跨域的方式

  1. 使用代理,通过webpack自带的express来访问后台接口

    1. 写法1 直接代理

      js
      proxy: {
          // 如果是 /api 开头的服务,就使用devServer去发送请求,发送到 http://localhost:3001 上
          '/api': 'http://localhost:3001',
      }
    2. 写法2 重写路径

      js
      proxy: {
          // 如果是 /api 开头的服务,就使用devServer去发送请求,发送到 http://localhost:3001 上
          '/api': {
              target: 'http://localhost:3001',
                  pathRewrite: { '/api': '/' }, // 把接口地址的 /api 替换为空
          },
      }
  2. 直接将项目启动到后台上,相当于前台和后台公用一个端口

    js
    // webpack-dev-2\server.js
    // express
    const express = require('express')
    
    let app = express()
    
    // 在服务端使用webpack启动前端页面
    let webpack = require('webpack')
    
    // 使用中间件,引入webpack
    let middle = require('webpack-dev-middleware')
    /* 
    1. 先通过webpack拿到配置文件,交给webpack来处理
    2. 会产生一个编译后的结果
    3. 然后将编译后的结果 仍给中间件就可以了
    */
    let config = require('./webpack.config.js') // 1
    let compiler = webpack(config) // 2
    app.use(middle(compiler)) // 3
    
    // 这样的话,我们运行服务的时候,会连带着把webpack也启动开
    
    // http://localhost:3001/user
    app.get('/user', (req, res) => {
        res.json({
            name: 'cheny & xzz'
        })
    })
    
    app.listen(3001, () => {
        console.log('服务启动成功 在 3001 端口', 'http://localhost:3001/');
    })

另外使用webpack-dev-server还可以模拟后台接口数据,因为它自带的有express,可以拿到app,并且有个before钩子,如果访问接口匹配到这个钩子会直接拦截

js
devServer: { // 开发服务器的配置
    before(app) { // webpack-dev-server提供的一个钩子,调用的时候会传一个app,其实就是express的app
        app.get('/user', (req, res) => {
            res.json({
                name: 'cheny & xzz ❤'
            })
        })
    },
},

参考

https://www.bilibili.com/video/BV1a4411e7Bz?p=16