使webpack的库输出与babel6兼容

15

Babel的第6个版本改变了export default的功能,特别是它与commonjs的require之间的关系。

简而言之,在babel 5之前,require('module')返回的是模块的默认输出,现在它总是返回包含模块所有导出的模块对象。 如果只想要默认值,则必须使用require('module').default如此解释,这背后有非常好的原因,本问题的目的不是破坏或篡改这种行为。

然而,如果一个人正在构建一个库,他通常不想分发一个模块,而是想要分发他的库的导出值(例如,一个函数,无论内部使用什么模块系统)。 这可以通过webpack和使用commonjsAMD时的output.library配置很好地处理。由于早期的babel版本允许使用commonjs要求默认导出,因此babel也与这个机制兼容。但现在不再是这种情况:库现在始终提供es6模块对象。

这里有一个例子。

src/main.js

export default "my lib content";

webpack.config.js

var path = require("path");
var webpack = require("webpack");

module.exports = {
  entry: {
    lib: [ path.resolve(__dirname, "src/main.js") ],
  },
  output: {
    path: path.join(__dirname, "dist"),
    filename: "mylib-build.js",
    library: 'myLib'
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: "babel",
        include: path.join(__dirname, "src"),
        query: { presets: ['es2015'] }
      }
    ]
  }
};

test.html

<html>
<head></head>
<body>
<script src="dist/mylib-build.js"></script>
<!-- `myLib` will be attached to `window` -->
<script>
  console.log(JSON.stringify(myLib)); // { default: "my lib content" }
</script>
</body>
</html>

这是一个非常简单的例子,但我显然希望导出"my lib content"字符串而不是{ default: "my lib content" }

一种解决方案是创建一个 CommonJS 的导出源文件来执行转换:

module.exports = require('./main').default;

然而,我认为这个解决方案相当糟糕。应该能够在编译级别解决它,而不需要改变源代码。 有什么想法吗?


"应该能够通过编译选项解决问题,而无需更新源代码。有什么想法吗?" 我不太确定您期望什么样的答案。如果您认为这应该是一个编译选项,那么您应该向开发人员提出建议。 - Felix Kling
Webpack 配置非常强大。对于所有的加载器,有许多不同的插件和参数可供选择。 - Quentin Roy
例如,如果一个人只使用默认导出,他可以使用babel-plugin-transform-es2015-modules-commonjs将所有他的es6包转换为commonjs。 - Quentin Roy
我确实使用babel-loader(请参见webpack配置的module.loaders [0] .loader,自动添加了“-loader”部分)。尽管如此,我目前不使用.babelrc,但我可以。但我认为所有设置都可以作为加载程序的参数进行设置。 - Quentin Roy
1
@vvo 很遗憾还没有...所以我不得不使用导出解决方案,给webpack提供一个commonjs模块。 - Quentin Roy
显示剩余8条评论
3个回答

6

我刚想自己处理这个问题。无论是称其为解决方法还是权宜之计,似乎有一个Babel插件可以“解决”这个问题。

使用babel-plugin-add-module-exports插件,参考https://dev59.com/N5Lea4cB1Zd3GeqP8ezf#34778391

示例配置:

var webpackOptions = {
    entry: {
        Lib1: './src/Lib1.js',
        Lib2: './src/Lib2.js'
    },
    output: {
        filename: "Master.[name].js",
        library: ["Master","[name]"],
        libraryTarget: "var"
    },
    module: {
        loaders: [
            {
                loader: 'babel',
                query: {
                    presets: ['es2015'],
                    plugins: ["add-module-exports"]
                }
            }
        ]
    }
};

这将导致 Master.Lib1 代替 Master.Lib1.default 成为 lib1。

我想这是一个足够好的解决方法。然而,我认为它只应该应用于入口点。完全恢复babel的旧行为似乎不是一个好主意。 - Quentin Roy

4

Webpack 2现在支持ES6模块,这在一定程度上解决了这个问题。从Webpack 1迁移到Webpack 2相对来说并不痛苦。只需要记住禁用Babel的ES6模块转换为CommonJS即可使其正常工作:

.babelrc

{
  "presets": [
    ["es2015", {"modules": false}]
  ]
}

然而,不幸的是,它与 export default 不兼容(但 已经有一个问题被提出,希望最终会发布解决方案)。
编辑
好消息!Webpack 3 支持 output.libraryExport 选项,可用于直接公开默认导出:
var path = require("path");
var webpack = require("webpack");

module.exports = {
  entry: {
    lib: [ path.resolve(__dirname, "src/main.js") ],
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "mylib-build.js",
    library: "myLib",
    // Expose the default export.
    libraryExport: "default"
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: "babel",
        include: path.resolve(__dirname, "src")
      }
    ]
  }
};

-1

您可以使用此解决方案(这更像是一个变通方法,但它使您能够保持源代码不变):

有一个名为callback-loader的加载器。 它允许您在构建时通过调用回调并放置结果而更改您的源代码。 换句话说,您可以在构建时自动将所有require('module')转换为require('module').default

这是适用于它的配置:

var webpackConfig = {
    module: {
        loaders: [
            { test: /\.js$/, exclude: /node_modules/, loader: 'callback' },
            ...
        ]
    },
    ...
    callbackLoader: {
        require: function() {
            return 'require("' + Array.prototype.join.call(arguments, ',') + '").default';
        }
    }
};

抱歉,但它不起作用。这会改变模块加载的方式,而不是导出。 - Quentin Roy

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