Использование Node.js требует ES6 импорта / экспорта
В проекте, над которым я работаю, у нас есть два варианта использования модульной системы:
- Импорт модулей с использованием
require
и экспорт используяmodule.exports
а такжеexports.foo
, - Импорт модулей с использованием ES6
import
и экспорт с использованием ES6export
Есть ли какие-либо преимущества в производительности при использовании одного над другим? Есть ли что-то еще, что мы должны знать, если бы мы использовали модули ES6 вместо узлов?
11 ответов
Есть ли какие-либо преимущества в производительности при использовании одного над другим?
Имейте в виду, что еще нет движка JavaScript, который изначально поддерживает модули ES6. Вы сами сказали, что используете Вавилон. Бабель обращает import
а также export
декларация для CommonJS (require
/module.exports
) по умолчанию в любом случае. Так что даже если вы используете синтаксис модуля ES6, вы будете использовать CommonJS под капотом, если будете запускать код в Node.
Существуют технические различия между модулями CommonJS и ES6, например, CommonJS позволяет динамически загружать модули. ES6 не позволяет этого, но для этого существует API.
Поскольку модули ES6 являются частью стандарта, я бы их использовал.
Есть несколько вариантов использования / возможностей, которые вы можете рассмотреть:
Требуется:
- Вы можете иметь динамическую загрузку там, где имя загруженного модуля не является предопределенным / статическим, или если вы условно загружаете модуль, только если он "действительно требуется" (в зависимости от определенного потока кода).
- Загрузка синхронная. Это означает, что если у вас есть несколько
require
s, они загружаются и обрабатываются по одному.
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"