¿Necesito require js cuando uso babel?

98

Estoy experimentando con ES6, y estoy usando gulp para construir y babel para transpilar a ES5. La salida no se ejecuta en el nodo, solo se vincula desde un archivo .htm con una etiqueta. Estoy pensando que necesito agregar

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

o algo así.

Estoy intentando importar / exportar.

////////////////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;
    } 

}

El error es

Uncaught ReferenceError: require is not defined

Se refiere a esto (después de .pipe (babel ()) en gulp)

var _shapes = require('shapes');
jason
fuente
3
Sí, debido a requireque no existe en el navegador, debe utilizar alguna herramienta de compilación como Require.js, Browserify o Webpack.
Jordan Running
1
Ahh, agregar browserify a mi búsqueda en Google me dio la respuesta, gracias.
Jason
10
FWIW, tenga en cuenta que el mensaje de error no indica que necesita require.js. Babel convierte módulos a CommonJS de forma predeterminada, que es lo que usa Node y que define una requirefunción (de nuevo, nada que ver con require.js). Sin embargo, puede decirle a Babel que convierta los módulos en otra cosa , por ejemplo, AMD o UMD, que luego funcionaría con require.js. De cualquier manera, necesita un sistema para cargar módulos en el navegador, porque el navegador no proporciona uno por defecto (todavía).
Felix Kling

Respuestas:

136

¿Necesito require js cuando uso babel?

Puede que necesite algún cargador de módulos, pero no es necesario RequireJS. Tienes varias opciones. Lo siguiente le ayudará a comenzar.


rollup.js con rollup-plugin-babel

Rollup es un paquete de módulos JavaScript de próxima generación. Entiende los módulos ES2015 de forma nativa y producirá un paquete que no necesita ningún cargador de módulos para funcionar. Las exportaciones no utilizadas se recortarán de la salida, se llama agitación de árboles.

Ahora personalmente recomiendo usar rollupjs, ya que produce la salida más clara y es fácil de configurar, sin embargo, le da un aspecto diferente a la respuesta. Todos los demás enfoques hacen lo siguiente:

  1. Compile el código ES6 con babel, use el formato de módulo de su elección
  2. Concatenar los módulos compilados junto con un cargador de módulos O utilice un paquete que recorra las dependencias por usted.

Con rollupjs, las cosas realmente no funcionan de esta manera. Aquí, el rollup es el primer paso, en lugar de babel. Solo comprende los módulos ES6 por defecto. Debe proporcionar un módulo de entrada del cual se atravesarán y concatenarán las dependencias. Como ES6 permite múltiples exportaciones con nombre en un módulo, rollupjs es lo suficientemente inteligente como para eliminar las exportaciones no utilizadas, reduciendo así el tamaño del paquete. Desafortunadamente, el analizador rollupjs-s no comprende la sintaxis de ES6, por lo que los módulos de ES7 deben compilarse antes de que el rollup los analice, pero la compilación no debería afectar las importaciones de ES6. Se hace usando el rollup-plugin-babelcomplemento con el babel-preset-es2015-rolluppreset (este preset es el mismo que el es2015, excepto el módulo transformer y el plugin external-helpers). Así que el rollup hará lo siguiente con sus módulos si está configurado correctamente:

  1. Lee su módulo ES6-7 del sistema de archivos
  2. El complemento de babel lo compila en ES6 en la memoria
  3. rollup analiza el código ES6 para importaciones y exportaciones (usando el analizador acorn, compilado en rollup)
  4. atraviesa todo el gráfico y crea un solo paquete (que aún puede tener dependencias externas, y las exportaciones de la entrada pueden exportarse, en un formato de su elección)

Ejemplo de compilación de nodejs:

// 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));

Ejemplo de construcción de grunt con 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"]
        }
      }
    }
  });
}

Ejemplo de compilación de gulp con 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 tiene un paquete ordenado llamado babelify . Su uso es simple y directo:

$ 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 ] ]

o puede usarlo desde 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"));

Esto transpilará y concatenará su código a la vez. Browserify's .bundleincluirá un pequeño cargador CommonJS y organizará sus módulos transpilados en funciones. Incluso puede tener importaciones relativas.

Ejemplo:

// 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);

Para compilar, simplemente ejecute node build.jsen la raíz de su proyecto.


