Использование Node.js требует ES6 импорта / экспорта

В проекте, над которым я работаю, у нас есть два варианта использования модульной системы:

  1. Импорт модулей с использованием requireи экспорт используя module.exports а также exports.foo,
  2. Импорт модулей с использованием ES6 importи экспорт с использованием ES6 export

Есть ли какие-либо преимущества в производительности при использовании одного над другим? Есть ли что-то еще, что мы должны знать, если бы мы использовали модули ES6 вместо узлов?

11 ответов

Решение

Есть ли какие-либо преимущества в производительности при использовании одного над другим?

Имейте в виду, что еще нет движка JavaScript, который изначально поддерживает модули ES6. Вы сами сказали, что используете Вавилон. Бабель обращает import а также export декларация для CommonJS (require/module.exports) по умолчанию в любом случае. Так что даже если вы используете синтаксис модуля ES6, вы будете использовать CommonJS под капотом, если будете запускать код в Node.

Существуют технические различия между модулями CommonJS и ES6, например, CommonJS позволяет динамически загружать модули. ES6 не позволяет этого, но для этого существует API.

Поскольку модули ES6 являются частью стандарта, я бы их использовал.

Есть несколько вариантов использования / возможностей, которые вы можете рассмотреть:

Требуется:

  • Вы можете иметь динамическую загрузку там, где имя загруженного модуля не является предопределенным / статическим, или если вы условно загружаете модуль, только если он "действительно требуется" (в зависимости от определенного потока кода).
  • Загрузка синхронная. Это означает, что если у вас есть несколько requires, они загружаются и обрабатываются по одному.

ES6 Импорт:

  • Вы можете использовать именованный импорт, чтобы выборочно загрузить только те части, которые вам нужны. Это может сохранить память.
  • Импорт может быть асинхронным (и в текущем ES6 Module Loader это действительно так) и может работать немного лучше.

Кроме того, система модулей Require не основана на стандартах. Маловероятно, что это станет стандартом сейчас, когда существуют модули ES6. В будущем будет встроена поддержка модулей ES6 в различных реализациях, что будет выгодно с точки зрения производительности.

На данный момент импорт ES6, экспорт всегда компилируется в CommonJS, поэтому нет никакой пользы от использования того или другого. Хотя рекомендуется использовать ES6, так как это должно быть выгодно, когда будет выпущена встроенная поддержка браузеров. Причина в том, что вы можете импортировать частичные данные из одного файла, в то время как с CommonJS вам нужно потребовать весь файл.

ES6 → import, export default, export

CommonJS → require, module.exports, exports.foo

Ниже показано их общее использование.

ES6 экспорт по умолчанию

// hello.js
function hello() {
  return 'hello'
}
export default hello

// app.js
import hello from './hello'
hello() // returns hello

ES6 экспорт нескольких и импорт нескольких

// hello.js
function hello1() {
  return 'hello1'
}
function hello2() {
  return 'hello2'
}
export { hello1, hello2 }

// app.js
import { hello1, hello2 } from './hello'
hello1()  // returns hello1
hello2()  // returns hello2

CommonJS module.exports

// hello.js
function hello() {
  return 'hello'
}
module.exports = hello

// app.js
const hello = require('./hello')
hello()   // returns hello

Модуль CommonJS. Экспорт нескольких

// hello.js
function hello1() {
  return 'hello1'
}
function hello2() {
  return 'hello2'
}
module.exports = {
  hello1,
  hello2
}

// app.js
const hello = require('./hello')
hello.hello1()   // returns hello1
hello.hello2()   // returns hello2

Основные преимущества синтаксические:

  • Более декларативный / компактный синтаксис
  • Модули ES6 в основном сделают UMD (Universal Module Definition) устаревшим - по сути, устранит раскол между CommonJS и AMD (сервер против браузера).

