Нужно ли мне требовать 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

Ответы:


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.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.