Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
173 views
in Technique[技术] by (71.8m points)

javascript - Do I need require js when I use babel?

Im experimenting with ES6, and Im using gulp to build and babel to transpile to ES5. The output is not being run in node, just linked to from a .htm file with a tag. Im thinking I need to add

<script src='require.js'></script>

or something like that.

Im trying to import / export.

////////////////scripts.js
import {Circle} from 'shapes';

c = new Circle(4);

console.log(c.area());


/////////////////shapes.js
export class Circle {

    circle(radius) {
        this.radius = radius;
    }

    area() {
        return this.radius * this.radius * Math.PI;
    } 

}

Error is

Uncaught ReferenceError: require is not defined

Refers to this (after .pipe(babel()) in gulp)

var _shapes = require('shapes');
Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Do I need require js when I use babel?

You might need some module loader, but it is not necessary RequireJS. You have several options. The following will help you to get started.


rollup.js with rollup-plugin-babel

Rollup is a next-generation JavaScript module bundler. It understands ES2015 modules natively, and will produce a bundle that doesn't need any module loader to operate. Unused exports will be trimmed from the output, it's called tree-shaking.

Now I personally recommend using rollupjs, as it produces the clearest output, and is easy to setup, however, it gives a different aspect to the answer. All the other approaches do the following:

  1. Compile the ES6 code with babel, use the module format of your choice
  2. Concatenate the compiled modules alongside with a module loader OR use a bundler that will traverse the dependencies for you.

