还在run eject 修改create-react-app中的配置?

11,209 阅读8分钟

前奏曲

众所周知, create-react-app(以下称为CRA)是 FaceBook 开源的 创建 React 现代Web应用程序应用的脚手架,让我们可以快速和专注项目的开发而不用过多的去关心工具和服务的配置,在一些场景中还是需要另外的加入或者修改 CRA 中的配置,在 CRA 的官方文档中指出可以通过执行npm run eject 来暴露出配置文件来进行修改,但是这种方式是不可逆的,同时会有一些隐藏的问题。

eject做了什么

当你运行 npm run eject会将潜藏的一系列配置文件和一些依赖项都“弹出”到项目中,然后就可以由你自己完全控制,但是这个过程是不可逆的。

来看看 npm run eject后项目发生了什么变化,以下的示例使用 npx create-react-app进行创建。

eject之前的目录结构:

屏幕快照 2019-12-30 上午9.10.44.png

eject之后的目录结构:

image.png


通过Git版本管理追踪到的文件名高亮显示可以看出eject后产生变化,而打开package.json可以看到项目的依赖项(dependencies)和npm script发生了变化以及添加了 jest的配置。

eject副作用

虽然Eject后可以根据自己的需要添加配置项做一些更高阶的配置,但是由于这个操作是不可逆的,所以也会带来一些问题:

无法迎接未来

如果后续的create-react-app更新了并添加了很多不错的功能,你想应用到你的项目中,可以通过更新 create-react-app版本来实现,而不是去更新webpack的配置,但是如果已经对项目进行了 eject操作,那么你无法很好的“迎接”未来。

代码洁癖

create-react-app其中的一个目的就是可以让你专注与项目的开发,这对于一些有代码洁癖或者喜欢保持应用程序尽可能干净的人是友好的,他不用去关心其他的配置与服务,看不到其他从来不用关心的代码,然而在 eject之后,package.json就写入了很多依赖项,还有其他的脚本和配置文件,差不多有10多个新文件,每个文件包含了50-200行代码,这对于这类人在维护依赖项的时候会辣眼睛。

回到根本

eject修改配置后,你会将其复制粘贴到其他的项目中,因为在大多数情况下,项目的配置(比如webpack与babel)都是相同的。如果你由多个类似的项目,那么你必须得自己去维护多份配置和脚本,告辞!

eject前请三思

在你 eject之前请多思考和对你的问题或者疑问进行调查。

Eject后自己添加补充的功能是CRA缺少的吗?

CRA 缺少所需要的功能,需要自己去添加,这其实是大部分人进行 eject 的原因。但是在 eject之前,请你花点时间思考一下或者搜索一下来确保自己需要添加的功能不会变成额外的工作或者重复别人之前已经做过的工作。

CRA的 issues 中已经记录了很多的问题,很有可能已经有人解决了你的问题,去在讨论区里探索一下,你有可能会发现解决方案或者替代的方法,如果没有关于你问题的相关讨论,你可以考虑提出 issues ,而且经过深思的 issues 是值得被赞赏的。

Eject给你带来多少价值?


eject修改配置文件之前,你可以问问自己,如果进行这个更改可以增加多少价值?增加价值是否超过了管理构建过程中所引入的认知负担?

在执行eject后,你得对更新那些你可能没有完全了解的代码负责,因为 eject后将添加数千行复杂的代码进行构建和测试,你需要学习这些代码来正确的更新、测试和构建。如果构建不通过,CRA 也将无法支持你的自定义配置,如果你有一个开发团队,你是否相信他们还需要充分了解他们对构建过程所做的更改来保证其稳定性。

需要对CRA进行很大更改?


如果你的工作流是很特殊的,需要特殊的工具或者服务,但是在你 eject之前,请思考让你的用例比你想象中的更加重用,fork react-script允许你进行更改,还可以在其他类似的项目上进行重用,而不是 eject了然后自己去维护。这种方式相对于直接进行 eject是一种更灵活和维护性更强的一种修改方式,可以和CRA一样进行升级script包来应用特性。

允许其他人使用并为你的构建过程做出贡献,这可以提高项目的稳定性和完善性。Fork 也是CRA团队所建议的,这篇文档提供了指南来引导你如何将 create-react-app定制化为自己的利器。

想学习CRA内部构建过程的原理?


这是极好的!如果你想学习内部的工作原理,那么请尽情的进行 eject!不过了解CRA的工作原理是一项不简单的任务,如果你感到困惑或者迷茫,请不要灰心,多在社区、google、stackoverflow上进行探索,你不会辜负你所付出的!

替代方案

react-app-rewired + customize-cra

文档连接:

在项目中使用过 ant design的应该知道,可以使用react-app-rewiredcustomize-cra这两个工具库来进行自定义配置。

image.png

对于 create-react-app1.x需要使用 react-app-rewired@1.62,而因为各自版本升级的原因,在到 react-app-rewired@2.x版本的时候只保留了核心的功能,在这个commit你可以看到移除了 rewire helper 的功能函数。因此你需要借助customize-cra来自定义 CRA v2+以上的配置。

常见用法

首先安装依赖:

yarn add react-app-rewired customize-cra

antd 配置:

修改npm script:

"start": "PROT=3002 react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",

项目根目录创建 config-override.js并写入配置:

// confit-override.js
// 按需加载组件代码和样式
// addLessLoader 来帮助加载 less 样式,帮助自定义主题
// 使用插件让 Day.js 替换 momentjs 减小打包大小,
const { override, fixBabelImports,addWebpackPlugin, addLessLoader } = require('customize-cra');
const AntdDayjsWebpackPlugin = require('antd-dayjs-webpack-plugin');
const path = require('path');

module.exports = override(
    fixBabelImports('import', {
        libraryName: 'antd',
        libraryDirectory: 'es',
        style: true,
    }),
   addLessLoader({
      javascriptEnabled: true,
      modifyVars: { '@primary-color': '#1DA57A' },
    }),
    addWebpackPlugin(new AntdDayjsWebpackPlugin())
);

添加alias别名

在平时项目开发中,经常性会遇见引用组件或者工具函数时会出现这样的情况:

import header from '../../../../components/header';

而alias别名的设置,会让这更为方便的引用。

// config-override.js
const { addWebpackAlias} = require('customize-cra');
const resolve = dir => path.join(__dirname, '.', dir);

module.exports = override(
  ...
	addWebpackAlias({
    ['src']: resolve('./src')
  }),
  ...
)

如果是TS项目,注意需要在 tsconfig.json 中进一步设置,如果直接在 tsconfig.json 文件中直接设置 paths 属性,当重新run 的时候,属性又会被删除。

CRA issue 中的解决方式:
github.com/facebook/cr…

// tsconfig.json
{
 	...
	"extends": "./paths.json",
  ...
}
// paths.json

{
    "compilerOptions": {
      "baseUrl": "src",
      "paths": {
        "src/*": ["*"]
      }
    }
  }

API代理

const { 
  override, 
  overrideDevServer,
} = require('customize-cra');

const devServerConfig = () => config => {
  return {
      ...config,
      port: 3000,
      proxy: {
        '/app/v1': {
          target: 'http://localhost:3005',
          changeOrigin: true,
          ws: false,
          pathRewrite: {
            '^/app/v1': '/app/v1',
          },
          secure: false,
        },
      },
  }
}

module.exports = {
 	...
  devServer: overrideDevServer(
    devServerConfig()
  ),
  ...
}

关闭sourceMap

//config-override.js
const { 
  override, 
} = require('customize-cra');

const rewiredMap = () => config => {
    config.devtool = config.mode === 'development' ? 'cheap-module-source-map' : false;
    return config;
};

module.exports = override(
    rewiredMap()
);

其他更多用法可参见文档,传送门

@craco/craco

首先安装依赖:

yarn add @craco/craco 

同样修改 npm scripts:

"scripts": {
  "start": "craco start",
  "build": "craco build",
  "test": "craco test",
}

常见用法

修改alias别名

根目录创建craco.config.js 并写入配置

//craco.config.js
const path = require('path');
const resolve = dir => path.join(__dirname, '.', dir);

module.exports = {
 ...
 webpack: {
 	alias: {
  	 'src': resolve('src')
  }
 }
 ...
}

antd配置

安装 craco-antd插件

yarn add craco-antd 

添加配置

const CracoAntDesignPlugin = require("craco-antd");

module.exports = {
   	...
    plugins: [{ plugin: CracoAntDesignPlugin }],
    ...
}

其他更多用法可参见文档,传送门

最后

现在我们知道有哪些方式来修改CRA创建的项目中的默认配置,不管是 npm run eject或者是通过 fork一份 react-script来正对项目来高度定制化,在或者是用一些替代的方法都是可以达到目的的,不过从对比上来看,使用 craco或者 react-app-rewired+ customize-cra是可以满足绝大多数需求的,不过各自带来的效应不管是负面还是正面的,既然去使用来就得去面对。

记得在你进行 eject之前,请三思!多思考思考!你的项目和现有团队的项目中需要修改CRA中默认配置的需求到底是怎么样的!选择合适的,才是最好的!

人生也是,多思考,多看看自己内心深处到底需要的是什么。