专栏首页coding for love5-4 使用 webpack-dev-server 实现请求转发

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

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用法

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • CSS进阶08-绝对定位 Absolute Positioning

    (注1:如果有问题欢迎留言探讨,一起学习!转载请注明出处,喜欢可以点个赞哦!) (注2:更多内容请查看我的目录。)

    love丁酥酥
  • 2-3 webpack的正确安装方式

    webpack是基于node开发的环境打包工具。首先需要安装node环境。 进入node官网,尽量安装最新版本的稳定版node。因为提高webpack打包速度...

    love丁酥酥
  • 3-8 使用 WebpackdevServer 提升开发效率

    webpack-dev-server 是 webpack 集成的开发者服务器,用于帮助开发者快速开发应用程序。

    love丁酥酥
  • webpack学习(三)—— webpack-dev-server

    这两种模式都支持Hot Module Replacement(热加载),所谓热加载是指当文件发生变化后,内存中的bundle文件会收到通知,同时更新页面中变化的...

    前端博客 : alili.tech
  • 入职第一天:leader手把手教我入门Vue服务器端渲染(SSR)

    今天是我入职第一天,在简短的内部培训了一上午后,前端leader让我先了解下什么是vue的服务器端渲染(SSR)。

    闰土大叔
  • 入职第一天:前端leader手把手教我入门Vue服务器端渲染(SSR)

    继前段时间西安电面之后顺利拿到了OFFER,今天(5月2号)是我入职第一天,在简短的内部培训了一上午后,前端leader让我先了解下什么是vue的服务器端渲染(...

    前端博客 : alili.tech
  • 分布式协调服务ZooKeeper工作原理

    大数据处理框架Hadoop、Redis分布式服务Codis、淘宝的分布式消息中间件MetaMQ …… 他们都使用ZooKeeper做为基础部件,可以看出ZooK...

    dys
  • 代码体积减少80%!Taro H5转换与优化升级

    作为一个多端开发框架,Taro从项目发起时就已经支持编译到H5端。随着Taro 多端能力的不断成熟,对 Taro H5 端应用的要求也不断提升,已经不再满足于“...

    京东技术
  • 新一代企业应用平台的探究(上):只拿干货说话

    魏新宇
  • Python骚操作,利用python更改室友电脑开机秘密,只需几步!

    今天教大家用Python脚本来控制小伙伴们Windows电脑的开机密码。没错就是神不知鬼不觉,用random()随机生成的密码,只有你自己知道哦~

    python学习教程

扫码关注云+社区

领取腾讯云代金券