Нужно ли мне требовать js, когда я использую babel?

98

Я экспериментирую с ES6, использую gulp для сборки и babel для переноса на ES5. Вывод не запускается в узле, а просто связан с файлом .htm с тегом. Я думаю, мне нужно добавить

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

или что-то вроде того.

Я пытаюсь импортировать / экспортировать.

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

}

Ошибка

Uncaught ReferenceError: require is not defined

Относится к этому (после .pipe (babel ()) in gulp)

var _shapes = require('shapes');
Джейсон
источник
3
Да, поскольку requireего нет в браузере, вам нужно использовать какой-нибудь инструмент сборки, например Require.js, Browserify или Webpack.
Jordan Running
1
Ах, добавление браузера в мой поиск в Google дало мне ответ, спасибо.
Джейсон
10
FWIW, обратите внимание, что сообщение об ошибке не указывает на то, что вам нужен require.js. Babel по умолчанию преобразует модули в CommonJS, что и использует Node, и определяет requireфункцию (опять же, ничего общего с require.js). Однако вы можете указать Babel преобразовать модули во что-то еще , например AMD или UMD, которые затем будут работать с require.js. В любом случае вам нужна система для загрузки модулей в браузер, потому что браузер не предоставляет их по умолчанию (пока).
Felix Kling
См. Здесь: stackoverflow.com/questions/28125554/…
Тодд

Ответы:

136

Нужно ли мне требовать js, когда я использую babel?

Вам может понадобиться загрузчик модулей, но это не обязательно RequireJS. У вас есть несколько вариантов. Следующее поможет вам начать работу.


rollup.js с помощью rollup-plugin-babel

Rollup - это сборщик модулей JavaScript нового поколения. Он изначально понимает модули ES2015 и создаст пакет, для работы которого не нужен загрузчик модулей. Неиспользуемый экспорт будет удален из вывода, это называется встряхиванием дерева.

Теперь я лично рекомендую использовать rollupjs, так как он дает наиболее четкий вывод и прост в настройке, однако он дает другой аспект ответа. Все остальные подходы делают следующее:

  1. Скомпилируйте код ES6 с помощью babel, используйте формат модуля по вашему выбору
  2. Объедините скомпилированные модули вместе с загрузчиком модулей ИЛИ используйте сборщик, который будет обходить зависимости за вас.

С rollupjs дела обстоят не так. Здесь rollup - это первый шаг, а не babel. По умолчанию он понимает только модули ES6. Вы должны указать модуль ввода, зависимости которого будут отслеживаться и объединяться. Поскольку ES6 позволяет использовать несколько именованных экспортов в модуле, rollupjs достаточно умен, чтобы удалять неиспользуемые экспорты, уменьшая таким образом размер пакета. К сожалению, парсер rollupjs-s не понимает> синтаксис ES6, поэтому модули ES7 должны быть скомпилированы до того, как их проанализирует объединительный пакет, но компиляция не должна влиять на импорт ES6. Это делается с помощью rollup-plugin-babelплагина с babel-preset-es2015-rollupпресетом (этот пресет такой же, как у es2015, за исключением модуля-преобразователя и плагина external-helpers). Таким образом, при правильной настройке накопительный пакет будет выполнять следующие действия с вашими модулями:

  1. Читает ваш модуль ES6-7 из файловой системы
  2. Плагин babel компилирует его в ES6 в памяти
  3. rollup анализирует код ES6 для импорта и экспорта (с помощью синтаксического анализатора acorn, скомпилированного в rollup)
  4. он проходит через весь граф и создает один пакет (который все еще может иметь внешние зависимости, и экспорт записи может быть экспортирован в формате по вашему выбору)

Пример сборки 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));

Пример сборки grunt с 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"]
        }
      }
    }
  });
}

Пример сборки gulp с 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 есть аккуратный пакет под названием babelify . Его использование простое и понятное:

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

или вы можете использовать его из 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"));

Это сразу перекомпилирует и объединит ваш код. Browserify .bundleбудет включать в себя небольшой симпатичный загрузчик CommonJS и упорядочит ваши перенесенные модули в функции. У вас может быть даже относительный импорт.

Пример:

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