Вы вряд ли увидите какие-либо преимущества в производительности с модулями ES6. Вам по-прежнему понадобится дополнительная библиотека для объединения модулей, даже если в браузере имеется полная поддержка функций ES6.

Есть ли какие-либо преимущества в производительности при использовании одного над другим?

Текущий ответ - нет, потому что ни один из текущих движков браузера не реализует import/export от стандарта ES6.

Некоторые таблицы сравнения http://kangax.github.io/compat-table/es6/ не учитывают это, поэтому, когда вы видите почти все зеленые цвета для Chrome, просто будьте осторожны. import Ключевое слово из ES6 не было учтено.

Другими словами, современные браузерные движки, включая V8, не могут импортировать новый файл JavaScript из основного файла JavaScript через любую директиву JavaScript.

(У нас может быть всего несколько ошибок или годы, пока V8 не реализует это в соответствии со спецификацией ES6.)

Этот документ - то, что нам нужно, и этот документ - то, что мы должны соблюдать.

И стандарт ES6 говорит, что зависимости модуля должны быть там, прежде чем мы будем читать модуль, как на языке программирования C, где у нас были (заголовки) .h файлы.

Это хорошая и хорошо протестированная структура, и я уверен, что эксперты, создавшие стандарт ES6, имели это в виду.

Это то, что позволяет Webpack или другим пакетным пакетам оптимизировать пакет в некоторых особых случаях и уменьшить некоторые ненужные зависимости от пакета. Но в тех случаях, когда у нас есть идеальные зависимости, этого никогда не произойдет.

Это займет некоторое время, пока import/export родная поддержка начинает жить, а require Ключевое слово никуда не денется долго.

Что такое require?

