React Typescript音乐播放器项目笔记:4、create-react-app搭建代理(一)

项目地址:https://github.com/BUPTlhuanyu/react-music-lhy

create-react-app搭建代理(一)

经过npm run eject之后配置代理,create-react-app用的是webpack-dev-server实现一个简单的web服务器,webpack-dev-server是基于express实现的。webpack-dev-server相关配置可参考webpack中devServer

create-react-app配置初探

首先贴出项目的启动相关的代码:

React Typescript音乐播放器项目笔记:4、create-react-app搭建代理(一)_第1张图片

然后贴出package.json文件的代码如下,这里并不想用webpack-dev-server中的proxy字段进行代理转发,所以package.json文件中也看不到proxy字段(这里在下一篇文章会实现利用这个字段以及用expres搭建一个代理转发服务器)。

{
  "name": "react-music-lhy",
  "version": "0.1.0",
  "private": true,
  "dependencies":{...}
  "scripts": {
    "start": "node scripts/start.js",
    "build": "node scripts/build.js",
    "test": "node scripts/test.js"
  },
  "eslintConfig":{...}
  "browserslist":{...}
  "jest":{...}
  "babel":{...}
  "devDependencies":{...}
}

执行npm start启动项目,执行start.js,代码如下:

'use strict';

// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';

// Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code.
//通过监听unhandledrejection事件,promise被reject了但是没有reject处理函数时,该事件被触发。
// The unhandledrejection event is fired
// when a JavaScript Promise is rejected
// but there is no rejection handler to deal with the rejection.
process.on('unhandledRejection', err => {
  throw err;
});

// Ensure environment variables are read.
require('../config/env');

const fs = require('fs');
const chalk = require('chalk');
const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const clearConsole = require('react-dev-utils/clearConsole');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const {
  choosePort,
  createCompiler,
  prepareProxy,
  prepareUrls,
} = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser');
const paths = require('../config/paths');
//用于生成不同模式下的config
const configFactory = require('../config/webpack.config');
//webpack-dev-server的配置
const createDevServerConfig = require('../config/webpackDevServer.config');

const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;

// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
  process.exit(1);
}

// Tools like Cloud9 rely on this.
//webpack-dev-server监听的端口,默认为3000端口,访问url为localhost:3000的时候会被webpack-dev-server服务器代理并返回html页面
//host默认为'0.0.0.0'也就是localhost
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
const HOST = process.env.HOST || '0.0.0.0';

if (process.env.HOST) {...}

// We require that you explictly set browsers and do not fall back to
// browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
checkBrowsers(paths.appPath, isInteractive)
  .then(() => {
    // We attempt to use the default port but if it is busy, we offer the user to
    // run on a different port. `choosePort()` Promise resolves to the next free port.
    return choosePort(HOST, DEFAULT_PORT);
  })
  .then(port => {
    if (port == null) {
      // We have not found a port.
      return;
    }
	//生成开发环境下的config
    const config = configFactory('development');
    const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
    const appName = require(paths.appPackageJson).name;
    const urls = prepareUrls(protocol, HOST, port);
    // Create a webpack compiler that is configured with custom messages.
	//利用配置进行编译,webpack(config)的封装
    const compiler = createCompiler(webpack, config, appName, urls, useYarn);

    // Load proxy config
      //没有配置packjson的时候proxyConfig为undefined
    const proxySetting = require(paths.appPackageJson).proxy;
    const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
    // Serve webpack assets generated by the compiler over a web server.
    //urls.lanUrlForConfig是根据协议,host以及端口生成的,这里为本机的ip地址
    const serverConfig = createDevServerConfig(
      proxyConfig,
      urls.lanUrlForConfig
    );
    const devServer = new WebpackDevServer(compiler, serverConfig);
    // Launch WebpackDevServer.
    devServer.listen(port, HOST, err => {
      if (err) {
        return console.log(err);
      }
      if (isInteractive) {
        clearConsole();
      }
      console.log(chalk.cyan('Starting the development server...\n'));
        console.log(chalk.cyan(urls.lanUrlForConfig))
	  //打开浏览器,默认为localhost
      openBrowser(urls.localUrlForBrowser);
    });

	['SIGINT', 'SIGTERM'].forEach(...);
  })
  .catch(err => {
    if (err && err.message) {
      console.log(err.message);
    }
    process.exit(1);
  });

start.js根据如下代码导入webpackDevServer.config对webpack-dev-server创建的服务器进行自定义配置:

	const createDevServerConfig = require('../config/webpackDevServer.config');
    const serverConfig = createDevServerConfig(
      proxyConfig,
      urls.lanUrlForConfig
    );

搭建简易代理

webpackDevServer.config.js的代码如下:

