likes
comments
collection
share

不用 npm run eject,我是这么配置webpack的

作者站长头像
站长
· 阅读数 3

前言

如果开发者要导出 webpack,必须使用 react-script eject。但这是一个单向操作,eject 后,就无法恢复了。个人觉得这种操作十分不便利,远没有vue 自带vue.config.js 的配置这么方便可行。推荐使用react-app-rewiredcustomize-cra 的插件组合,再通过 config-overrides.js 实现对 webpack 的自由配置。

项目使用 eject

如下所示,执行 create-react-app xx(项目名称) 初始化一个react 项目之后,在package.json中可以看到如下脚本命令。

{
  ...
  "scripts": {
    "eject": "react-scripts eject"
  },
  ...
}

执行命令:npm run eject后,会将封装在 CRA 中的配置全部反编译到当前项目,这样用户就可以完全取得 webpack 文件的控制权,可以随心所欲修改wabpack配置。如下所示。CRA与其他脚手架不同的地方在于可以通过升级其中的react-scripts包来升级 CRA 的特性。但如果我们使用了eject命令,就再也享受不到 CRA 升级带来的好处了,原因如下:

react-scripts 已经是以文件的形式存在于项目中,而不是以包的形式,所以无法对其升级。

config
├── env.js
├── jest
│   ├── cssTransform.js
│   └── fileTransform.js
├── paths.js
├── polyfills.js
├── webpack.config.dev.js // 开发环境配置
├── webpack.config.prod.js // 生产环境配置
└── webpackDevServer.config.js

项目使用react-app-rewired

在 CRA 创建的项目中安装了react-app-rewired后,可以通过创建一个config-overrides.js 文件来对 webpack 配置进行扩展。具体操作如下:

  1. 安装 react-app-rewired customize-cra:
yarn add react-app-rewired customize-cra -D
  1. 修改package.json文件,替换其中的 react-scripts:
......
"scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-scripts eject"
},
......
  1. 在项目根目录新建config-overrides.js
const { override } = require('customize-cra');

module.exports = {
  ......
};

1. 常规开发配置(打包路径、gzip压缩、端口配置等)

通过从 customize-cra中引入 override,然后在 override() 中自定义打包配置相关函数,可以实现自定义的 webpack 配置。

const { override } = require("customize-cra");
const CompressionWebpackPlugin = require('compression-webpack-plugin');
const path = require("path");

// 修改端口号:
process.env.PORT = 8000;

// 常规配置
const addCustomize = () => (config) => {
  if (process.env.NODE_ENV === 'production') {
    // 配置打包后的文件位置(默认为build, 修改为dist)
    const paths = require("react-scripts/config/paths");
    paths.appBuild = path.join(path.dirname(paths.appBuild), "dist");
    config.output.path = path.join(path.dirname(config.output.path), "dist");
    // 添加js打包gzip配置
    config.plugins.push(
      new CompressionWebpackPlugin({
        test: /.js$|.css$/,
        threshold: 1024,
      }),
    )
  }
  if (config.resolve) {
    config.resolve.extensions.push(".jsx");
  }
  return config;
};

module.exports = override(
   addCustomize()
)

2. 通过postcss-pxtorem 插件实现移动端适配

实现移动端自适应布局的最简单方式就是通过 lib-flexiblepostcss-pxtorem插件实现。

  1. 安装插件:
yarn add lib-flexible postcss-pxtorem
  1. 项目主入口引入 lib-flexible,该插件可以获取当前h5屏幕尺寸来设置跟元素的 font-size大小, 实现响应式的屏幕尺寸适配。
// 在 index.js 文件中引入
import 'lib-flexible';
  1. 再配置 postcss-pxtorem 插件,该插件的作用是可以把 px 尺寸转成 rem 尺寸,方便我们在写样式的时候,可以直接写 px 就可以了。在当前config-overrides.js 中配置目前发现一个问题。几乎网上全都是通过以下方法来解决适配问题:
const { override, addPostcssPlugins } = require("customize-cra");

