关于基于process.env.NODE_ENV的条件导出ES6模块的可能性?

时间:2019-03-19 21:18:15

标签: javascript ecmascript-6 module static export

我编写了一个实用程序库,当我的用户tree-shaking他们的应用程序时,我想publishes他们。

在Webpack v4中,您需要使模块ES6支持tree-shaking,但是我也想将development buildproduction build拆分为不同的文件。 / p>

我想要的就像是React的NPM模块:

// index.js
'use strict';

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./cjs/react.production.min.js');
} else {
  module.exports = require('./cjs/react.development.js');
}

这给我带来了疑问。

如果我将实用程序模块全部设为commonjs,我将永远无法得到tree-shaking,我的应用程序将会变得如此庞大。

如果我的实用程序模块全部为ES6 static export,则必须在development message中包含production code

发布两个模块(例如:my-utilitymy-utility-es)将无济于事,因为在开发中,我的代码如下:

import { someFunc } from 'my-utility';

但是在生产代码中,我将不得不将其更改为:

import { someFunc } from 'my-utility-es';

我该如何解决这个问题?

更新

更清楚地说,我的development buildproduction build包含不同的源代码(例如:生产版本删除了所有错误消息)

所以指定webpack模式对我来说并不令人满意。

3 个答案:

答案 0 :(得分:2)

我自己找到了答案,我认为最好的方法是通过babel macros

import { something } from 'myLibrary/macro';

// In webpack development:
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
// import { something } from 'myLibrary/development';

// In webpack production:
// ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
// import { something } from 'myLibrary/production';

我的宏实现:

import { createMacro } from 'babel-plugin-macros';

function macro({ references, state, babel }) {
  state.file.path.node.body.forEach(node => {
    if (node.type === 'ImportDeclaration') {
      if (node.source.value.includes('myLibrary/macro')) {
        if (process.env.NODE_ENV === 'production') {
          node.source.value = 'myLibrary/module/production';
        } else {
          node.source.value = 'myLibrary/module/development';
        }
      }
    }
  });

  return { keepImports: true };
}

export default createMacro(macro);

答案 1 :(得分:1)

这是我找到的最好的解决方案,不需要用户使用Babel宏...

让我们说:

  • 我们的图书馆称为crazy-components
  • 它导出两个名为ComponentAComponentB的React组件
  • 这些组件仅使用代码防护来在开发人员模式下进行一些记录
// src/index.js
import React from 'react';

export function ComponentA(props) {
  if (process.env.NODE_ENV !== 'production') {
    console.log(`Rendering ComponentA with props ${props}`);
  }

  return <div>ComponentA message: {props.msg}</div>;
}

export function ComponentB(props) {
  if (process.env.NODE_ENV !== 'production') {
    console.log(`Rendering ComponentB with props ${props}`);
  }

  return <div>ComponentB message: {props.msg}</div>;
}

我们希望图书馆:

  1. 是可摇树的,因此如果用户使用import { ComponentA } from 'crazy-components',则ComponentB的代码将不会出现在其捆绑包中。

  2. 从生产包中剥离了日志记录代码。

解决方案

1。捆绑库与汇总

2。配置汇总以在生产版本和开发版本(剥离了日志代码的生产版本等)中创建CJS和ESM版本。

CJS构建输出到/dist/cjs,ESM构建输出到/dist/esm。文件分别称为crazy-components.prod.min.jscrazy-components.dev.js

仅开发版本包含日志记录代码(没有解释如何完成所有这些操作,如果您正在阅读此书,您可能已经知道了)。

3。创建CJS和ESM的入口点,如下所示:

// index.js
if (process.env.NODE_ENV === 'production') {
    module.exports = require('./dist/cjs/crazy-components.min.js');
} else {
    module.exports = require('./dist/cjs/crazy-components.js');
}
// es/index.js
import {
    ComponentA as ComponentA_prod,
    ComponentB as ComponentA_prod
} from '../dist/esm/crazy-components.prod.min.js';

import {
    ComponentA as ComponentA_dev,
    ComponentB as ComponentA_dev
} from '../dist/esm/crazy-components.dev.js';

export const ComponentA = process.env.NODE_ENV === 'production' ? ComponentA_prod : ComponentA_dev;
export const ComponentB = process.env.NODE_ENV === 'production' ? ComponentB_prod : ComponentB_dev;

4。指向package.json中的两个入口点:

// package.json
{
  "name": "crazy-components",
  "version": "1.0.0",
  "main": "index.js",
  "module": "es/index.js",
  "sideEffects": false
}

5。 (可选)在NodeJS中将其导入为ESM

节点12(带有标志)和节点13+ support ES modules natively

添加到package.json

  "exports": {
    ".": {
      "import": "./es/index.js",
      "require": "./index.js"
    },
    "./es": "./es/index.js"
  },

package.json文件夹中添加额外的es文件,以将该文件夹的内容标记为对NodeJS的ESM:

// es/package.json
{
  "type": "module"
}

使用rollup-plugin-copy来获取汇总,并将该文件也复制到dist/esm中:

// rollup.config.js
import copy from 'rollup-plugin-copy';
/* ... other imports ... */

export default {
  input: 'src/index.js',
  /* ... other config ... */
  plugins: [
    /* ... other plugins ... */
    copy({targets: [{src: 'es/package.json', dest: 'dist/esm'}]})
  ]
};

可能的改进

es/index.js是手工创建的,因此如果以后添加ComponentC,它也需要添加到es/index.js中。如果有一个Rollup插件可以自动创建es/index.js,那是理想的选择,但是我还没有找到。

此外,您的里程可能会有所不同。我今天才刚刚尝试过。在create-react-app应用程序中导入库后,它似乎可以按预期工作,但是我尚未使用手动编码的Webpack配置对其进行过测试。

这种方法应该可以推广到任何库,不仅是React组件,而且我还没有尝试过。

非常欢迎任何改进建议!

答案 2 :(得分:0)

解决此问题所需要做的就是使用mode。参见Specify the Mode

  

自webpack v4起,指定模式会自动为您配置DefinePlugin:

webpack.prod.js

  const merge = require('webpack-merge');
  const common = require('./webpack.common.js');

  module.exports = merge(common, {
    mode: 'production',
  });

他们提到了React的名字:

  

如果您使用的是类似react之类的库,那么添加此插件后,捆绑包的大小实际上应该会明显下降。