Это node.js способ загрузки модулей. ( https://github.com/nodejs/node)

Узел использует системные методы для чтения файлов. Вы в основном полагаетесь на это при использовании require, require закончится каким-то системным вызовом, как uv_fs_open (зависит от конечной системы, Linux, Mac, Windows) для загрузки файла / модуля JavaScript.

Чтобы убедиться, что это правда, попробуйте использовать Babel.js, и вы увидите, что import Ключевое слово будет преобразовано в require,

Использование модулей ES6 может быть полезно для "тряски деревьев"; т.е. позволяя Webpack 2, Rollup (или другим упаковщикам) идентифицировать пути кода, которые не используются / не импортируются, и, следовательно, не попадают в результирующий пакет. Это может значительно уменьшить размер файла за счет исключения кода, который вам никогда не понадобится, но с CommonJS по умолчанию связан, потому что Webpack и другие не имеют возможности узнать, нужен ли он.

Это делается с помощью статического анализа пути кода.

Например, используя:

import { somePart } 'of/a/package';

... дает подсказку, что package.anotherPart не требуется (если он не импортирован, его нельзя использовать, верно?), поэтому он не будет мешать его связыванию.

Чтобы включить это для Webpack 2, вам нужно убедиться, что ваш транспортер не выплевывает модули CommonJS. Если вы используете es2015 плагин с Babel, вы можете отключить его в своем .babelrc вот так:

{
  "presets": [
    ["es2015", { modules: false }],
  ]
}

Свернуть и другие могут работать по-другому - просматривать документы, если вам интересно.

Когда дело доходит до асинхронной или ленивой загрузки, тогда import () гораздо мощнее. Посмотрим, когда мы требуем компонент в асинхронном режиме, тогда мы используем import это в некоторой асинхронной манере, как в const переменная с помощью await,

const module = await import('./module.js');

Или если вы хотите использовать require() затем,

const converter = require('./converter');

Дело в том import() на самом деле асинхронный характер. Как упомянуто neehar venugopal в ReactConf, вы можете использовать его для динамической загрузки компонентов.

Также это намного лучше, когда речь идет о маршрутизации. Это единственная особенность, которая заставляет сетевой журнал загружать необходимую часть, когда пользователь подключается к определенному веб-сайту к его конкретному компоненту. например. Страница входа в систему до панели инструментов не загрузит все компоненты панели инструментов. Потому что то, что нужно текущему, то есть компоненту входа, которое будет загружено только.

ПРИМЕЧАНИЕ. - Если вы разрабатываете проект node.js, вы должны строго использовать require() как узел будет выдавать ошибку исключения как invalid token 'import' если вы будете использовать import , Таким образом, узел не поддерживает операторы импорта

ОБНОВЛЕНИЕ - В соответствии с предложением Dan Dascalescu: начиная с версии 8.5.0 (выпущена в сентябре 2017 г.), node --experimemntal-modules index.mjs позволяет вам использовать import без Вавилона. Вы можете (и должны) также публиковать свои пакеты npm как собственный ESModule с обратной совместимостью для старых require путь.

Посмотрите это, чтобы больше узнать, как использовать асинхронный импорт - https://www.youtube.com/watch?v=bb6RCrDaxhw

Самое важное, что нужно знать, это то, что модули ES6 действительно являются официальным стандартом, а модули CommonJS (Node.js) - нет.

В 2019 году модули ES6 поддерживаются 84% браузеров. В то время как Node.js помещает их за флаг --experimental-modules, есть также удобный пакет узлов, называемый esm, который делает интеграцию гладкой.

Другая проблема, с которой вы можете столкнуться между этими модульными системами, - это местоположение кода. Node.js предполагает, что источник хранится в node_modules каталог, в то время как большинство модулей ES6 развернуты в единой структуре каталогов. Это не легко согласовать, но это можно сделать, взломав package.json файл со сценариями до и после установки. Вот пример изоморфного модуля и статья, объясняющая, как он работает.

Я лично использую импорт, потому что мы можем импортировать необходимые методы, члены, используя импорт.

import {foo, bar} from "dep";

Имя файла: dep.js

export foo function(){};
export const bar = 22

Кредит идет на Пола Шана. Больше информации

  • Модули ES являются статическими, что означает, что импорт описывается на верхнем уровне каждого модуля и вне любого оператора потока управления. Так не пойдет:

            if (condition) {
       import module1 from 'module1'
    }
    

Но это в commonjs разрешено:

      if (condition) {
    module = require('module1')
}
  • Модули ES неявно запускаются в . Это означает, что нам не нужно явно добавлять операторы «use strict» в начале каждого файла. Строгий режим нельзя отключить; поэтому мы не можем использовать необъявленные переменные или оператор with или иметь другие функции, доступные только в нестрогом режиме. strict modeявляется более безопасным режимом выполнения.

  • В ESM некоторые важные ссылки CommonJS не определены. Это включает require , exports , module.exports , __filename,а также __dirname.

  • Мы можем импортировать модули CommonJS из ESM, используя стандартный синтаксис импорта. Но только default exportsРабота:

               import packageName from 'commonjs-package' // Works
      import { moduleName } from 'commonjs-package' // Errors
    

Но невозможно импортировать модули ES из модулей CommonJS.

  • ESM не может импортировать файлы JSON напрямую как модули — функция, которая довольно часто используется с CommonJS. Вот почему в reactjs fetchиспользуется апи.

            import data from './data.json' //fails
    

Не уверен, почему (возможно, оптимизация - ленивая загрузка?) Это работает, но я заметил, что importможет не анализировать код, если импортированные модули не используются.
В некоторых случаях это может быть неожиданным поведением.

В качестве примера зависимости возьмем ненавистный класс Foo.

foo.ts

export default class Foo {}
console.log('Foo loaded');

Например:

index.ts

import Foo from './foo'
// prints nothing

index.ts

const Foo = require('./foo').default;
// prints "Foo loaded"

index.ts

(async () => {
    const FooPack = await import('./foo');
    // prints "Foo loaded"
})();

С другой стороны:

index.ts

import Foo from './foo'
typeof Foo; // any use case
// prints "Foo loaded"
Другие вопросы по тегам