Gulp - 在合并的 js 文件中使用 ES6 模块?

Gulp - using ES6 modules in a combined js file?

我正在尝试在当前 GULP 设置中使用 ES6 模块。我读到浏览器或 Babel 尚不支持此功能,因此需要对 make this work 进行一些精心设置,使用诸如 Browserifybabelify、[=16= 之类的东西]. (似乎非常复杂的设置)。

我想要的和网上找的例子不一样。所有的例子都是导入外部文件,我真的不想要那样。我希望将所有文件捆绑到一个 单个文件 中,其中包含所有模块。这是我拥有的:

我当前的 GULP 设置是这样的:

gulp.task('buildJS', function() {
    var src = [
        './js/dist/app.js',
        './js/dist/templates.js',
        './js/dist/connect.js',
        './js/dist/config.js',
        './js/dist/utilities.js',
        './js/dist/components/*.js',
        './js/dist/pages/**/*.js',
        './js/dist/modals/*.js',
        './js/dist/init.js' // must be last
    ];

    gulp.src(src)
        .pipe(concat('app.js'))
        .pipe(babel({modules:"common"})) // I have no idea what "modules" actually does
        .pipe(gulp.dest('../js/'))

});

这是 /js/dist/components/ 中的组件文件示例。
像这样的文件有很多,全部合并成一个文件。

module "components/foo" {
    export function render(settings) {
        return ...
    }
}

所以稍后我会在某些页面控制器中使用它:

import { render } from "components/foo";

问题:

现在我只有一个文件(已使用 Babel 转换),我如何通过 Import 使用模块?

不,不要天真地连接文件。使用 browserify 来捆绑它们,使用 babelify 来编译它们(通过 babel)。一个基本示例如下所示:

browserify('./entry')
  .transform(babelify)
  .bundle()
  // ...

很难给出更具体的建议,因为您的用例太不明确了。您是否有一个从一个文件开始的依赖关系图,或者您是否试图将一堆独立的模块捆绑在一起?您是要 运行 脚本来启动应用程序,还是只想能够单独访问模块?

根据您在评论中链接到的示例,您应该有这样的内容:

components/doughnut.js

export default function Doughnut (settings = {}) {
  // ...
};

Doughnut.prototype = {}

routes/home.js

import Doughnut from './components/doughnut';
export default function () {
  var component = new Doughnut();
  $('body').html(component.render());
};

让每个模块导出您希望从任何其他模块获得的内容。让每个模块从任何其他模块导入任何它需要的东西。无论使用此示例中的控制器,都应该执行 import home from './routes/home';这些模块未绑定到全局变量 App,可以在其他应用程序中重复使用(只要您以其他方式使它们可重复使用)。

.pipe(babel({modules:"common"})) // I have no idea what "modules" 

modules 是一个 babel option ,它决定了它把 ES6 模块语法编译成什么模块格式。在这种情况下,CommonJS.

module "components/foo" {

感谢您的评论,我现在明白您为什么会有这个了。你需要消除它。您的组件文件应该类似于:

export function render (settings) {
    return ...
}

配对:

import { render } from "components/foo";

或者如果您想要默认导出/导入:

export default function render (settings) {
    return ...
}
import render from "components/foo";
import { render } from "components/foo";

如果你正在浏览你的模块,你可能需要使用像 ./components/foo 这样的相对路径或者使用其他东西来处理路径,比如 babel 的 resolveModuleSource 选项。

自 2015 年底以来,我一直在使用 rollupjs 来创建一组 ES2015 (ES6) 模块,因此我可以使用 import/export 在我的代码中自由使用。


I've found Rollupjs to be very good and easy to use. The people behind it are great people which devote themselves to the project. I've had many questions which I had posted on the project's Github issues page and I always got answered pretty quickly.


安装程序包括这些 rollupjs 插件:

  1. rollup(基本的 rollupjs 打包器)
  2. rollup-plugin-babel(将 ES2015 代码转换为 ES5 或更早版本,以支持旧版浏览器)
  3. rollup-plugin-eslint(验证javascript代码是否有效)
  4. rollup-plugin-uglify(缩小代码,使其更小)
  5. rollup-plugin-progress(在终端中显示捆绑进度。显示正在“处理”哪个文件)
  6. beepbeep(使控制台发出哔哔声。我用它来通知我编译错误)

简化的GULP设置我正在使用:

var gulp               = require('gulp'),
    gutil              = require('gulp-util'),
    rollup             = require('rollup').rollup,
    babelRollup        = require('rollup-plugin-babel'),
    eslintRollup       = require('rollup-plugin-eslint'),
    uglifyRollup       = require('rollup-plugin-uglify'),
    rollupProgress     = require('rollup-plugin-progress'),
    beep               = require('beepbeep');

// ESlint 
var eslint_settings = {
    rulePaths: [],
    rules: {
        "no-mixed-spaces-and-tabs" : [2, "smart-tabs"],
        "block-spacing"            : [2, "always"],
        "comma-style"              : [2, "last"],
        "no-debugger"              : [1],
        "no-alert"                 : [2],
        "indent-legacy"            : [1, 4, {"SwitchCase":1}],
        'strict'                   : 0,
        'no-undef'                 : 1
    },
    ecmaFeatures : {
        modules: true,
        sourceType: "module"
    },
    "parserOptions": {
        "ecmaVersion" : 6,
        "sourceType": "module",
        "ecmaFeatures": {
            "jsx": false,
            "experimentalObjectRestSpread": true
        }
    },
    globals : ['$', '_', 'afterEach', 'assert', 'beforeEach', 'Cookies', 'd3', 'dataLayer', 'describe', 'done', 'expect', 'ga', 'it', 'jQuery', 'sinon'], baseConfig: {
        //parser: 'babel-eslint',
    },
    envs: [
        'browser', 'es6'
    ]
};


// Rollup plugins configuration
function getRollupPlugins( settings = {} ){
    var rollupPlugins = [];

    rollupPlugins.push({
        presets        : [['es2015', {"modules": false}]], //['es2015-rollup'],
        runtimeHelpers : true,
        exclude        : 'node_modules/**',
        plugins        : ["external-helpers"]
    });

    rollupPlugins.push(eslintRollup( Object.assign({throwOnError:true}, eslint_settings) ))

    rollupPlugins.push(rollupProgress({
         clearLine:true // default: true
    }))

    // I would advise Babel to only be used for production output since it greatly slower bundle creation
    if( settings.ENV == 'production' ){
        rollupPlugins.push(uglifyRollup())
        rollupPlugins.push(babelRollup(rollupPlugins__babel));
    }

    return rollupPlugins;
}

var rollupPlugins = getRollupPlugins();

/**
 * a generic Rollup bundle creator
 * @param  {String} outputPath     [where to save the bundle to (must end with /)]
 * @param  {String} outputFileName [bundle file name]
 * @param  {String} entryFile      [rollup entry file to start scanning from]
 * @return {Object}                [Promise]
 */
function rollupBundle(outputPath, outputFileName, entryFile, bundleOptions){
    bundleOptions = bundleOptions || {};
    bundleOptions.plugins = bundleOptions.plugins || rollupPlugins;

    return new Promise(function(resolve, reject) {
        outputFileName += '.js';
        var cache;

        // fs.truncate(outputPath + outputFileName, 0, function() {
        //     gutil.log( gutil.colors.dim.gray('Emptied: '+ outputPath + outputFileName) );
        // });

        rollup({
            entry   : entryFile,
            plugins : bundleOptions.plugins,
            cache   : cache
        })
        .then(function (bundle) {
            var bundleSettings = {
                    format    : bundleOptions.format || 'umd',
                    sourceMap : false,
                    banner    : config.banner
                },
                result = bundle.generate(bundleSettings),
                mapFileName = outputFileName + '.map',
                sourceMappingURL = '\n//# sourceMappingURL='+ mapFileName;

            cache = bundle;

            // if folder does not exists, create it
            if( !fs.existsSync(outputPath) ){
                gutil.log( gutil.colors.black.bgWhite('Creating directory ' + outputPath) );
                fs.mkdirSync(outputPath);
            }

            // save bundle file to disk
            fs.writeFile( outputPath + outputFileName, result.code + (bundleSettings.sourceMap ? sourceMappingURL : ''), function(){
                resolve();
            });

            // save map file to disk
            if( bundleSettings.sourceMap )
                fs.writeFile( outputPath + mapFileName, result.map.toString());
        })
        .catch(function(err){
            beep(1);
            gutil.log( gutil.colors.white.bgRed('Rollup [catch]: ', err.stack) );
            resolve();
        })
    });
}

// This task bundles the main application, using an entry file which itself has many imports, 
// and those imports also has imports.. like a tree branching
gulp.task('bundle-app', ()=>{
    return rollupBundle('../dist/js/', 'app', 'js/dist/app.js', {format:'cjs'});
});