Czy potrzebuję wymagania js, gdy używam babel?

98

Eksperymentuję z ES6 i używam łyka do budowania i babel do transpozycji do ES5. Dane wyjściowe nie są uruchamiane w węźle, tylko są połączone z plikiem .htm ze znacznikiem. Myślę, że muszę dodać

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

czy jakoś tak.

Próbuję importować / eksportować.

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

}

Błąd jest

Uncaught ReferenceError: require is not defined

Odnosi się do tego (po .pipe (babel ()) łykiem)

var _shapes = require('shapes');
Jason
źródło
3
Tak, ponieważ requirenie istnieje w przeglądarce, musisz użyć jakiegoś narzędzia do budowania, takiego jak Require.js, Browserify lub Webpack.
Jordan Bieganie
1
Ach, dodanie browserify do mojego googlowania dało mi odpowiedź, dziękuję.
jason
10
FWIW, zwróć uwagę, że komunikat o błędzie nie oznacza, że ​​potrzebujesz require.js. Babel domyślnie konwertuje moduły na CommonJS, czego używa Node i który definiuje requirefunkcję (znowu nie ma to nic wspólnego z require.js). Jednak można powiedzieć Babel do konwersji modułów do czegoś innego , np AMD lub UMD, które następnie pracować z require.js. Tak czy inaczej, potrzebujesz systemu do ładowania modułów w przeglądarce, ponieważ przeglądarka domyślnie (jeszcze) takiego nie udostępnia.
Felix Kling

Odpowiedzi:

136

Czy potrzebuję wymagania js, gdy używam babel?

Możesz potrzebować modułu ładującego, ale nie jest to konieczne. Masz kilka opcji. Poniższe informacje pomogą Ci rozpocząć.


rollup.js z rollup-plugin-babel

Rollup to pakiet modułów JavaScript nowej generacji. Rozumie natywnie moduły ES2015 i stworzy pakiet, który nie wymaga żadnego modułu ładującego do działania. Niewykorzystany eksport zostanie usunięty z wyniku, co nazywa się potrząsaniem drzewami.

Teraz osobiście polecam korzystanie z pakietu rollupjs, ponieważ zapewnia on najwyraźniejszy wynik i jest łatwy w konfiguracji, jednak daje inny aspekt odpowiedzi. Wszystkie inne podejścia wykonują następujące czynności:

  1. Skompiluj kod ES6 za pomocą babel, użyj wybranego formatu modułu
  2. Połącz skompilowane moduły razem z programem ładującym moduły LUB użyj pakietu, który przejdzie za Ciebie przez zależności.

W przypadku rollupjs rzeczy tak naprawdę nie działają w ten sposób. Tutaj rollup jest pierwszym krokiem zamiast babel. Domyślnie rozumie tylko moduły ES6. Musisz podać moduł wejściowy, którego zależności będą przeszukiwane i łączone. Ponieważ ES6 pozwala na wiele nazwanych eksportów w module, rollupjs jest wystarczająco inteligentny, aby usunąć nieużywane eksporty, zmniejszając w ten sposób rozmiar pakietu. Niestety, parser rollupjs-s nie rozumie składni> ES6, więc moduły ES7 muszą zostać skompilowane przed ich przeanalizowaniem przez pakiet zbiorczy, ale kompilacja nie powinna wpływać na import ES6. Odbywa się to za pomocą rollup-plugin-babelwtyczki z babel-preset-es2015-rollupustawieniem wstępnym (to ustawienie jest takie samo jak w przypadku es2015, z wyjątkiem transformatora modułu i wtyczki pomocników zewnętrznych). Tak więc pakiet zbiorczy wykona następujące czynności z modułami, jeśli są poprawnie skonfigurowane:

  1. Odczytuje moduł ES6-7 z systemu plików
  2. Wtyczka babel kompiluje go do ES6 w pamięci
  3. pakiet zbiorczy analizuje kod ES6 pod kątem importu i eksportu (przy użyciu parsera żołędzi, skompilowany do pakietu zbiorczego)
  4. przechodzi przez cały wykres i tworzy pojedynczy pakiet (który nadal może mieć zewnętrzne zależności, a eksporty wpisu mogą być eksportowane w wybranym przez Ciebie formacie)

Przykładowa kompilacja 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));

Przykładowa kompilacja gruntowa z grunt-rollupem

// 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"]
        }
      }
    }
  });
}

Przykładowa kompilacja łyka z rollupem gulp

// 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 ma zgrabny pakiet o nazwie babelify . Jego użycie jest proste i nieskomplikowane:

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

lub możesz go użyć z 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"));

Spowoduje to natychmiastową transpozycję i konkatenację kodu. Browserify .bundlebędzie zawierał ładny, mały program ładujący CommonJS i zorganizuje transpiled moduły w funkcje. Możesz nawet mieć względny import.