module.exports = override(
   ......
   addPostcssPlugins([require('postcss-pxtorem')({ 
     rootValue: 37.5, 
     propList: ['*'], 
     minPixelValue: 2
   })]),
)

但是本人在配置的时候始终不生效,最后不得已通过执行 npm run eject 后在修改webpack 配置,发现还是不生效,目前网上千篇一律的这种做法,也没有附带解决方案,感受到了极大的困扰。然后发现可以通过在pacakge.json 中的直接添加 postcss 配置实现,算是可以顺利解决这个问题。

...
"postcss": {
    "plugins": {
      "autoprefixer": {},
      "postcss-pxtorem": {
        "rootValue": 37.5,
        "selectorBlackList": [],
        "propList": [
          "*"
        ]
      }
    }
  },
 ...

3. 引入less 和 antd插件的按需加载

需要支持less 编写样式,需要安装依赖:lessless-loaderbabel-plugin-import; 再通过引入 antd 插件,具体的配置如下:

const { override, fixBabelImports, addLessLoader } = require("customize-cra");

module.exports = override(
  // 针对antd实现按需打包: 根据import来打包(使用babel-plugin-import)
  fixBabelImports("import", {
    libraryName: "antd",
    libraryDirectory: "es",
    style: true, // 自动打包相关的样式
  }),

  // 使用less-loader对源码中的less的变量进行重新指定
  addLessLoader({
    javascriptEnabled: true,
    modifyVars: { "@primary-color": "#1DA57A" }, // 自定义antd的主题
    lessOptions: {
      javascriptEnabled: true,
      localIdentName: '[local]--[hash:base64:5]',
    },
  })

执行代码后发现奇怪的报错:

PostCSS Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'plugins'. These properties are valid:

可以通过再加上如下配置解决:

adjustStyleLoaders(({ use: [, , postcss] }) => {
    const postcssOptions = postcss.options;
    postcss.options = { postcssOptions };
}),

4. 添加别名

配置别名后,可以在组件路径引入的地方统一通过别名处理, 例如组件引入: import Name from '@/pages/name' , 例如文件路径引入:<img src={require("@/imgs/pa-icon.png")} alt="" />等,采用别名的好处在于清晰文件路径,同时加快webpack构建的速度。

const { override, addWebpackAlias } = require("customize-cra");

module.exports = override(
  // 配置路径别名
  addWebpackAlias({
    "@": path.resolve(__dirname, "./src")
  }),

5. sourceMap控制

const { override, addWebpackAlias } = require("customize-cra");

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

module.exports = override(
  // sourceMap控制
  closedMap(),

6. 添加 addWebpackExternals 排除打包插件,用来配置cdn地址

通过cdn引入能大大减少打包体积的大小,提升首页加载时间,可以通过以下方式实现:

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

module.exports = override(
  // 排除打包依赖插件,配置cdn引入
  addWebpackExternals({
    react: 'React',
    'react-dom': 'ReactDOM',
    'react-router-dom': 'ReactRouterDOM',
    'redux': 'Redux',
    'axios': 'axios'
  })
)

同时在public 的index.html 文件中引入cdn地址:

<!-- react相关 -->
<script crossorigin src="https://unpkg.com/react@17.0.2/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.production.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/react-router-dom/5.3.0/react-router-dom.min.js"></script>
<script src="https://unpkg.com/redux@4.0.1/dist/redux.js"></script>
<!-- axios -->
<script type="text/javascript" src="https://unpkg.com/axios/dist/axios.min.js"></script>

7. addWebpackModuleRule 处理图片

addWebpackModuleRule 相当于webpack里Module的rules,通过规则匹配来处理。如下所示,通过加载插件url-loader 来处理图片,可以将 20kb 以下的图片转成base64, 超出则不处理,将文件输出到目录static/imgs/下。

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

module.exports = override(
  addWebpackModuleRule({
    test: /\.(png|jpg|gif|jpeg|svg)$/i,
    use: [{
      loader: 'url-loader',
      options: {
        limit: 20 * 1024,
        esModule: false,
        outputPath: `static/imgs/`,
      },
    },],
  }),
)