如何从一个NPM包中导出多个ES6模块

26

我已经构建了一个相对较小的NPM包,包含大约5个不同的ES6类,每个类都包含在一个文件中,它们看起来几乎都像这样:

export default class MyClass {
    // ...
}

我随后为我的程序包设置了一个入口点,如下所示:

export { default as MyClass } from './my-class.js';
export { default as MyOtherClass } from './my-other-class.js';

我随后通过 webpack 和 babel 运行入口点,最终得到了经过转译和缩小的 index.js 文件。

安装并导入包运行良好,但是当我在客户端代码中执行以下操作时:

import { MyClass } from 'my-package';

它不仅仅导入"MyClass",而是导入整个文件,包括每个类的所有依赖项(我的某些类有大量的依赖项)。

我认为当你尝试导入已经捆绑的软件包的部分时,这就是webpack的工作方式? 因此,我设置了本地webpack配置以通过babel运行 node_modules/my-package,然后尝试了:

import { MyClass } from 'my-package/src/index.js';

但是即使这样也会导入index.js导出的每个类。唯一看起来按照我想要的方式工作的是,如果我这样做:

import MyClass from 'my-package/src/my-class.js';

但我更希望:

  1. 能够导入经过转译和压缩的文件,这样我就不必告诉webpack在node_modules中运行babel了。
  2. 能够直接从我的入口点导入每个单独的类,而不必输入每个文件的路径。

这里有什么最佳实践?别人如何实现类似的设置?我注意到GlideJS有一个ESM版本的包,它允许你只导入你需要的东西,而无需像运行babel那样对它进行处理。

相关的包:https://github.com/powerbuoy/sleek-ui

webpack.config.js

const path = require('path');

module.exports = {
    entry: {
        'sleek-ui': './src/js/sleek-ui.js'
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, 'dist'),
        library: 'sleek-ui', // NOTE: Before adding this and libraryTarget I got errors saying "MyClass() is not a constructor" for some reason...
        libraryTarget: 'umd'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: ['@babel/preset-env']
                        }
                    }
                ]
            }
        ]
    }
};

包描述文件

  "name": "sleek-ui",
  "version": "1.0.0",
  "description": "Lightweight SASS and JS library for common UI elements",
  "main": "dist/sleek-ui.js",
  "sideEffects": false, // NOTE: Added this from Abhishek's article but it changed nothing for me :/
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --mode production"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/powerbuoy/sleek-ui.git"
  },
  "author": "Andreas Lagerkvist",
  "license": "GPL-2.0-or-later",
  "bugs": {
    "url": "https://github.com/powerbuoy/sleek-ui/issues"
  },
  "homepage": "https://github.com/powerbuoy/sleek-ui#readme",
  "devDependencies": {
    "@babel/core": "^7.8.6",
    "@babel/preset-env": "^7.8.6",
    "babel-loader": "^8.0.6",
    "webpack": "^4.42.0",
    "webpack-cli": "^3.3.11"
  },
  "dependencies": {
    "@glidejs/glide": "^3.4.1",
    "normalize.css": "^8.0.1"
  }
}


2
你是否在你的lib的package.json中添加了 main(入口点)属性?检查一下你的构建。另外,你是如何捆绑打包你的lib文件的? - Abhishek
是的,main属性指向我的index.js文件,该文件导出了所有其他类。我使用webpack和babel打包main/index.js文件。这在问题中都有解释。 - powerbuoy
这可能会对你有所帮助 - https://www.danielberndt.net/blog/2018/you-might-not-need-rollup-for-libraries/ - Abhishek
@powerbuoy,我想花点时间帮助你。你能否分享代码或者一个简单的复现呢?我猜看到webpack配置和其他文件会很有帮助。 - Dawid Zbiński
请在此处发布您的webpack配置。 - user9408899
显示剩余11条评论
2个回答

4

我认为当你尝试导入已捆绑包的部分时,这就是webpack的工作方式?

是的,您设置的方式正在导入index.js中的每个类,然后将其转译为一个文件(如果它的目标是ES5,则为最常见*)。这意味着当该文件在另一个文件中导入时,它会完整地带有所有这些类。

如果您想要正确的树摇,应避免将其转译为CommonJS(ES5)包。我的建议是保留ES6模块,可以单独使用或与ES5包不同的位置。本文应帮助您充分了解此并提供推荐的说明。基本上,这归结为使用preset-env设置Babel环境(如果您尚未使用它,则强烈建议!)以保留ES6语法。如果您不想将其转译为ES5,则以下是相关的Babel配置:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "esmodules": true
        }
      }
    ]
  ]
}
本文详细介绍如何设置2个bundle,每个bundle使用不同的模块语法。
值得注意的是,在文章中也提到了,你可以在package.json中设置ES模块入口点。这告诉Webpack/Babel可以找到ES6模块的位置,这可能是你用例所需要的全部内容。看起来传统智慧建议执行以下操作:
{
  "main": "dist/sleek-ui.js",
  "module": "src/main.js"
}

但是Node文档中是这样写的:
{
  "type": "module",
  "main": "dist/sleek-ui.js",
  "exports": {
    ".": "dist/sleek-ui.js",
    "./module": "src/main.js"
  }
}

如果我有时间,我会尝试一下哪个是正确的,但这应该足以让您走上正确的道路。

* ES5目标的捆绑包是以CommonJS格式呈现的,这意味着必须包含所有相关文件,因为ES5不支持本地模块。这在ES2015 / ES6中得到了支持。


我尝试添加 targets.esmodules: true,虽然它对构建脚本进行了更改,但最终导入的内容没有任何变化。从 my-package 导入一个单独的类仍然会导入所有内容。我还尝试了在 package.json 中进行更改(以及其他更改),但也没有改变任何东西。好吧,添加 type: module 实际上破坏了我的构建,出现了 "Must use import to load ES Module: /sleek-ui/webpack.config.js require() of ES modules is not supported." 的错误,所以我不得不删除那一部分。我会查看链接的文章。 - powerbuoy
好的,所以这篇文章实际上告诉我要设置 modules: false(不是在 targets 中),但那也没用... 我想我只能直接从源文件导入并继续通过 node_modules 运行 babel,直到我们可以原生地使用这些东西。 - powerbuoy
@powerbuoy 从源文件导入是可以正常工作的。也许从我的帖子中并不清楚,如果是这种情况,我可以编辑它,但你只想引入类,就像 import MyClass from 'my-package/myClass';。一个很好的仓库示例是 lodash-es - CaitlinWeb
@powerbuoy 如果这个方法对您有用,您可以将其标记为答案吗? - CaitlinWeb

-3

这是一个有效的用例。最终目标是这样做:import { MyClass } from 'my-package',但有一种更简洁的方法。

在你的my-package中创建一个聚合器索引文件。基本上是my-package/index.js,它应该像这样:

import MyClass from './my-class.js'
import MyOtherClass from './my-other-class.js'

export { MyClass, MyOtherClass }

然后你可以执行 import { MyClass } from 'my-package'。非常简单。

玩得开心!


2
这正是我已经在做的事情,我认为这在问题中已经很清楚了。 - powerbuoy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接