Для компиляции просто запустите node build.jsсвой проект в корне.


Babel + WebPack

Скомпилируйте весь свой код с помощью babel. Я рекомендую вам использовать трансформатор модуля amd (названный babel-plugin-transform-es2015-modules-amdв babel 6). После этого свяжите ваши скомпилированные исходники с WebPack.

WebPack 2 вышел! Он понимает собственные модули ES6 и будет выполнять (или, скорее, моделировать) встряхивание дерева с помощью babili. встроенную мертвого кода . На данный момент (сентябрь 2016 г.) я все же предлагаю использовать rollup с babel, хотя мое мнение может измениться с выходом первого выпуска WebPack 2. Не стесняйтесь обсуждать свое мнение в комментариях.


Пользовательский конвейер компиляции

Иногда вам нужно больше контроля над процессом компиляции. Вы можете реализовать свой собственный конвейер следующим образом:

Во-первых, вам нужно настроить babel для использования модулей amd. По умолчанию babel переносится в модули CommonJS, что немного сложно обрабатывать в браузере, хотя browserify справляется с ними хорошо.

  • Вавилон 5: использование { modules: 'amdStrict', ... } вариант
  • Babel 6: используйте es2015-modules-amdплагин

Не забудьте включить moduleIds: true опцию.

Проверьте переданный код на наличие сгенерированных имен модулей, часто есть несоответствия между определенными и требуемыми модулями. Смотрите sourceRoot и moduleRoot .

Наконец, у вас должен быть какой-то загрузчик модулей, но это не обязательно requirejs. Есть almondjs , крошечная регулировочная прокладка, которая хорошо работает. Вы даже можете реализовать свои собственные:

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

В конце вы можете просто объединить прокладку загрузчика и скомпилированные модули вместе и запустить на этом uglify.


Стандартный код Babel дублируется в каждом модуле

По умолчанию большинство вышеперечисленных методов компилируют каждый модуль с babel по отдельности, а затем объединяют их вместе. Это то, что делает babelify. Но если вы посмотрите на скомпилированный код, вы увидите, что babel вставляет множество шаблонов в начало каждого файла, большинство из них дублируются во всех файлах.

Чтобы предотвратить это, вы можете использовать babel-plugin-transform-runtimeплагин.

Тамас Хегедус
источник
1
Это так чертовски тщательно; Спасибо. Re: дубликат шаблона Babel для каждого файла - было бы правильно предположить, что gzip почти отрицает это?
iono
1
Я никогда не измерял это сам, но я предполагаю, что нужно минимизировать пакет перед распространением, и минификация, вероятно, найдет разные имена для локальных, поэтому они не будут точно такими же. Gzip должен найти общие части (что приведет к хорошей степени сжатия), но браузеру все равно придется анализировать их по отдельности. В конечном итоге это не должно вызывать заметных накладных расходов, но будут такие люди, как я, которым просто не нравится дублированный код.
Tamas Hegedus
Достаточно честно, спасибо за ответ. Это, вероятно, будет иметь большой смысл также в тех случаях, когда вам нужно создавать резервную копию или отслеживать выходной код в системе управления версиями (когда размер несжатого файла увеличивается) или когда вы хотите, чтобы выходные данные не минифицировались по какой-либо причине.
iono
gulp-rollup тоже может быть хорошим дополнением к этому списку
GGG
@GGG Добавлен пример gulp. К сожалению, ни один из примеров в настоящее время не работает с Windows, см. Объяснение в верхней части кода.
Тамас Хегедус
8

barebones Webpack 2

1) Если это ваш корневой каталог:

index.html

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

scripts.js

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

shape.js

export class Circle {
  ...
}

2) иметь узел установлен узел

3) запустите в терминале следующую команду:

$ npm install -g webpack

5) в корневом каталоге выполните следующее:

$ webpack scripts.js bundle.js

Теперь у вас должен быть файл с именем bundle.js в корневом каталоге, который будет использовать ваш index.html. Это минималистичная функция связывания из webpack. Вы можете узнать больше здесь

Исаак Пак
источник
4

requireне существует в браузере, поэтому эта ошибка ожидаема. Вам нужно использовать что-то вроде require.js или Browserify.

Джехлин
источник