Babel + WebPack

Compila todo tu código usando babel. Te recomiendo que uses el transformador de módulo amd (llamado babel-plugin-transform-es2015-modules-amden babel 6). Después de eso, combine sus fuentes compiladas con WebPack.

¡WebPack 2 ya está disponible! Entiende los módulos nativos de ES6 y realizará (o más bien simulará) la agitación de árboles utilizando la eliminación de código muerto incorporada de babili -s. Por ahora (septiembre de 2016), todavía sugeriría usar rollup con babel, aunque mi opinión podría cambiar con la primera versión de WebPack 2. No dude en comentar sus opiniones en los comentarios.


Canalización de compilación personalizada

A veces, desea tener más control sobre el proceso de compilación. Puede implementar su propia canalización de esta manera:

Primero, debe configurar babel para usar módulos amd. De forma predeterminada, babel se transpila a módulos CommonJS, lo cual es un poco complicado de manejar en el navegador, aunque browserify se las arregla para manejarlos de una manera agradable.

  • Babel 5: { modules: 'amdStrict', ... }opción de uso
  • Babel 6: usa el es2015-modules-amdcomplemento

No olvide activar la moduleIds: trueopción.

Compruebe el código transpilado para ver los nombres de los módulos generados, a menudo hay discrepancias entre los módulos definidos y requeridos. Consulte sourceRoot y moduleRoot .

Finalmente, debe tener algún tipo de cargador de módulos, pero no es necesario requirejs. Hay almondjs , una pequeña calza que funciona bien. Incluso puedes implementar el tuyo propio:

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);
    }
}

Al final, puede concatenar el calce del cargador y los módulos compilados juntos, y ejecutar un uglify en eso.


El código repetitivo de Babel está duplicado en cada módulo

De forma predeterminada, la mayoría de los métodos anteriores compilan cada módulo con babel individualmente y luego los concatenan juntos. Eso es lo que hace también babelify. Pero si observa el código compilado, verá que babel inserta una gran cantidad de texto estándar al principio de cada archivo, la mayoría de ellos están duplicados en todos los archivos.

Para evitar esto, puede utilizar el babel-plugin-transform-runtimecomplemento.

Tamas Hegedus
fuente
1
Esto es tan jodidamente minucioso; gracias. Re: el duplicado de Babel repetitivo por archivo - ¿sería correcto asumir que gzip lo negaría?
iono
1
Nunca lo medí yo mismo, pero supongo que uno minificaría el paquete antes de la distribución, y la minificación probablemente encontrará diferentes nombres para los locales, por lo que no serán exactamente iguales. Gzip debería encontrar las partes comunes (lo que da como resultado una buena relación de compresión), pero el navegador aún tiene que analizarlas individualmente. En última instancia, no debería ser una sobrecarga notable, pero habrá personas como yo a las que simplemente no les gusta el código duplicado.
Tamas Hegedus
Muy bien, gracias por la respuesta. Probablemente también tenga mucho sentido en los casos en que tenga que hacer una copia de seguridad o rastrear el código de salida en el control de versiones (donde el tamaño del archivo sin comprimir se multiplica) o donde le gustaría que la salida no se redujera por cualquier motivo.
iono
gulp-rollup también podría ser una buena adición a esta lista
GGG
@GGG Se agregó un ejemplo de gulp. Desafortunadamente, ninguno de los ejemplos funciona en este momento en Windows, vea la explicación en la parte superior de los códigos.
Tamas Hegedus
8

paquete web barebones 2

1) Si este es su directorio raíz:

index.html

<html>
  ...
  <script src="./bundle.js"></script>
  ...
</html>

scripts.js

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

shape.js

export class Circle {
  ...
}

2) se han instalado nodo nodo

3) ejecuta el siguiente comando en tu terminal:

$ npm install -g webpack

5) en su directorio raíz ejecute lo siguiente:

$ webpack scripts.js bundle.js

Ahora debería tener un archivo llamado bundle.js en su directorio raíz, que será el archivo que consumirá su index.html. Esta es una característica de agrupación minimalista de webpack. Puedes aprender más aquí

Isaac Pak
fuente
4

requireno existe en el navegador, por lo que se espera este error. Necesita usar algo como require.js o Browserify.

djechlin
fuente