With rollupjs things doesn't really work this way. Here, rollup is the first step, instead of babel. It only understands ES6 modules by default. You must give an entry module of which the dependencies will be traversed and concatenated. As ES6 allows multiple named exports in a module, rollupjs is smart enough to strip unused exports, thus shrinking bundle size. Unfortunately rollupjs-s parser doesn't understand >ES6 syntax, so ES7 modules have to be compiled before rollup parses them, but the compilation should not affect the ES6 imports. It is done by using the rollup-plugin-babel plugin with the babel-preset-es2015-rollup preset (this preset is the same as the es2015 one, except the module transformer and the external-helpers plugin). So rollup will do the following with your modules if correctly set up:

  1. Reads your ES6-7 module from the filesystem
  2. The babel plugin compiles it to ES6 in memory
  3. rollup parses the ES6 code for imports and exports (using acorn parser, compiled into rollup)
  4. it traverses the whole graph, and creates a single bundle (which still might have external dependencies, and the entry's exports might be exported, in a format of your choice)

Example nodejs build:

// setup by `npm i rollup rollup-plugin-babel babel-preset-es2015 babel-plugin-external-helpers --save-dev`

// build.js:
require("rollup").rollup({
  entry: "./src/main.js",
  plugins: [
    require("rollup-plugin-babel")({
      "presets": [["es2015", { "modules": false }]],
      "plugins": ["external-helpers"]
    })
  ]
}).then(bundle => {
  var result = bundle.generate({
    // output format - 'amd', 'cjs', 'es6', 'iife', 'umd'
    format: 'iife'
  });

  require("fs").writeFileSync("./dist/bundle.js", result.code);
  // sourceMaps are supported too!
}).then(null, err => console.error(err));

Example grunt build with grunt-rollup

// setup by `npm i grunt grunt-rollup rollup-plugin-babel babel-preset-es2015 babel-plugin-external-helpers --save-dev`

// gruntfile.js
module.exports = function(grunt) {
  grunt.loadNpmTasks("grunt-rollup");
  grunt.initConfig({
    "rollup": {
      "options": {
        "format": "iife",
        "plugins": [
          require("rollup-plugin-babel")({
            "presets": [["es2015", { "modules": false }]],
            "plugins": ["external-helpers"]
          })
        ]
      },
      "dist": {
        "files": {
          "./dist/bundle.js": ["./src/main.js"]
        }
      }
    }
  });
}

Example gulp build with gulp-rollup

// setup by `npm i gulp gulp-rollup rollup-plugin-babel babel-preset-es2015 babel-plugin-external-helpers --save-dev`

// gulpfile.js
var gulp       = require('gulp'),
    rollup     = require('gulp-rollup');

gulp.task('bundle', function() {
  gulp.src('./src/**/*.js')
    // transform the files here.
    .pipe(rollup({
      // any option supported by Rollup can be set here.
      "format": "iife",
      "plugins": [
        require("rollup-plugin-babel")({
          "presets": [["es2015", { "modules": false }]],
          "plugins": ["external-helpers"]
        })
      ],
      entry: './src/main.js'
    }))
    .pipe(gulp.dest('./dist'));
});

Babelify + Browserify

Babel has a neat package called babelify. It's usage is simple and straightforward:

$ npm install --save-dev babelify babel-preset-es2015 babel-preset-react
$ npm install -g browserify
$ browserify src/script.js -o bundle.js 
  -t [ babelify --presets [ es2015 react ] ]

or you can use it from node.js:

$ npm install --save-dev browserify babelify babel-preset-es2015 babel-preset-react

...

var fs = require("fs");
var browserify = require("browserify");
browserify(["./src/script.js"])
  .transform("babelify", {presets: ["es2015", "react"]})
  .bundle()
  .pipe(fs.createWriteStream("bundle.js"));

This will transpile and concatenate your code at once. Browserify's .bundle will include a nice little CommonJS loader, and will organize your transpiled modules into functions. You can even have relative imports.

Example:

// project structure
.
+-- src/
|   +-- library/
|   |   -- ModuleA.js
|   +-- config.js
|   -- script.js
+-- dist/
-- build.js
...

// build.js
var fs = require("fs");
var browserify = require("browserify");
browserify(["./src/script.js"])
  .transform("babelify", {presets: ["es2015", "react"]})
  .bundle()
  .pipe(fs.createWriteStream("dist/bundle.js"));

// config.js
export default "Some config";

// ModuleA.js
import config from '../config';
export default "Some nice export: " + config;

// script.js
import ModuleA from './library/ModuleA';
console.log(ModuleA);

To compile just run node build.js in your project root.


Babel + WebPack

Compile all your code using babel. I recommend you to use the amd module transformer (called babel-plugin-transform-es2015-modules-amd in babel 6). After that bundle your compiled sources with WebPack.

WebPack 2 is out! It understands native ES6 modules, and will perform (or rather simulate) tree shaking using babili-s builtin dead code elimination. For now (September 2016) I would still suggest to use rollup with babel, although my opinion might change with the first release of WebPack 2. Feel free to discuss your opinions in the comments.


Custom compilation pipeline

Sometimes you want to have more control over the compilation process. You can implement your own pipeline like this:

First, you have to configure babel to use amd modules. By default babel transpiles to CommonJS modules, which is a little complicated to handle in the browser, although browserify manages to handle them in a nice way.

  • Babel 5: use { modules: 'amdStrict', ... } option
  • Babel 6: use the es2015-modules-amd plugin

Don't forget to turn on the moduleIds: true option.

Check the transpiled code for generated modul names, there are often mismatches between defined and required modules. See sourceRoot and moduleRoot.

Finally, you have to have some kind of module loader, but it isn't necessairy requirejs. There is almondjs, a tiny require shim that works well. You can even implement your own:

var __modules = new Map();

function define(name, deps, factory) {
    __modules.set(name, { n: name, d: deps, e: null, f: factory });
}

function require(name) {
    const module = __modules.get(name);
    if (!module.e) {
        module.e = {};
        module.f.apply(null, module.d.map(req));
    }
    return module.e;

    function req(name) {
        return name === 'exports' ? module.e : require(name);
    }
}

At the end, you can just concatenate the loader shim and the compiled modules together, and running an uglify on that.


Babel's boilerplate code is duplicated in every module

By default, most of the above methods compile each module with babel individually, and then concatenate them together. That's what babelify does too. But if you look at the compiled code, you see that babel inserts lots of boilerplate at the beginning of each file, most of them are duplicated across all files.

To prevent this you can use the babel-plugin-transform-runtime plugin.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...