'use strict';

const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
const ignoredFiles = require('react-dev-utils/ignoredFiles');
const paths = require('./paths');
const fs = require('fs');

const appRoute = require('../server/server.js')

const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const host = process.env.HOST || '0.0.0.0';

module.exports = function(proxy, allowedHost) {
  return {
    disableHostCheck:
      !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
    // Enable gzip compression of generated files.
    compress: true,
    // 去掉WebpackDevServer's日志,但是会显示编译错误以及警告
    clientLogLevel: 'none',
    //告诉服务器从哪个目录中提供内容。只有在你想要提供静态文件时才需要。devServer.publicPath 将用于确定应该从哪里提供 bundle,并且此选项优先。
    contentBase: paths.appPublic,
    // By default files from `contentBase` will not trigger a page reload.
    watchContentBase: true,
	//模块热更新,css有效,js等其他的需要自己利用loder配置
    hot: true,
    //假设服务器运行在 http://localhost:8080 并且 output.filename 被设置为 bundle.js。默认 publicPath 是 "/",所以你的包(bundle)可以通过 http://localhost:8080/bundle.js 访问。publicPath: '/assets/',可以通过 http://localhost:8080/assets/bundle.js 访问 bundle。
    publicPath: '/',
    // 不打印打包过程
    quiet: true,
    watchOptions: {
      ignored: ignoredFiles(paths.appSrc),
    },
    // Enable HTTPS if the HTTPS environment variable is set to 'true'
    https: protocol === 'https',
    host,
    overlay: false,
    historyApiFallback: {
      // Paths with dots should still use the history fallback.
      // See https://github.com/facebook/create-react-app/issues/387.
      disableDotRule: true,
    },
    //  结合nginx的时候需要配置
    public: allowedHost,
    proxy,
    before(app, server) {
	  //src下没建立setupProxy,因此此处没用
      if (fs.existsSync(paths.proxySetup)) {
        // This registers user provided middleware for proxy reasons
        require(paths.proxySetup)(app);
      }
      // appRoute(app)
      // This lets us fetch source contents from webpack for the error overlay
      app.use(evalSourceMapMiddleware(server));
      // This lets us open files from the runtime error overlay.
      app.use(errorOverlayMiddleware());
      // This service worker file is effectively a 'no-op' that will reset any
      // previous service worker registered for the same host:port combination.
      // We do this in development to avoid hitting the production cache if
      // it used the same host and port.
      // https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
      app.use(noopServiceWorkerMiddleware());
    },
  };
};

这里介绍一种简单的代理,注意上面代码中有如下代码:

const appRoute = require('../server/server.js')
before(app, server) {
	...
  appRoute(app)
	...
}};

devServer.before的作用:在服务内部的所有其他中间件之前, 提供执行自定义中间件的功能。 这可以用来配置自定义处理程序

由于webpack-dev-server是基于express实现的,会执行devServer.before(app),其中appwebpack-dev-server利用express创建的web服务器对象,因此可以在这个函数中为app这个服务器增加自定义的路由。整个请求的逻辑就是:

在浏览器中输入http://localhost:3000/——>webpack-dev-server创建的服务器监听到3000端口的请求返回index.html文件以及boundle.js——>操作页面执行boundle.js中的代码向http://localhost:3000/api/getSomthing发出请求——>webpack-dev-server创建的服务器监听到3000端口的请求,根据开发者定义的路由向本地数据库或者远端服务器请求数据并返回。

api请求的顺序就是:http://localhost:3000收到的请求被代理到webpack-dev-server创建的服务器,webpack-dev-server服务器向远端服务器请求数据,然后数据由webpack-dev-server返回给http://localhost:3000最终展示在页面上。

这里在server目录下server.js文件中写如下代码:

const express = require('express')
const opn = require('opn')
const path = require('path')
const axios require('axios')
const apiRoutes = express.Router()
apiRoutes.get('/getDiscList', function (req, res) {
    var url = 'https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg'
    axios.get(url, {
        headers: {
            referer: 'https://c.y.qq.com/',
            host: 'c.y.qq.com'
        },
        params: req.query
    }).then((response) => {
        res.json(response.data)
    }).catch((e) => {
        console.log(e)
    })
})

module.exports = function(app){
    app.use('/api', apiRoutes)
}

请求结果如下,request URL是本地,由webpack-dev-server代理转发给远端
React Typescript音乐播放器项目笔记:4、create-react-app搭建代理(一)_第2张图片

总结

在webpackDevServer.config.js中,也就是devServer对象的before方里,在传入的express的实例app上添加一些路由即可。create-react-app搭建代理(二)搭建更灵活的代理。

你可能感兴趣的:(webpack,git,React项目)