Eu preciso de require js quando uso o babel?

98

Estou experimentando com ES6, e estou usando gole para construir e babel para transpilar para ES5. A saída não está sendo executada no nó, apenas vinculado a um arquivo .htm com uma tag. Estou pensando que preciso adicionar

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

ou algo assim.

Estou tentando 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;
    } 

}

Erro é

Uncaught ReferenceError: require is not defined

Refere-se a isto (após .pipe (babel ()) em gulp)

var _shapes = require('shapes');
Jason
fonte
3
Sim, porque requirenão existe no navegador, você precisa usar alguma ferramenta de construção como Require.js, Browserify ou Webpack.
Jordan em execução em
1
Ahh, adicionar browserify à minha pesquisa no Google me deu a resposta, obrigado.
jason
10
FWIW, observe que a mensagem de erro não indica que você precisa de require.js. O Babel converte módulos em CommonJS por padrão, que é o que o Node usa e que define uma requirefunção (novamente, nada a ver com require.js). No entanto, você pode dizer ao Babel para converter módulos para outra coisa , por exemplo, AMD ou UMD, que funcionaria com o require.js. De qualquer forma, você precisa de um sistema para carregar módulos no navegador, porque o navegador não fornece um por padrão (ainda).
Felix Kling

Respostas:

136

Eu preciso de require js quando uso o babel?

Você pode precisar de algum carregador de módulo, mas não é necessário RequireJS. Você tem várias opções. O que segue irá ajudá-lo a começar.


rollup.js com rollup-plugin-babel

Rollup é um empacotador de módulo JavaScript de próxima geração. Ele entende os módulos ES2015 nativamente e produzirá um pacote que não precisa de nenhum carregador de módulo para operar. As exportações não utilizadas serão cortadas da saída, é chamado de agitação de árvore.

Agora, eu pessoalmente recomendo usar rollupjs, pois produz a saída mais clara e é fácil de configurar, no entanto, oferece um aspecto diferente para a resposta. Todas as outras abordagens fazem o seguinte:

  1. Compile o código ES6 com o babel, use o formato do módulo de sua escolha
  2. Concatene os módulos compilados junto com um carregador de módulo OU use um empacotador que atravessará as dependências para você.

Com rollupjs as coisas realmente não funcionam assim. Aqui, o rollup é a primeira etapa, em vez de babel. Ele só entende módulos ES6 por padrão. Você deve fornecer um módulo de entrada do qual as dependências serão percorridas e concatenadas. Como o ES6 permite várias exportações nomeadas em um módulo, rollupjs é inteligente o suficiente para retirar as exportações não utilizadas, reduzindo assim o tamanho do pacote. Infelizmente, o analisador rollupjs-s não entende a sintaxe ES6, então os módulos ES7 devem ser compilados antes que o rollup os analise, mas a compilação não deve afetar as importações ES6. Isso é feito usando o rollup-plugin-babelplugin com o babel-preset-es2015-rolluppreset (este preset é igual ao es2015, exceto o transformador do módulo e o plugin de auxiliares externos). Portanto, o rollup fará o seguinte com seus módulos, se configurados corretamente:

  1. Lê seu módulo ES6-7 do sistema de arquivos
  2. O plugin babel o compila para ES6 na memória
  3. rollup analisa o código ES6 para importações e exportações (usando o analisador acorn, compilado em rollup)
  4. ele atravessa todo o gráfico e cria um único pacote (que ainda pode ter dependências externas e as exportações da entrada podem ser exportadas, em um formato de sua escolha)

Exemplo de compilação 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));

Exemplo de construção do grunt com 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"]
        }
      }
    }
  });
}

Exemplo de gulp build com 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

O Babel tem um pacote bacana chamado babelify . Seu uso é simples e direto:

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

ou você pode usá-lo em 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"));

Isso irá transpilar e concatenar seu código de uma vez. O Browserify .bundleincluirá um pequeno carregador CommonJS agradável e organizará seus módulos transpilados em funções. Você pode até ter importações relativas.

Exemplo:

// 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, basta executar node build.jsna raiz do projeto.


Babel + WebPack