Przykład:

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

Aby skompilować, po prostu uruchom node build.jsw katalogu głównym projektu.


Babel + WebPack

Skompiluj cały kod za pomocą babel. Zalecam użycie transformatora modułu amd (nazywanego babel-plugin-transform-es2015-modules-amdw babel 6). Następnie spakuj swoje skompilowane źródła za pomocą WebPack.

WebPack 2 jest już dostępny! Rozumie natywne moduły ES6 i wykona (a raczej zasymuluje) potrząsanie drzewami przy użyciu wbudowanej eliminacji martwego kodu Babili . Na razie (wrzesień 2016) nadal sugerowałbym używanie rollupa z babel, chociaż moja opinia może się zmienić wraz z pierwszym wydaniem WebPack 2. Zapraszam do dyskusji w komentarzach.


Niestandardowy potok kompilacji

Czasami chcesz mieć większą kontrolę nad procesem kompilacji. Możesz zaimplementować swój własny potok w następujący sposób:

Najpierw musisz skonfigurować babel do używania modułów amd. Domyślnie babel transponuje się do modułów CommonJS, co jest trochę skomplikowane w obsłudze w przeglądarce, chociaż browserify radzi sobie z nimi w przyjemny sposób.

  • Babel 5: użyj { modules: 'amdStrict', ... }opcji
  • Babel 6: użyj es2015-modules-amdwtyczki

Nie zapomnij włączyć tej moduleIds: trueopcji.

Sprawdź transpiled kod pod kątem wygenerowanych nazw modułów, często występują niezgodności między zdefiniowanymi i wymaganymi modułami. Zobacz sourceRoot i moduleRoot .

Wreszcie, musisz mieć jakiś rodzaj modułu ładującego, ale nie jest to konieczne. Jest migdał , niewielka podkładka, która działa dobrze. Możesz nawet wdrożyć własne:

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

Na koniec możesz po prostu połączyć podkładkę modułu ładującego i skompilowane moduły razem i uruchomić na tym uglify.


Standardowy kod Babel jest powielany w każdym module

Domyślnie większość z powyższych metod kompiluje każdy moduł indywidualnie z babel, a następnie łączy je razem. To też robi babelify. Ale jeśli spojrzysz na skompilowany kod, zobaczysz, że babel wstawia wiele standardowych szablonów na początku każdego pliku, większość z nich jest powielana we wszystkich plikach.

Aby temu zapobiec, możesz skorzystać z babel-plugin-transform-runtimewtyczki.

Tamas Hegedus
źródło
1
To jest tak cholernie dokładne; Dziękuję Ci. Re: duplikat schematu Babel na plik - czy poprawne byłoby założenie, że program gzip mógłby to całkowicie zanegować?
iono
1
Nigdy sam tego nie mierzyłem, ale założyłem, że przed dystrybucją zminimalizowałbym pakiet, a minifikacja prawdopodobnie znajdzie różne nazwy dla lokalnych, więc nie będą dokładnie takie same. Gzip powinien znaleźć wspólne części (co daje dobry współczynnik kompresji), ale przeglądarka nadal musi je analizować indywidualnie. Ostatecznie nie powinno to być zauważalnym narzutem, ale znajdą się ludzie tacy jak ja, którzy po prostu nie lubią duplikatów kodu.
Tamas Hegedus
W porządku, dzięki za odpowiedź. Prawdopodobnie miałoby to wiele sensu również w przypadkach, w których musiałbyś wykonać kopię zapasową lub śledzić kod wyjściowy w kontroli wersji (gdzie rozmiar nieskompresowanego pliku się zwielokrotnia) lub gdy chciałbyś, aby wynik był niezminimalizowany z jakiegokolwiek powodu.
iono
gulp-rollup może być również dobrym dodatkiem do tej listy
GGG
@GGG Dodano przykład gulp. Niestety żaden z przykładów nie działa w tej chwili na oknach, zobacz wyjaśnienie na górze kodów.
Tamas Hegedus
8

Pakiet internetowy barebones 2

1) Jeśli to jest twój katalog główny:

index.html

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

scripts.js

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

kształty.js

export class Circle {
  ...
}

2) mieć węzeł zainstalowany węzeł

3) uruchom następującą komendę w swoim terminalu:

$ npm install -g webpack

5) w katalogu głównym uruchom następujące polecenie:

$ webpack scripts.js bundle.js

Powinieneś teraz mieć plik o nazwie bundle.js w swoim katalogu głównym, który będzie plikiem, który zajmie twój index.html. Jest to minimalistyczna funkcja sprzedaży pakietowej z pakietu internetowego. Możesz dowiedzieć się więcej tutaj

Isaac Pak
źródło
4

requirenie istnieje w przeglądarce, więc ten błąd jest oczekiwany. Musisz użyć czegoś takiego jak require.js lub Browserify.

djechlin
źródło