支持 ES5 模块中的 ES6 导入
Support for ES6 imports in ES5 module
对于我一年级的学生,我提供了一个使用 Revealing Module Pattern 编写的简单的基于 ES5 的库。这是 "main" module/namespace 的一个片段,它将包含其他扩展:
window.Library = (function ($) {
if (!$) {
alert("The Library is dependent on jQuery, which is not loaded!");
}
return {};
})(window.jQuery);
这适用于几乎 99.9% 的网络开发新手,并且没有将 ES6 与 Webpack 或 Babel 结合使用等奇特的东西。
0.1%现在要求我提供一个基于ES6的版本,可以正常导入。我很乐意提供这个,但我对如何最好地解决这个问题有点困惑。
我显然想保持 ES5 的方式,所以我的学生可以使用脚本标签包含文件并在他们喜欢的地方输入 Library.SomeExtension.aFunction();
。最重要的是,一些扩展依赖于 jQuery,它的注入方式与上面的代码片段类似。
我现在正在寻找一些可维护的方法来获得两全其美,使用一个代码库,jQuery 作为依赖项。我想给 99.9% 一个 window.Library
,而我想给 0.1% 一个使用 import Library from 'library'
.
的方法
我可以用一个 JS 文件同时完成这两项工作吗?或者我需要一个特殊的 ES6 版本(这不是问题)?最重要的是:我将如何重新调整我的代码(都类似于上面的代码片段)以支持这两种情况?
任何和所有的指针将不胜感激!
编辑:作为旁注,我已经有一个 gulpfile.js
可以通过 Babel
、minifiers
和其他东西运行这个库。所以必须扩展它来解决上述问题不是问题!
您可以使用 Rollup.js 将您的代码捆绑为 ES5 库以及随附的 ES6 模块。
Rollup 有 built-in support for Gulp 所以这样的东西是一个合理的起点(基于 Rollup 的 Gulp 示例):
const gulp = require('gulp');
const rollup = require('rollup');
const rollupTypescript = require('rollup-plugin-typescript');
gulp.task('bundle', () => {
return rollup.rollup({
input: './src/main.ts',
plugins: [
rollupTypescript()
]
}).then(bundle => {
return Promise.all([
// Build for ES5
bundle.write({
file: './dist/library.js',
format: 'umd',
name: 'Library',
sourcemap: true
}),
// Build for ES6 modules
bundle.write({
file: './dist/library.esm.js',
format: 'esm',
sourcemap: true
})
]);
});
});
您可以查看有关可用的不同输出格式的更多信息:https://rollupjs.org/guide/en/#outputformat
如果您的库源是使用 ES 模块编写的,然后 bundled/transpiled 到 ES5 包,这通常是最简单的。
它们是 Universal JS Loader Patten,它允许您以多种不同的方式导出代码。
;(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.YourModule = factory();
}
}(this, function () {
return {};
}));
经过几个晚上来回移动代码并安装如此多的 gulp 包后,我什至不记得我尝试过的所有包,我最终选择了一些我不太满意的东西。
首先,回答我自己的问题,@Bergi 在评论中也支持了这个问题:
Short answer: No, you cannot, in any way (right now), mix ES6 syntax with ES5 (modules) in a single file because the browser will error out on the use of export
.
Long answer: Technically you can set your script-element type to "module", which will make browsers accept the export
keyword. However, your code will simply run twice (once on load, and once after importing). If you can live with this, then the no becomes a yes, kinda.
那我最后做了什么?让我先声明一下,我真的很想在输出 ES5 文件中保留我拥有的 IIFE 模块设置(目前)。我将我的代码库更新为纯 ES6,并尝试了各种插件组合(包括@David Bradshaw 提到的 Rollup.js)。但我根本无法让它们工作,或者它们会以浏览器无法再加载模块的方式完全破坏输出,或者放弃 sourcemap 支持。随着学生们现在正在做的项目接近尾声,我已经为我0.1%的学生浪费了足够的业余时间。祝明年“更好”的解决方案好运。
我将我的 UMD 模式(如@Sly_cardinal 所述)基于 jQuery 并将该代码放入 Library.umd.js
。对于ES6部分,我做了一个Library.es6.js
,里面只有一个export default Library
。
在这两个文件中,我放置了一个多行注释 /*[Library.js]*/
,我想在其中注入连接的未缩小 Library.js.
我修改了我现有的 Gulp 任务以仅使用 concat
和 Babel
构建 Library.js
并将其存储在一个临时位置。我选择在此过程中牺牲源映射支持,因为串联代码在输出中完全可读。从技术上讲,源映射支持“有效”,但它仍然会过多地影响调试器。
我添加了一个额外的 Gulp 任务来读取临时 Library.js
的内容,然后对于第 1 步中的每个文件,我将查找并替换 multi - 对内容进行注释。保存生成的文件,然后将它们扔到最后的 concat
(例如,使用 jQuery 捆绑)、terser
和源映射生成。
最终结果是两个文件:
- 一个 UMD/ES5-browser 支持
- 一个支持 ES6
虽然我对结果很满意,但我不喜欢 Gulp 任务链和过程前半部分失去源映射支持。如前所述,我已经尝试了许多 gulp 插件来实现相同的效果(例如 gulp-inject
),但它们要么无法正常工作,要么对 sourcemap 做了奇怪的事情(即使他们声称支持一下)。
为了接下来的努力,我可能会研究与 Gulp 不同的东西,或者创建我自己的插件来执行上述操作,但正确 :P
对于我一年级的学生,我提供了一个使用 Revealing Module Pattern 编写的简单的基于 ES5 的库。这是 "main" module/namespace 的一个片段,它将包含其他扩展:
window.Library = (function ($) {
if (!$) {
alert("The Library is dependent on jQuery, which is not loaded!");
}
return {};
})(window.jQuery);
这适用于几乎 99.9% 的网络开发新手,并且没有将 ES6 与 Webpack 或 Babel 结合使用等奇特的东西。
0.1%现在要求我提供一个基于ES6的版本,可以正常导入。我很乐意提供这个,但我对如何最好地解决这个问题有点困惑。
我显然想保持 ES5 的方式,所以我的学生可以使用脚本标签包含文件并在他们喜欢的地方输入 Library.SomeExtension.aFunction();
。最重要的是,一些扩展依赖于 jQuery,它的注入方式与上面的代码片段类似。
我现在正在寻找一些可维护的方法来获得两全其美,使用一个代码库,jQuery 作为依赖项。我想给 99.9% 一个 window.Library
,而我想给 0.1% 一个使用 import Library from 'library'
.
我可以用一个 JS 文件同时完成这两项工作吗?或者我需要一个特殊的 ES6 版本(这不是问题)?最重要的是:我将如何重新调整我的代码(都类似于上面的代码片段)以支持这两种情况?
任何和所有的指针将不胜感激!
编辑:作为旁注,我已经有一个 gulpfile.js
可以通过 Babel
、minifiers
和其他东西运行这个库。所以必须扩展它来解决上述问题不是问题!
您可以使用 Rollup.js 将您的代码捆绑为 ES5 库以及随附的 ES6 模块。
Rollup 有 built-in support for Gulp 所以这样的东西是一个合理的起点(基于 Rollup 的 Gulp 示例):
const gulp = require('gulp');
const rollup = require('rollup');
const rollupTypescript = require('rollup-plugin-typescript');
gulp.task('bundle', () => {
return rollup.rollup({
input: './src/main.ts',
plugins: [
rollupTypescript()
]
}).then(bundle => {
return Promise.all([
// Build for ES5
bundle.write({
file: './dist/library.js',
format: 'umd',
name: 'Library',
sourcemap: true
}),
// Build for ES6 modules
bundle.write({
file: './dist/library.esm.js',
format: 'esm',
sourcemap: true
})
]);
});
});
您可以查看有关可用的不同输出格式的更多信息:https://rollupjs.org/guide/en/#outputformat
如果您的库源是使用 ES 模块编写的,然后 bundled/transpiled 到 ES5 包,这通常是最简单的。
它们是 Universal JS Loader Patten,它允许您以多种不同的方式导出代码。
;(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.YourModule = factory();
}
}(this, function () {
return {};
}));
经过几个晚上来回移动代码并安装如此多的 gulp 包后,我什至不记得我尝试过的所有包,我最终选择了一些我不太满意的东西。
首先,回答我自己的问题,@Bergi 在评论中也支持了这个问题:
Short answer: No, you cannot, in any way (right now), mix ES6 syntax with ES5 (modules) in a single file because the browser will error out on the use of
export
.Long answer: Technically you can set your script-element type to "module", which will make browsers accept the
export
keyword. However, your code will simply run twice (once on load, and once after importing). If you can live with this, then the no becomes a yes, kinda.
那我最后做了什么?让我先声明一下,我真的很想在输出 ES5 文件中保留我拥有的 IIFE 模块设置(目前)。我将我的代码库更新为纯 ES6,并尝试了各种插件组合(包括@David Bradshaw 提到的 Rollup.js)。但我根本无法让它们工作,或者它们会以浏览器无法再加载模块的方式完全破坏输出,或者放弃 sourcemap 支持。随着学生们现在正在做的项目接近尾声,我已经为我0.1%的学生浪费了足够的业余时间。祝明年“更好”的解决方案好运。
我将我的 UMD 模式(如@Sly_cardinal 所述)基于 jQuery 并将该代码放入
Library.umd.js
。对于ES6部分,我做了一个Library.es6.js
,里面只有一个export default Library
。在这两个文件中,我放置了一个多行注释
/*[Library.js]*/
,我想在其中注入连接的未缩小 Library.js.我修改了我现有的 Gulp 任务以仅使用
concat
和Babel
构建Library.js
并将其存储在一个临时位置。我选择在此过程中牺牲源映射支持,因为串联代码在输出中完全可读。从技术上讲,源映射支持“有效”,但它仍然会过多地影响调试器。我添加了一个额外的 Gulp 任务来读取临时
Library.js
的内容,然后对于第 1 步中的每个文件,我将查找并替换 multi - 对内容进行注释。保存生成的文件,然后将它们扔到最后的concat
(例如,使用 jQuery 捆绑)、terser
和源映射生成。
最终结果是两个文件:
- 一个 UMD/ES5-browser 支持
- 一个支持 ES6
虽然我对结果很满意,但我不喜欢 Gulp 任务链和过程前半部分失去源映射支持。如前所述,我已经尝试了许多 gulp 插件来实现相同的效果(例如 gulp-inject
),但它们要么无法正常工作,要么对 sourcemap 做了奇怪的事情(即使他们声称支持一下)。
为了接下来的努力,我可能会研究与 Gulp 不同的东西,或者创建我自己的插件来执行上述操作,但正确 :P