Compile todo o seu código usando o babel. Eu recomendo que você use o transformador do módulo amd (chamado babel-plugin-transform-es2015-modules-amdem babel 6). Depois disso, empacote suas fontes compiladas com WebPack.

O WebPack 2 foi lançado! Ele compreende módulos ES6 nativos e executa (ou melhor, simula) a agitação da árvore usando a eliminação de código morto embutida do babili . Por enquanto (setembro de 2016), eu ainda sugiro usar o rollup com o babel, embora minha opinião possa mudar com o primeiro lançamento do WebPack 2. Sinta-se à vontade para discutir suas opiniões nos comentários.


Pipeline de compilação personalizado

Às vezes, você deseja ter mais controle sobre o processo de compilação. Você pode implementar seu próprio pipeline desta forma:

Primeiro, você deve configurar o babel para usar os módulos amd. Por padrão, o babel transpila para módulos CommonJS, o que é um pouco complicado de manipular no navegador, embora o browserify consiga lidar com eles de uma maneira agradável.

  • Babel 5: { modules: 'amdStrict', ... }opção de uso
  • Babel 6: use o es2015-modules-amdplugin

Não se esqueça de ativar a moduleIds: trueopção.

Verifique o código transpilado para nomes de módulos gerados, muitas vezes há incompatibilidades entre os módulos definidos e necessários. Consulte sourceRoot e moduleRoot .

Finalmente, você deve ter algum tipo de carregador de módulo, mas não é necessário. Há almondjs , um pequeno calço que funciona bem. Você pode até implementar o seu próprio:

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

No final, você pode apenas concatenar o shim do carregador e os módulos compilados juntos e executar um uglify nisso.


O código padrão do Babel é duplicado em todos os módulos

Por padrão, a maioria dos métodos acima compila cada módulo com babel individualmente e, em seguida, concatena-os. É isso que o babelify também faz. Mas se você olhar o código compilado, verá que o babel insere vários clichês no início de cada arquivo, a maioria deles duplicados em todos os arquivos.

Para evitar isso, você pode usar o babel-plugin-transform-runtimeplugin.

Tamas Hegedus
fonte
1
Isso é tão completo; obrigado. Re: o boilerplate do Babel duplicado por arquivo - seria correto assumir que o gzip negaria isso?
iono de
1
Eu mesmo nunca fiz a medição, mas presumiria que alguém minimizaria o pacote antes da distribuição, e a minificação provavelmente encontrará nomes diferentes para locais, portanto, eles não serão exatamente os mesmos. O Gzip deve encontrar as partes comuns (resultando em uma boa taxa de compactação), mas o navegador ainda precisa analisá-las individualmente. Em última análise, não deve ser uma sobrecarga perceptível, mas haverá pessoas como eu que simplesmente não gostam de código duplicado.
Tamas Hegedus de
Muito justo, obrigado pela resposta. Provavelmente faria muito sentido, também, nos casos em que você tivesse que fazer backup ou rastrear o código de saída no controle de versão (onde o tamanho do arquivo descompactado se multiplica) ou onde você gostaria que a saída não fosse minimizada por qualquer motivo.
iono de
gulp-rollup pode ser um bom complemento para esta lista também
GGG
@GGG Adicionado exemplo de gulp. Infelizmente nenhum dos exemplos funciona no momento em janelas, veja a explicação no topo dos códigos.
Tamas Hegedus
8

barebones webpack 2

1) Se este for o seu diretório raiz:

index.html

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

scripts.js

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

shapes.js

export class Circle {
  ...
}

2) ter o nó instalado no

3) execute o seguinte comando em seu terminal:

$ npm install -g webpack

5) em seu diretório raiz, execute o seguinte:

$ webpack scripts.js bundle.js

Agora você deve ter um arquivo chamado bundle.js em seu diretório raiz, que será o arquivo que seu index.html consumirá. Este é um recurso de empacotamento minimalista do webpack. Você pode aprender mais aqui

Isaac Pak
fonte
4

requirenão existe no navegador, portanto, esse erro é esperado. Você precisa usar algo como require.js ou Browserify.

Djechlin
fonte