Create React App:不使用eject,自定义webpack配置

前言:译者一直使用react,但是前段时间因为facebook的协议问题,公司开始禁止使用react来开发前端项目。(更新到react16的MIT协议也不行)于是,笔者决定将react替换为preact,这样就需要在webpack配置中设置alias,但是又不希望使用create-react-app中不可逆的eject。于是找到了这篇教程,简单翻译后结合自己的需要进行了修改。分享给有需要的人。

Create React App提供了一套非常不错的配置,能够上手即用,并且提供了“eject”功能,让你能够自己接管所有的配置项。

但是,如果当你仅仅是想微调一下Webpack的配置呢?而并不是接管所有的配置项。你可能想要增加SASS或者SCSS的支持,或者使用自己定义的.eslintrc文件。(译者的使用场景是在webpack中增加alias)

让我们开始讲解如何操作。

警告!
首先你要注意: 如果你并不了解Webpack的运行机制,或是不喜欢在工程里加入的hacky代码(少量的),我建议你不要使用这种方式,这是一种高级的技巧。

当Create React App变化时,特别是它使用的react-scripts包变化时,很有可能导致我们的代码需要一些修复。如果官方修改了webpack的结构,或者导出方式,我们的代码将出现问题,这时就需要你自己去了解如何修复它们。所以,再强调一次,如果你觉得这种方式有问题,请不要这么做!(译者觉得原作者有点夸张了)

破解Create React App
好了,有了这个可怕的免责声明,让我们来看看如何破解Create React App,如果你想直接去看示例项目的代码,也是可以的。在开始之前,确保使用的是最新的react-scripts,作者在编写的时候是1.0.11。(译者使用的是1.4.1)

Create React App的基础被封装在“react-scripts”包中,可以在package.json中的“dependencies”的列表里了解到。

我们将使用rewire来创建猴子补丁(运行时动态替换),使得我们在执行之前对Webpack配置进行定制。

下面这个文件是这个项目中最重要的部分。我建议在您的CRA项目中建立一个名为“scripts”的目录,并将这些代码放入scripts/customized-config.js文件中。你可以任意命名,不过(稍后我们将用到这个文件名)。

scripts/customized-config.js

/*
  本模块运行react-scripts里的脚本 (Create React App)
  可以自定义webpack配置,通过在项目根目录创建"config-overrides.dev.js" 、 "config-overrides.prod.js" 文件.

  A config-overrides file should export a single function that takes a
  config and modifies it as necessary.

  module.exports = function(webpackConfig) {
    webpackConfig.module.rules[0].use[0].options.useEslintrc = true;
  };
*/
var rewire = require('rewire');
var proxyquire = require('proxyquire');

switch(process.argv[2]) {
  // The "start" script is run during development mode
  case 'start':
    rewireModule('react-scripts/scripts/start.js', loadCustomizer('../config-overrides.dev'));
    break;
  // The "build" script is run to produce a production bundle
  case 'build':
    rewireModule('react-scripts/scripts/build.js', loadCustomizer('../config-overrides.prod'));
    break;
  // The "test" script runs all the tests with Jest
  case 'test':
    // Load customizations from the config-overrides.testing file.
    // That file should export a single function that takes a config and returns a config
    let customizer = loadCustomizer('../config-overrides.testing');
    proxyquire('react-scripts/scripts/test.js', {
      // When test.js asks for '../utils/createJestConfig' it will get this instead:
      '../utils/createJestConfig': (...args) => {
        // Use the existing createJestConfig function to create a config, then pass
        // it through the customizer
        var createJestConfig = require('react-scripts/utils/createJestConfig');
        return customizer(createJestConfig(...args));
      }
    });
    break;
  default:
    console.log('customized-config only supports "start", "build", and "test" options.');
    process.exit(-1);
}

// Attempt to load the given module and return null if it fails.
function loadCustomizer(module) {
  try {
    return require(module);
  } catch(e) {
    if(e.code !== "MODULE_NOT_FOUND") {
      throw e;
    }
  }

  // If the module doesn't exist, return a
  // noop that simply returns the config it's given.
  return config => config;
}

function rewireModule(modulePath, customizer) {
  // Load the module with `rewire`, which allows modifying the
  // script's internal variables.
  let defaults = rewire(modulePath);

  // Reach into the module, grab its global 'config' variable,
  // and pass it through the customizer function.
  // The customizer should *mutate* the config object, because
  // react-scripts imports the config as a `const` and we can't
  // modify that reference.
  let config = defaults.__get__('config');
  customizer(config);
}

为了跑通代码,你需要安装一些额外的依赖包:

npm install –save rewire proxyquire

你可以通过注释来了解它是如何工作的。最有趣的部分是位于底部的rewireModule方法,它使用rewire库来查看另一个文件,并获取定义在那里的配置变量的引用。

一旦你完成了这个操作,就可以为开发,生产,测试环境编写用来覆盖的配置文件。这一部分完全取决于你——无论你对CRA的Webpack配置做了什么改动,你都可以直接去做。

这些文件应该直接在CRA文件夹的根目录下,所有3个文件都是可选的。如果您想要重新配置它们的位置,只需改变上面“loadCustomizer”调用的路径。只是不要把它们放在“src”中就可以。

下面是一些开发环境替换配置的例子:

config-overrides.dev.js

const path = require('path');

module.exports = function(config) {
  // 使用你自己的 ESLint 
  let eslintLoader = config.module.rules[0];
  eslintLoader.use[0].options.useEslintrc = true;

  // Add the SASS loader second-to-last
  // (last one must remain as the "file-loader")
  let loaderList = config.module.rules[1].oneOf;
  loaderList.splice(loaderList.length - 1, 0, {
    test: /\.scss$/,
    use: ["style-loader", "css-loader", "sass-loader"]
  });
}

还需要创建一个config-overrides.prod.js文件具有相同的内容。开发过程中使用的是dev文件(例如npm start),在构建期间使用prod文件(例如,npm run build)。

为了让刚刚的配置生效,你需要安装SASS loader, 和它的依赖node-sass:

npm install –save sass-loader node-sass

最后,要让这些新代码生效,你需要更改package.json,调用这些新的脚本,而不是默认的react-scripts的脚本。要做到这一点,可以用以下方法替换“start”、“build”和“test”。

package.json

"scripts": {
  "start": "node scripts/customized-config start",
  "build": "node scripts/customized-config build",
  "test": "node scripts/customized-config test --env=jsdom",
}

示例项目
一个CRA生成的项目,在github上,点击这里.

译者补充:
1.原文没有讲到默认的配置文件位置,是在node_modules\react-scripts\config\目录下的
webpack.config开头的文件,例如:webpack.config.dev.js
2.替换react为preact只需要在config-overrides.dev.js文件中加入:

  /*
  * 替换react为preact
  * */
  let alias = config.resolve.alias
  alias["react"] = "preact-compat"
  alias["react-dom"] = "preact-compat"

相关资料:
preact替换react:https://preactjs.com/guide/sw…

    原文作者:恒1910
    原文地址: https://segmentfault.com/a/1190000011752103
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