美文网首页
5-4 使用 webpack-dev-server 实现请求转发

5-4 使用 webpack-dev-server 实现请求转发

作者: love丁酥酥 | 来源:发表于2020-05-02 17:21 被阅读0次

    1. 简介

    请求转发,其实是使用 webpack-dev-server 的代理功能来实现的,本节为大家介绍 webpack-dev-server 的代理功能和主要使用场景。

    2. 正向代理与反向代理

    在进入正题之前,先简单地先介绍一下什么是代理,字面意义上理解就是委托第三方处理有关事务。网络代理分为正向代理和反向代理,所谓正向代理就是顺着请求的方向进行的代理,即代理服务器他是由你配置为你服务,去请求目标服务器地址。反向代理正好与正向代理相反,代理服务器是为目标服务器服务的。虽然整体的请求返回路线都是一样的都是 Client 到 Proxy 到 Server。
    webpack-dev-server 的代理功能更偏向于正向代理,即是为前端开发者服务的。

    3. 页面准备和接口请求

    我们在项目中,新建如下文件:

    // webpack.common.js
    var HtmlWebpackPlugin = require('html-webpack-plugin');
    var { CleanWebpackPlugin } = require('clean-webpack-plugin');
    var path = require('path');
    
    module.exports = {
        entry: {
            index: "./src/index.jsx",
        },
        output: {
            path: path.resolve(__dirname, '../dist'),
            filename: "[name].js"
        },
        module: {
            rules: [
                {
                    test: /\.(js|jsx)$/,
                    exclude: /node-modules/,
                    use: 'babel-loader'
                },
                {
                    test: /\.(jpg|jpeg|png|gif)$/,
                    use: {
                        loader: 'url-loader',
                        options: {
                            name: '[name].[ext]',
                            limit: 2048
                        }
                    }
                },
                {
                    test: /\.css$/,
                    use: [ 'style-loader', 'css-loader' ]
                },
                {
                    test: /\.scss$/,
                    use: [
                        'style-loader',
                        'css-loader',
                        'postcss-loader',
                        'sass-loader',
                    ]
                },
                {
                    test: /\.(eot|svg|ttf|woff)$/,
                    use: 'file-loader'
                }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({
                template: "./src/index.html"
            }),
            new CleanWebpackPlugin()
        ]
    };
    
    // webpack.dev.js
    var path = require('path');
    var webpack = require('webpack');
    var merge = require('webpack-merge');
    var commonConfig = require('./webpack.common');
    
    var devConfig = {
        mode: 'development',
        devtool: "cheap-module-eval-source-map",
        devServer: {
            contentBase: path.resolve(__dirname, 'dist'),
            open: true,
            port: 3000,
            hot: true // 开启热更新
        },
        plugins: [
            new webpack.HotModuleReplacementPlugin()
        ]
    };
    
    module.exports = merge(commonConfig, devConfig);
    
    // webpack.prod.js
    var merge = require('webpack-merge');
    var commonConfig = require('./webpack.common');
    
    var prodConfig = {
        mode: 'production',
        devtool: "cheap-module-source-map",
    };
    
    module.exports = merge(commonConfig, prodConfig);
    
    // src/index.js
    // src/index.js
    import React, { Component } from 'react';
    import ReactDom from 'react-dom';
    import axios from 'axios';
    
    class App extends Component {
      constructor() {
        super();
        this.state = {};
      }
      componentDidMount() {
        axios.get('http://127.0.0.1:3600/api/hello.json').then(res => {
          console.log(res)
          this.setState({
            msg: res.data.msg
          })
        }).catch(e => {
          console.error(e);
        })
      }
    
      render() {
        return <div>{this.state.msg}</div>
      }
    }
    
    ReactDom.render(<App />, document.getElementById('root'));
    
    
    <!--src/index.html-->
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>esmodule-oop</title>
    </head>
    <body>
        <div id="root"></div>
    </body>
    </html>
    

    .babelrc:

    {
      "presets": [
        [
          "@babel/preset-env",
          {
            "corejs": 2,
            "useBuiltIns": "usage"
          }
        ],
        "@babel/preset-react"
      ]
    }
    
    "scripts": {
        "dev": "webpack --config ./build/webpack.dev.js --watch",
        "dev-analyse": "webpack --config ./build/webpack.dev.js --profile --json > stas.json",
        "build-analyse": "webpack --config ./build/webpack.prod.js --profile --json > stas.json",
        "dev-server": "webpack-dev-server --config ./build/webpack.dev.js",
        "build": "webpack --config ./build/webpack.prod.js"
      },
    

    3. 接口准备

    这里就不用 node 写 server 了,直接 http-server 起一个简单的服务。运行 npm run build,然后在 dist 下新建如下文件: api/hello.json

    {
      "msg": "hello world"
    }
    

    进入 dist,使用 http-server -p 3600 开启服务,访问 http://127.0.0.1:3600


    image.png

    4. 代理请求

    但是我们部署的服务可能会改变地址(先上来讲是域名),另外,在开发环境的时候,我们的后台接口可能还没有开发完成,需要我们访问其他的开发地址或者测试地址。那该怎么做呢?一个最容易想到的方案就是将域名配置到统一的地方,一处更改,多处生效。比如我们封装一层请求,request,为其配置 host,每次请求的时候自动加上 host。我们的代码中只要写相对路径即可:

    request.get('/api/hello.json')
    

    但其实 webpack dev-server 为我们提供了方便地配置。一般为了防止跨域,我们会将静态资源和接口资源部署在同一个服务下,比如上面的 dist 下面加一个 api 目录,当然实际可能并不是这样,比如使用了反向代理等。在代码中我们写相对地址即可:

    axios.get('/api/hello.json')
    

    如果仅仅这样写,那么代码请求的始终是当前服务下的 api/hello,每次修改代码,需要部署之后才能生效。这显然是不可能的。我们关闭之前的服务,新建一个文件:server/api/hello.json,进入 server 使用 3000 端口重新开启服务。
    然后我们使用 dev-server 开启服务:npm run dev-server


    image.png

    可以看到, 请求的是 3600 端口下的接口,但是我们这里的 dev-server 仅提供了页面资源,并没有接口资源,接口资源在线上(这里用 3000 端口代替)。
    这时候就要使用上述我们提到的代理了:

        devServer: {
            contentBase: path.resolve(__dirname, 'dist'),
            open: true,
            port: 3000,
            hot: true,// 开启热更新
            proxy: {
                '/api': 'http://127.0.0.1:3000'
            }
        },
    

    重新运行 npm run dev-server,如下:


    image.png

    4. 跨域

    有的人会想,那这样做其实和在源码中通过配置去写也是一样的呀,只要最终达到以下效果就可以了:

    axios.get('http://127.0.0.1:3600/api/hello.json').then(res => {
          console.log(res)
          this.setState({
            msg: res.data.msg
          })
        }).catch(e => {
          console.error(e);
        })
    

    那么我们代码作如上改写,关闭代理后,npm run dev-server 看看:


    image.png

    打开 console:


    image.png
    可以看到,报了跨域。这是因为浏览器现代浏览器的同源安全策略,禁止跨域发送请求。而 proxy 是通过一个代理服务器帮我们转发请求,不受浏览器的跨域限制。但其实对于很多后端服务,出于安全考虑,我们也会做跨域限制,这时候接口就无法正常返回数据呢。对于这种情况,我们可以使用 changeOrigin 来解决:
    我们把请求地址改回相对地址,然后修改 proxy 配置如下:
    proxy: {
                '/api': {
                    target: 'http://127.0.0.1:3600',
                    changeOrigin: true
                }
            }
    

    就可以帮我们解决接口跨域问题了。

    5. 重写路径

    有时候,我们会遇到路径不一致的场景,比如我们本来是请求 hello 接口的,但这个接口正在开发中,后端可能丢了一个 demo 接口让我们先用,还有的时候我们的生产接口可能放在 api 下面,但是测试接口并没有这一层路径,这时候我们就可以通过重写路径来保证访问地址的正确性:

    module.exports = {
      //...
      devServer: {
        proxy: {
          '/api': {
            target: 'http://localhost:3600',
            pathRewrite: {'^/api' : ''}
          }
        }
      }
    };
    

    6. 过滤

    有时你不想代理所有的请求。可以基于一个函数的返回值绕过代理。
    在函数中你可以访问请求体、响应体和代理选项。必须返回 false 或路径,来跳过代理请求。
    例如:对于浏览器请求,你想要提供一个 HTML 页面,但是对于 API 请求则保持代理。你可以这样做:

    proxy: {
      "/api": {
        target: "http://localhost:3000",
        bypass: function(req, res, proxyOptions) {
          if (req.headers.accept.indexOf("html") !== -1) {
            console.log("Skipping proxy for browser request.");
            return "/index.html";
          }
        }
      }
    }
    
    1. 代理多个路径
      如果你想要代码多个路径代理到同一个target下, 你可以使用由一个或多个「具有 context 属性的对象」构成的数组:
    proxy: [{
      context: ["/auth", "/api"],
      target: "http://localhost:3000",
    }]
    

    8. 使用 https

    默认情况下,不接受运行在 HTTPS 上,且使用了无效证书的后端服务器。如果你想要接受,修改配置如下:

    proxy: {
      "/api": {
        target: "https://other-server.example.com",
        secure: false
      }
    }
    

    9. 小结

    proxy 的配置相当丰富,甚至还可以帮我们修改 header,携带 cookie 等。这些都让我们能在不修改源码的情况下通过简单的配置即可做到,远远优于直接手动在源码进行修改的方法,极大方便了我们的开发。

    参考

    正向代理与反向代理的区别
    https://webpack.js.org/configuration/dev-server/#devserverproxy
    https://www.webpackjs.com/configuration/dev-server/#devserver-proxy
    Webpack-dev-server的proxy用法

    相关文章

      网友评论

          本文标题:5-4 使用 webpack-dev-server 实现请求转发

          本文链接:https://www.haomeiwen.com/subject/bevcghtx.html