Export default javascript: export — JavaScript | MDN

Содержание

export — JavaScript | MDN

Инструкция export используется для экспорта функций, объектов или примитивов из файла (или модуля).

Примечание: Эта функциональность не реализована в браузерах на данный момент, но она реализована во многих транспайлерах, таких как Traceur Compiler, Babel or Rollup.

export { name1, name2, …, nameN };
export { variable1 as name1, variable2 as name2, …, nameN };
export let name1, name2, …, nameN; // или var
export let name1 = …, name2 = …, …, nameN; // или var, const

export default выражение;
export default function (…) { … } // или class, function*
export default function name1(…) { … } // или class, function*
export { name1 as default, … };

export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
nameN
Идентификатор для экспорта (чтобы он мог быть импортирован с помощью import в другом файле (скрипте)).

Существует два типа экспорта, каждый из которых описан ниже:

  • Именованный экспорт:
    export { myFunction }; 
    export const foo = Math.sqrt(2); 
  • Дефолтный экспорт (экспорт по умолчанию) (один на скрипт):
    export default function() {} 
    

Именованная форма более применима для экспорта нескольких величин. Во время импорта, можно будет использовать одно и то же имя, чтобы обратиться к соответствующему экспортируемому значению.

Касательно экспорта по умолчанию (default), он может быть только один для каждого отдельного модуля (файла). Дефолтный экспорт может представлять собой функцию, класс, объект или что-то другое. Это значение следует рассматривать как «основное», так как его будет проще всего импортировать.

Использование именованного экспорта

Мы могли бы использовать следующий код в модуле:


function cube(x) {
  return x * x * x;
}
const foo = Math.PI + Math.SQRT2;
export { cube, foo };

Таким образом в другом скрипте при помощи импорта (см. import) мы могли бы получить следующее:

import { cube, foo } from 'my-module';
console.log(cube(3)); 
console.log(foo);    

Использование export default

Если мы хотим экспортировать единственное значение или иметь резервное значение (fallback) для данного модуля, мы можем использовать export default.


export default function cube(x) {
  return x * x * x;
}

Затем, в другом скрипте можно импортировать это значение по умолчанию таким образом:

import cube from 'my-module';
console.log(cube(3)); 

BCD tables only load in the browser

Почему мы отказались от export default в Javascript и вам следует поступить также | by Tor Kazaryan

ES2015 содержал одни из лучших нововведений в Javascript за последние годы. Среди множества отличных улучшений появилась новая система модулей — Ecma Script Modules (ESM), которая окончательно решила проблему разделения кода между файлами (модулями) на уровне языка. Это стало большим шагом вперёд, но была необходимость совместимости с уже существующими модулями, особенно с CommonJS, используемым в node (require). По этой причине потребовался ряд компромиссов, одним из которых стало наличие экспорта по-умолчанию (export default). В этой статье я хочу объяснить, почему в Neufund мы решили отказаться от экспорта по умолчанию и использовать только именованные экспорты.

ESM являются частью ES6 (ES2015)

Улучшенный опыт разработки

Экспорт по умолчанию не экспортирует никакого имени, то есть символа, с которым может быть легко связанно экспортированное значение. С другой стороны, именованные экспорты, всегда привязаны к имени (довольно очевидно, правда?😂) . Это позволяет IDE находить и импортировать зависимости автоматически, что отлично сказывается на продуктивности.

Правда это круто?

Рефакторинг

Экспорт по-умолчанию делает невозможным крупномасштабный рефакторинг, так как импортируемый модуль может называться по-разному в каждом месте (включая опечатки).

/ в файле exports.js
export default function () {...}// в файле import1.js
import doSomething from "./exports.js"// в файле import2.js
import doSmth from "./exports.js"

В противоположность этому, именованные экспорты делают такой рефакторинг тривиальным.

Улучшенный tree shaking

Иногда, вы можете поддаться искушению и экспортировать один большой объект используя export default. Данный способ является анти-паттерном и препятствует корректному выполнению tree-shaking:

// не повторяйте этого дома
export default {
propertyA: "A",
propertyB: "B",
}// используйте данный способ
export const propertyA = "A";
export const propertyB = "B";

Использование именованных экспортов способно уменьшить размер вашей сборки, в случае, когда вы не используете все экспортируемые значения ( особенно полезно при сборке библиотек )

Заключение

Экспорт по-умолчанию был представлен в основном для более простой совместимости со множеством модулей CommonJS, которые экспортировались как единственное значение, например:

module.exports = function() {...}

Они не дают особых преимуществ при использовании внутри вашего проекта, поэтому, пожалуйста, избегайте их и таким образом сделайте этот мир лучше 😁

Разница между module.exports | exports | export | export default

1. Прежде всего, разница в использовании

Использование module.exports и exports сопровождается знаком равенства, за которым следует конкретный экспорт.

module.exports=...

exports=...

Использование экспорта и экспорта по умолчанию заключается в том, чтобы следовать конкретному экспорту напрямую без знака равенства.

export ...

export default ...

2. Exports — это ссылка на module.exports.

В nodejs каждый файл js рассматривается как модуль. Следовательно, nodejs будет генерировать объект модуля для каждого файла js. Этот объект модуля будет иметь атрибут экспорта, а атрибут экспорта является пустым объектом, а именно

module={
    exports:{}
}

Также существует объект экспорта, который ссылается на атрибут экспорта модуля.

exports=module.exports

3. Разница между экспортом и экспортом

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

Экспорт — это синтаксис, введенный es6, используемый для экспорта переменных, объектов, функций и классов в модуле. Соответствующее ключевое слово import — import. Чтобы
exports — это объект, а не грамматика. Конкретное значение было объяснено во втором пункте.

4. Разница между экспортом и экспортом по умолчанию.

И экспорт, и экспорт по умолчанию являются синтаксисом экспорта es6. Чтобы
Различия между ними заключаются в следующем:

1. В модуле может быть только один экспорт по умолчанию, и, конечно, не может быть никакого. В модуле может быть несколько экспортов.
2. Экспортируйте объекты, переменные, функции и классы по умолчанию без имен. У экспорта должно быть имя.
3. Импорт и экспорт, соответствующие экспорту по умолчанию, различаются.

, например

// test.js
export default const a = 1;
export const a = 1;

Соответствующий импорт:

import a from 'test'
import {a} from 'test'

Необходимо добавить импорт, соответствующий экспорту {}

  • Разница между алиасами при импорте

Оба могут иметь псевдоним при импорте. Но формат другой. Чтобы
Например, назовите импортированный a как b

// Псевдоним метода, соответствующий экспорту по умолчанию
import b from 'test'
 // Псевдоним метода, соответствующий экспорту
import {a as b} from 'test'

 

Разделение кода – React

Бандлинг

Большинство React-приложений «собирают» свои файлы такими инструментами, как Webpack, Rollup или Browserify. Сборка (или «бандлинг») — это процесс выявления импортированных файлов и объединения их в один «собранный» файл (часто называемый «bundle» или «бандл»). Этот бандл после подключения на веб-страницу загружает всё приложение за один раз.

Пример

Приложение:


import { add } from './math.js';

console.log(add(16, 26)); 

export function add(a, b) {
  return a + b;
}

Бандл:

function add(a, b) {
  return a + b;
}

console.log(add(16, 26)); 

Примечание:

Ваши бандлы будут выглядеть не так, как мы только что показали.

Если вы используете Create React App, Next.js, Gatsby или похожие инструменты, то у вас уже будет настроенный Webpack для бандлинга приложения.

Иначе, вам нужно будет настроить webpack самостоятельно. Для этого ознакомьтесь со страницами по установке и началу работы в документации по Webpack.

Разделение кода

Бандлинг — это хорошо, но по мере роста вашего приложения, ваш бандл тоже будет расти. Особенно если вы подключаете крупные сторонние библиотеки. Вам нужно следить за кодом, который вы подключаете, чтобы случайно не сделать приложение настолько большим, что его загрузка займёт слишком много времени.

Чтобы предотвратить разрастание бандла, стоит начать «разделять» ваш бандл. Разделение кода — это возможность, поддерживаемая такими бандлерами как Webpack, Rollup или Browserify (с factor-bundle), которая может создавать несколько бандлов и загружать их по мере необходимости.

Хоть вы и не уменьшите общий объём кода вашего приложения, но избежите загрузки кода, который
может никогда не понадобиться пользователю и уменьшите объём кода, необходимый для начальной загрузки.

import()

Лучший способ внедрить разделение кода в приложение — использовать синтаксис динамического импорта: import().

До:

import { add } from './math';

console.log(add(16, 26));

После:

import("./math").then(math => {
  console.log(math.add(16, 26));
});

Когда Webpack сталкивается с таким синтаксисом, он автоматически начинает разделять код вашего приложения. Если вы используете Create React App, то всё уже настроено и вы можете сразу начать использовать синтаксис динамического импорта. Он также поддерживается «из коробки» в Next.js.

Если вы настраиваете Webpack самостоятельно, то, вероятно, захотите прочитать руководство Webpack по разделению кода. Файл конфигурации Webpack должен выглядеть примерно так.

Если вы используете Babel, вам необходимо убедиться, что он понимает синтаксис динамического импорта.
Для этого вам необходимо установить пакет @babel/plugin-syntax-dynamic-import.

React.lazy

Примечание:

Возможности React.lazy и задержки (suspense) пока недоступны для рендеринга на стороне сервера. Если вам нужно разделение кода в серверном приложении, мы рекомендуем Loadable Components. У них есть хорошее руководство по разделению бандла с серверным рендерингом.

Функция React.lazy позволяет рендерить динамический импорт как обычный компонент.

До:

import OtherComponent from './OtherComponent';

После:

const OtherComponent = React.lazy(() => import('./OtherComponent'));

Она автоматически загрузит бандл, содержащий OtherComponent, когда этот компонент будет впервые отрендерен.

React.lazy принимает функцию, которая должна вызвать динамический import(). Результатом возвращённого Promise является модуль, который экспортирует по умолчанию React-компонент (export default).

Задержка

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

import React, { Suspense } from 'react';

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Загрузка...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}

Проп fallback принимает любой React-элемент, который вы хотите показать, пока происходит загрузка компонента. Компонент Suspense можно разместить в любом месте над ленивым компонентом. Кроме того, можно обернуть несколько ленивых компонентов одним компонентом Suspense.

import React, { Suspense } from 'react';

const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Загрузка...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </div>
  );
}

Предохранители

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

import React, { Suspense } from 'react';
import MyErrorBoundary from './MyErrorBoundary';

const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));

const MyComponent = () => (
  <div>
    <MyErrorBoundary>
      <Suspense fallback={<div>Загрузка...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </MyErrorBoundary>
  </div>
);

Разделение кода на основе маршрутов

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

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

Вот пример того, как организовать разделение кода на основе маршрутов с помощью React.lazy и таких библиотек как React Router.

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Загрузка...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);

Именованный экспорт

React.lazy в настоящее время поддерживает только экспорт по умолчанию. Если модуль, который требуется импортировать, использует именованный экспорт, можно создать промежуточный модуль, который повторно экспортирует его как модуль по умолчанию. Это гарантирует работоспособность tree shaking — механизма устранения неиспользуемого кода.


export const MyComponent = ;
export const MyUnusedComponent = ;

export { MyComponent as default } from "./ManyComponents.js";

import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));

экспорт — JavaScript | MDN

Используется оператор экспорт
при создании модулей JavaScript для экспорта живых привязок к функциям, объектам или
примитивные значения из модуля, чтобы они могли использоваться другими программами с
импорт выписка. Значение импортированной привязки
может быть изменен в модуле, который его экспортирует. Когда модуль обновляет значение
привязка, которую он экспортирует, обновление будет отображаться в его импортированном значении.

Экспортируемые модули находятся в строгом режиме, независимо от того,
объявлять их таковыми или нет. Оператор экспорта не может использоваться во встроенных скриптах.

Есть два типа экспорта:

  1. Именованный экспорт (ноль или более экспортов на модуль)
  2. Экспорт по умолчанию (один на модуль)
 
экспорт let name1, name2,…, nameN;
экспорт let name1 =…, name2 =…,…, nameN;
функция экспорта functionName () {...}
экспорт класса ClassName {...}


экспорт {имя1, имя2,…, имяN};


экспорт {переменная1 как имя1, переменная2 как имя2,…, имяN};


экспорт const {имя1, имя2: бар} = o;


экспортировать выражение по умолчанию;
функция экспорта по умолчанию (…) {…}
экспортировать функцию по умолчанию name1 (…) {…}
экспорт {имя1 по умолчанию,…};


экспорт * из…;
экспорт * как name1 из…;
экспорт {имя1, имя2,…, имяN} из…;
экспорт {импорт1 как имя1, импорт2 как имя2,…, имяN} из…;
экспорт {по умолчанию,…} из…;
  
наименование N

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

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

Именованный экспорт:

 
экспорт {myFunction, myVariable};



экспорт let myVariable = Math.sqrt (2);
функция экспорта myFunction () {...};
  

Экспорт по умолчанию:

 
экспорт {myFunction по умолчанию};


функция экспорта по умолчанию () {...}
экспортировать класс по умолчанию {..}


  

Именованный экспорт полезен для экспорта нескольких значений. При импорте обязательно
использовать то же имя соответствующего объекта.

Но экспорт по умолчанию можно импортировать с любым именем, например:

 
пусть k; экспорт по умолчанию k = 12;
  
 
импортировать m из './test';
console.log (м);
  

Вы также можете переименовать именованный экспорт, чтобы избежать конфликтов имен:

  экспорт {myFunction as function1,
         myVariable как переменная};
  

Реэкспорт / агрегирование

Также можно «импортировать / экспортировать» из разных модулей родительского модуля, поэтому
что они доступны для импорта из этого модуля.Другими словами, можно создать
один модуль, концентрирующий различные экспортные данные из разных модулей.

Этого можно добиться с помощью синтаксиса «экспорт из»:

  экспорт {по умолчанию как function1,
         function2} из bar.js;
  

Что сравнимо с комбинацией импорта и экспорта:

  import {по умолчанию как function1,
         function2} из bar.js;
экспорт {функция1 по умолчанию, функция2};
  

Но где function1 и function2 недоступны
внутри текущего модуля.

Примечание: Следующее синтаксически недопустимо, несмотря на его импорт
эквивалент:

  импортировать DefaultExport из bar.js;
  
  экспорт DefaultExport из bar.js;
  

Правильный способ сделать это — переименовать экспорт:

  экспорт {по умолчанию как DefaultExport} из bar.js;
  

Синтаксис «экспорт из» позволяет опустить как маркер ; тем не мение
это будет означать, что элемент по умолчанию не может быть импортирован как именованный импорт:

  экспорт {по умолчанию, функция2} из бара.js ';
  

Использование именованного экспорта

В модуль my-module.js мы могли бы включить следующий код:

 
function cube (x) {
  вернуть х * х * х;
}

const foo = Math.PI + Math.SQRT2;

var graph = {
  параметры: {
      цвет белый',
      толщина: '2px'
  },
  draw: function () {
      console.log ('Из функции рисования графика');
  }
}

экспорт {cube, foo, graph};
  

Тогда в модуле верхнего уровня, включенном в вашу HTML-страницу, мы могли бы иметь:

  импорт {cube, foo, graph} из './my-module.js ';

graph.options = {
    цвет синий',
    толщина: '3px'
};

graph.draw ();
console.log (куб (3));
console.log (foo);
  

Важно отметить следующее:

  • Вам необходимо включить этот скрипт в свой HTML с

    Основной файл my-module.js , где моделируем модуль:

      var myModule = (function () {// Открыть IIFE
      // Импорт (через глобальные переменные)
      var importFunc1 = otherModule1.importedFunc1;
      var importFunc2 = otherModule2.importedFunc2;
    
      // Тело
      function internalFunc () {
        // ···
      }
      function exportedFunc () {
        importFunc1 ();
        importFunc2 ();
        internalFunc ();
      }
    
      // Экспорт (присваивается глобальной переменной `myModule`)
      возвращение {
        exportedFunc: exportedFunc,
      };
    }) (); // Закрыть IIFE  

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

    Этот способ обертывания фрагмента кода называется немедленно вызываемым функциональным выражением (IIFE, придумано Беном Алманом). Что мы получаем от IIFE? var не является блочной областью видимости (например, const и let ), она привязана к функции: единственный способ создать новую область видимости для var -объявленных переменных - использовать функции или методы (с const и , пусть , вы можете использовать функции, методы или блоки {} ).Следовательно, IIFE в этом примере скрывает все следующие переменные из глобальной области и минимизирует конфликты имен: importFunc1 , importFunc2 , internalFunc , exportedFunc .

    Обратите внимание, что мы используем IIFE особым образом: в конце мы выбираем то, что хотим экспортировать, и возвращаем его через литерал объекта. Это называется выкройкой модуля раскрытия (придуманной Кристианом Хейльманном).

    У этого способа моделирования модулей есть несколько проблем:

    • Библиотеки в файлах сценариев экспортируют и импортируют функции через глобальные переменные, что может привести к конфликту имен.
    • Зависимости не указаны явно, и нет встроенного способа для скрипта загружать скрипты, от которых он зависит. Следовательно, веб-страница должна загружать не только сценарии, которые необходимы странице, но также и зависимости этих сценариев, зависимости зависимостей и т. Д. И это должно происходить в правильном порядке!

    27.4 Модульные системы, созданные до ES6

    До ECMAScript 6 в JavaScript не было встроенных модулей. Следовательно, гибкий синтаксис языка был использован для реализации пользовательских модульных систем в пределах языка.Два популярных:

    • CommonJS (нацелен на сервер)
    • AMD (определение асинхронного модуля, нацеленное на клиентскую сторону)
    27.4.1 Сторона сервера: модули CommonJS

    Первоначальный стандарт CommonJS для модулей был создан для серверных и настольных платформ. Это была основа исходной модульной системы Node.js, где она приобрела огромную популярность. Этому способствовали менеджер пакетов npm для Node и инструменты, позволяющие использовать модули Node на стороне клиента (browserify, webpack и другие).

    С этого момента модуль CommonJS означает версию этого стандарта для Node.js (которая имеет несколько дополнительных функций). Это пример модуля CommonJS:

      // Импорт
    var importFunc1 = require ('./ other-module1.js'). importFunc1;
    var importFunc2 = require ('./ other-module2.js'). importFunc2;
    
    // Тело
    function internalFunc () {
      // ···
    }
    function exportedFunc () {
      importFunc1 ();
      importFunc2 ();
      internalFunc ();
    }
    
    // Экспорт
    module.exports = {
      exportedFunc: exportedFunc,
    };  

    CommonJS можно охарактеризовать следующим образом:

    • Предназначен для серверов.
    • Модули предназначены для загрузки синхронно (импортер ожидает, пока импортируемый модуль загружается и выполняется).
    • Компактный синтаксис.
    27.4.2 Клиентская сторона: модули AMD (определение асинхронного модуля)

    Формат модуля AMD был создан для того, чтобы его было проще использовать в браузерах, чем формат CommonJS. Самая популярная его реализация - RequireJS. Ниже приведен пример модуля AMD.

      определить (['./ other-module1.js ',' ./other-module2.js '],
      function (otherModule1, otherModule2) {
        var importFunc1 = otherModule1.importedFunc1;
        var importFunc2 = otherModule2.importedFunc2;
    
        function internalFunc () {
          // ···
        }
        function exportedFunc () {
          importFunc1 ();
          importFunc2 ();
          internalFunc ();
        }
        
        возвращение {
          exportedFunc: exportedFunc,
        };
      });  

    AMD можно охарактеризовать следующим образом:

    • Предназначен для браузеров.
    • Модули предназначены для загрузки асинхронно .Это важнейшее требование для браузеров, в которых код не может дождаться завершения загрузки модуля. Он должен быть уведомлен, как только модуль станет доступен.
    • Синтаксис немного сложнее.

    С другой стороны, модули AMD можно запускать напрямую. Напротив, модули CommonJS должны быть либо скомпилированы перед развертыванием, либо пользовательский исходный код должен быть сгенерирован и динамически оценен (например, eval () ). Это не всегда разрешено в Интернете.

    27.4.3 Характеристики модулей JavaScript

    Если посмотреть на CommonJS и AMD, можно заметить сходство между модульными системами JavaScript:

    • На каждый файл приходится по одному модулю.
    • Такой файл по сути представляет собой фрагмент кода, который выполняется:
      • Локальная область: код выполняется в локальной «области модуля». Поэтому по умолчанию все объявленные в нем переменные, функции и классы являются внутренними, а не глобальными.
      • Экспорт: если вы хотите, чтобы любой объявленный объект был экспортирован, вы должны явно пометить его как экспорт.
      • Импорт: каждый модуль может импортировать экспортированные объекты из других модулей. Эти другие модули идентифицируются с помощью спецификаторов модулей (обычно пути, иногда полные URL-адреса).
    • Модули

    • - это синглтонов : Даже если модуль импортируется несколько раз, существует только один его «экземпляр».
    • Глобальные переменные не используются. Вместо этого спецификаторы модуля служат глобальными идентификаторами.

    27,5 Модули ECMAScript

    модулей ECMAScript ( ES модулей или ESM ) были представлены с ES6.Они продолжают традицию модулей JavaScript и обладают всеми вышеперечисленными характеристиками. Дополнительно:

    • В CommonJS модули ES имеют компактный синтаксис и поддерживают циклические зависимости.
    • Совместно с AMD модули ES предназначены для асинхронной загрузки.

    Модули

    ES также имеют новые преимущества:

    • Синтаксис даже компактнее, чем у CommonJS.
    • Модули

    • имеют статических, структур (которые нельзя изменить во время выполнения).Это помогает со статической проверкой, оптимизированным доступом к импорту, удалением мертвого кода и многим другим.
    • Поддержка циклического импорта полностью прозрачна.

    Это пример синтаксиса модуля ES:

      импорт {importFunc1} из './other-module1.mjs';
    импортировать {importFunc2} из './other-module2.mjs';
    
    function internalFunc () {
      ···
    }
    
    функция экспорта exportedFunc () {
      importFunc1 ();
      importFunc2 ();
      internalFunc ();
    }  

    Отныне «модуль» означает «модуль ECMAScript».

    27.5.1 ES-модули: синтаксис, семантика, загрузчик API

    Полный стандарт модулей ES состоит из следующих частей:

    1. Синтаксис (как пишется код): Что такое модуль? Как декларируются импорт и экспорт? И т. Д.
    2. Семантика (как выполняется код): как экспортируются привязки переменных? Как импорт связан с экспортом? И т. Д.
    3. Программный загрузчик API для настройки загрузки модуля.

    Части 1 и 2 были представлены в ES6.Работа над частью 3 продолжается.

    27,6 Названный экспорт и импорт

    27.6.1 Именованный экспорт

    Каждый модуль может иметь ноль или более именованных экспортов .

    В качестве примера рассмотрим следующие два файла:

      библиотека / my-math.mjs
    main.mjs  

    Модуль my-math.mjs имеет два именованных экспорта: square и LIGHTSPEED .

      // Не экспортируется, частный для модуля
    function times (a, b) {
      вернуть a * b;
    }
    квадрат функции экспорта (x) {
      время возврата (x, x);
    }
    экспорт const LIGHTSPEED = 2997
  • ;
  • Чтобы что-то экспортировать, мы помещаем ключевое слово export перед декларацией.Сущности, которые не экспортируются, являются частными для модуля и недоступны извне.

    27.6.2 Именованный импорт

    Модуль main.mjs имеет единственный именованный импорт, квадрат :

      импорт {квадрат} из './lib/my-math.mjs';
    assert.equal (квадрат (3), 9);  

    Он также может переименовать свой импорт:

      импортировать {квадрат как sq} из './lib/my-math.mjs';
    assert.equal (sq (3), 9);  
    27.6.2.1 Синтаксическая ошибка: именованный импорт не деструктурирует

    И названный импорт, и деструктуризация выглядят одинаково:

      импорт {foo} из './bar.mjs '; // Импортировать
    const {foo} = require ('./ bar.mjs'); // деструктуризация  

    Но они совсем другие:

    • Импорт остается связанным с экспортом.

    • Вы можете снова деструктурировать внутри шаблона деструктуризации, но {} в операторе импорта не может быть вложенным.

    • Синтаксис для переименования другой:

        импортировать {foo as f} из './bar.mjs'; // импорт
      const {foo: f} = require ('./bar.mjs '); // деструктуризация  

      Обоснование: деструктуризация напоминает литерал объекта (включая вложение), в то время как импорт вызывает идею переименования.

    Упражнение: именованный экспорт

    упражнений / модулей / export_ named_test.mjs

    27.6.3 Импорт пространства имен

    Импорт пространства имен является альтернативой именованному импорту. Если мы импортируем модуль в пространство имен, он становится объектом, свойства которого являются именованными экспортами.Вот как выглядит main.mjs , если мы используем импорт пространства имен:

      import * as myMath from './lib/my-math.mjs';
    assert.equal (myMath.square (3), 9);
    
    assert.deepEqual (
      Object.keys (myMath), ['LIGHTSPEED', 'квадрат']);  
    27.6.4 Именованные стили экспорта: встроенное предложение по сравнению с предложением (расширенное)

    Именованный стиль экспорта, который мы видели до сих пор, был inline : Мы экспортировали объекты, добавив к ним префикс export .

    Но мы также можем использовать отдельные статьи экспорта .Например, так выглядит lib / my-math.mjs с условием экспорта:

      функции раз (а, б) {
      вернуть a * b;
    }
    function square (x) {
      время возврата (x, x);
    }
    const LIGHTSPEED = 2997
  • ; экспорт {квадрат, LIGHTSPEED}; // точка с запятой!
  • С помощью предложения экспорта мы можем переименовать перед экспортом и использовать другие имена внутри:

      функции раз (а, б) {
      вернуть a * b;
    }
    function sq (x) {
      время возврата (x, x);
    }
    const LS = 2997
  • ; export { кв как квадрат, LS как LIGHTSPEED, // запятая не обязательна };
  • 27.7 Экспорт и импорт по умолчанию

    Каждый модуль может иметь не более одного экспорта по умолчанию . Идея состоит в том, что модуль является экспортируемым значением по умолчанию .

    Избегайте смешивания именованного экспорта и экспорта по умолчанию

    Модуль может иметь как именованный экспорт, так и экспорт по умолчанию, но обычно лучше придерживаться одного стиля экспорта для каждого модуля.

    В качестве примера экспорта по умолчанию рассмотрим следующие два файла:

      my-func.mjs
    main.mjs  

    Модуль my-func.mjs имеет экспорт по умолчанию:

      const GREETING = 'Привет!';
    функция экспорта по умолчанию () {
      вернуться ПРИВЕТСТВИЕ;
    }  

    Модуль main.mjs по умолчанию - импортирует экспортируемую функцию:

      импортировать myFunc из './my-func.mjs';
    assert.equal (myFunc (), 'Привет!');  

    Обратите внимание на синтаксическую разницу: фигурные скобки вокруг именованных импортов указывают, что мы достигли в модуля, тогда как импорт по умолчанию - это модуль.

    Каковы варианты использования экспорта по умолчанию?

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

    27.7.1 Два стиля экспорта по умолчанию

    Есть два стиля выполнения экспорта по умолчанию.

    Во-первых, вы можете пометить существующие объявления с помощью по умолчанию для экспорта :

      export default function foo () {} // без точки с запятой!
    экспорт класса по умолчанию Bar {} // без точки с запятой!  

    Во-вторых, вы можете напрямую экспортировать значения по умолчанию.xyz $ /;
    экспорт по умолчанию 5 * 7;
    экспорт по умолчанию {нет: ложь, да: истина};

    27.7.1.1 Почему существует два стиля экспорта по умолчанию?

    Причина в том, что export default нельзя использовать для обозначения const : const может определять несколько значений, но export default требует ровно одно значение. Рассмотрим следующий гипотетический код:

      // Недопустимый JavaScript!
    экспорт по умолчанию const foo = 1, bar = 2, baz = 3;  

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

    Упражнение: экспорт по умолчанию

    упражнений / модулей / export_default_test.mjs

    27.7.2 Экспорт по умолчанию как именованный экспорт (расширенный)

    Внутренне экспорт по умолчанию - это просто именованный экспорт с именем по умолчанию . В качестве примера рассмотрим предыдущий модуль my-func.mjs с экспортом по умолчанию:

      const GREETING = 'Привет!';
    функция экспорта по умолчанию () {
      вернуться ПРИВЕТСТВИЕ;
    }  

    Следующий модуль my-func2.mjs эквивалентен этому модулю:

      const GREETING = 'Привет!';
    function greet () {
      вернуться ПРИВЕТСТВИЕ;
    }
    
    export {
      приветствовать по умолчанию,
    };  

    Для импорта мы можем использовать обычный импорт по умолчанию:

      импортировать myFunc из './my-func2.mjs';
    assert.equal (myFunc (), 'Привет!');  

    Или мы можем использовать именованный импорт:

      import {default as myFunc} from './my-func2.mjs';
    assert.equal (myFunc (), 'Привет!');  

    Экспорт по умолчанию также доступен через свойство .по умолчанию для импорта пространства имен:

      import * as mf from './my-func2.mjs';
    assert.equal (mf.default (), 'Привет!');  

    Разве по умолчанию не является недопустимым в качестве имени переменной?

    default не может быть именем переменной, но это может быть имя экспорта или имя свойства:

      const obj = {
      по умолчанию: 123,
    };
    assert.equal (obj.default, 123);  

    27,8 Подробнее об экспорте и импорте

    27.8.1 Импорт - это просмотр экспорта только для чтения

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

    Рассмотрим следующие два модуля:

      counter.mjs
    main.mjs  

    counter.mjs экспортирует (изменяемую!) Переменную и функцию:

      экспорт let counter = 3;
    функция экспорта incCounter () {
      счетчик ++;
    }  

    основной.mjs импортирует оба экспорта. Когда мы используем incCounter () , мы обнаруживаем, что соединение со счетчиком активно - мы всегда можем получить доступ к текущему состоянию этой переменной:

      import {counter, incCounter} из './counter.mjs';
    
    // Импортированное значение `counter` живо
    assert.equal (счетчик, 3);
    incCounter ();
    assert.equal (счетчик, 4);  

    Обратите внимание, что пока соединение активно и мы можем прочитать счетчик , мы не можем изменить эту переменную (например,g., через счетчик ++ ).

    Такой способ обработки импорта дает два преимущества:

    • Модули легче разделить, потому что ранее совместно используемые переменные можно экспортировать.
    • Это поведение имеет решающее значение для поддержки прозрачного циклического импорта. Читайте дальше для получения дополнительной информации.
    27.8.2 Прозрачная поддержка циклического импорта в ESM (расширенная)

    ESM прозрачно поддерживает циклический импорт. Чтобы понять, как это достигается, рассмотрим следующий пример: рис.7 показывает ориентированный граф модулей, импортирующих другие модули. P, импортировавший M, в данном случае является циклом.

    Рисунок 7: Направленный граф модулей, импортирующих модули: M импортирует N и O, N импортирует P и Q и т. Д.

    После синтаксического анализа эти модули настраиваются в два этапа:

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

    Этот подход правильно обрабатывает циклический импорт благодаря двум особенностям модулей ES:

    • Из-за статической структуры модулей ES экспорт уже известен после синтаксического анализа. Это позволяет создать экземпляр P до того, как его дочерний элемент M: P уже сможет искать экспорт M.

    • Когда оценивается P, M еще не оценивался. Однако объекты в P уже могут упоминать импорт из M.Они просто пока не могут их использовать, потому что импортированные значения заполняются позже. Например, функция в P может получить доступ к импорту из M. Единственное ограничение состоит в том, что мы должны дождаться завершения оценки M, прежде чем вызывать эту функцию.

      Импорт, заполняемый позже, становится возможным благодаря тому, что они являются «живыми неизменяемыми представлениями» для экспорта.

    27,9 пакетов в минуту

    Реестр программного обеспечения npm является основным способом распространения библиотек и приложений JavaScript для Node.js и веб-браузеры. Он управляется с помощью диспетчера пакетов npm (сокращенно: npm ). Программное обеспечение распространяется в виде так называемых пакетов . Пакет - это каталог, содержащий произвольные файлы и файл package.json на верхнем уровне, который описывает пакет. Например, когда npm создает пустой пакет внутри каталога my-package / , вы получаете package.json :

      {
      "имя": "мой-пакет",
      "версия": "1.0.0",
      "описание": "",
      "main": "index.js ",
      "scripts": {
        "test": "echo \" Ошибка: тест не указан \ "&& exit 1"
      },
      "ключевые слова": [],
      "автор": "",
      "лицензия": "ISC"
    }  

    Некоторые из этих свойств содержат простые метаданные:

    • name указывает имя этого пакета. После загрузки в реестр npm его можно установить с помощью npm install my-package .
    • версия используется для управления версиями и следует семантическому управлению версиями с тремя номерами:
      • Основная версия: увеличивается при внесении несовместимых изменений API.
      • Дополнительная версия: увеличивается, когда функциональность добавляется обратно совместимым способом.
      • Версия патча: увеличивается, когда вносятся обратно совместимые изменения.
    • описание , ключевые слова , автор упрощают поиск пакетов.
    • лицензия разъясняет, как можно использовать этот пакет.

    Другие свойства позволяют расширенную настройку:

    • main : указывает модуль, который «является» пакетом (поясняется далее в этой главе).
    • скриптов : команды, которые можно выполнить через npm run . Например, сценарий test может быть выполнен через npm run test .

    Для получения дополнительной информации о package.json обратитесь к документации npm.

    27.9.1 Пакеты устанавливаются внутри каталога

    node_modules /

    npm всегда устанавливает пакеты в каталог node_modules . Таких каталогов обычно много.Какой из них использует npm, зависит от того, в каком каталоге он сейчас находится. Например, если мы находимся внутри каталога / tmp / a / b / , npm пытается найти node_modules в текущем каталоге, его родительском каталоге, родительском каталоге родительского и т. Д. Другими словами, он выполняет поиск по следующей цепочке местоположений:

    • / tmp / a / b / node_modules
    • / tmp / a / node_modules
    • / tmp / node_modules

    При установке пакета some-pkg npm использует ближайшие node_modules .Если, например, мы находимся внутри / tmp / a / b / и в этом каталоге есть node_modules , то npm помещает пакет в каталог:

      / tmp / a / b / node_modules / some-pkg /  

    При импорте модуля мы можем использовать специальный спецификатор модуля, чтобы сообщить Node.js, что мы хотим импортировать его из установленного пакета. Как именно это работает, будет объяснено позже. А пока рассмотрим следующий пример:

      // / дом / Джейн / проект / main.mjs
    import * как модуль из the-package / the-module.mjs;  

    Чтобы найти the-module.mjs (Node.js предпочитает расширение имени файла .mjs для модулей ES), Node.js проходит по цепочке node_module и ищет следующие местоположения:

    • /home/jane/proj/node_modules/the-package/the-module.mjs
    • /home/jane/node_modules/the-package/the-module.mjs
    • / home / node_modules / пакет / модуль.mjs
    27.9.2 Почему npm можно использовать для установки интерфейсных библиотек?

    Поиск установленных модулей в каталогах node_modules поддерживается только на Node.js. Так почему мы можем также использовать npm для установки библиотек для браузеров?

    Это активируется с помощью инструментов связывания, таких как webpack, которые компилируют и оптимизируют код перед его развертыванием в сети. В процессе компиляции код в пакетах npm адаптируется для работы в браузерах.

    27.10 модулей именования

    Не существует установленных передовых методов именования файлов модулей и переменных, в которые они импортируются.

    В этой главе я использую следующий стиль именования:

    • Имена файлов модулей набираются тире и начинаются с строчных букв:

        ./my-module.mjs
      ./some-func.mjs  
    • Имена импортируемых пространств имен в нижнем регистре и в верблюжьем регистре:

        import * as myModule from './my-module.mjs ';  
    • Имена импорта по умолчанию в нижнем регистре и в верблюжьем регистре:

        импортировать someFunc из './some-func.mjs';  

    Каковы причины этого стиля?

    • npm не допускает прописных букв в именах пакетов (источник). Таким образом, мы избегаем верблюжьего регистра, чтобы имена «локальных» файлов совпадали с именами пакетов npm. Использование только строчных букв также минимизирует конфликты между файловыми системами, которые чувствительны к регистру, и файловыми системами, которые не чувствительны к регистру: первые различают файлы, имена которых имеют одинаковые буквы, но с разным регистром; последнее - нет.

    • Существуют четкие правила для преобразования имен файлов с тире в имена переменных JavaScript с верблюжьим регистром. Из-за того, как мы называем импорт пространства имен, эти правила работают как для импорта пространства имен, так и для импорта по умолчанию.

    Мне также нравятся имена файлов модулей с подчеркиванием, потому что вы можете напрямую использовать эти имена для импорта пространства имен (без какого-либо перевода):

      import * as my_module from './my_module.mjs';  

    Но этот стиль не работает для импорта по умолчанию: мне нравится подчеркивание для объектов пространства имен, но это не лучший выбор для функций и т. Д.

    27.11 Спецификаторы модуля

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

    27.11.1 Категории спецификаторов модулей

    В модулях ES мы различаем следующие категории спецификаторов. Эти категории возникли из модулей CommonJS.

    • Относительный путь: начинается с точки.Примеры:

        './some/other/module.mjs'
      '../../lib/counter.mjs'  
    • Абсолютный путь: начинается с косой черты. Пример:

        '/home/jane/file-tools.mjs'  
    • URL: включает протокол (технически пути также являются URL-адресами). Примеры:

        'https://example.com/some-module.mjs'
      'файл: ///home/john/tmp/main.mjs'  
    • Пустой путь: не начинается с точки, косой черты или протокола и состоит из одного имени файла без расширения.Примеры:

        'lodash'
      «пакет»  
    • Глубокий путь импорта: начинается с чистого пути и имеет как минимум одну косую черту. Пример:

        "пакет / dist / the-module.mjs"  
    27.11.2 Спецификаторы модуля ES в браузерах

    Браузеры обрабатывают спецификаторы модуля следующим образом:

    • Относительные пути, абсолютные пути и URL-адреса работают должным образом. Все они должны указывать на реальные файлы (в отличие от CommonJS, который позволяет опускать расширения файлов и многое другое).
    • Расширения имен файлов модулей не имеют значения, если они обслуживаются с типом содержимого text / javascript .
    • Каким образом будут обрабатываться голые пути, пока не ясно. Вы, вероятно, в конечном итоге сможете сопоставить их с другими спецификаторами через таблицы поиска.

    Обратите внимание, что инструменты объединения, такие как webpack, которые объединяют модули в меньшее количество файлов, часто менее строги к спецификаторам, чем браузеры. Это потому, что они работают во время сборки / компиляции (не во время выполнения) и могут искать файлы, просматривая файловую систему.

    27.11.3 Спецификаторы модуля ES на Node.js

    Node.js обрабатывает спецификаторы модуля следующим образом:

    • Относительные пути разрешаются так же, как и в веб-браузерах - относительно пути к текущему модулю.

    • Абсолютные пути в настоящее время не поддерживаются. В качестве обходного пути можно использовать URL-адреса, начинающиеся с file: /// . Вы можете создать такие URL-адреса через url.pathToFileURL () .

    • Только файл : поддерживается в качестве протокола для спецификаторов URL.

    • Пустой путь интерпретируется как имя пакета и разрешается относительно ближайшего каталога node_modules . Какой модуль следует загрузить, определяется путем просмотра свойства "main" пакета package.json (аналогично CommonJS).

    • Пути глубокого импорта также разрешены относительно ближайшего каталога node_modules . Они содержат имена файлов, поэтому всегда понятно, какой модуль имеется в виду.

    Все спецификаторы, кроме пустых путей, должны относиться к реальным файлам. То есть ESM не поддерживает следующие функции CommonJS:

    • CommonJS автоматически добавляет отсутствующие расширения файлов.

    • CommonJS может импортировать каталог dir , если есть dir / package.json со свойством "main" .

    • CommonJS может импортировать каталог dir , если есть модуль dir / index.js .

    Все встроенные модули Node.js доступны по голым путям и имеют имена экспорта ESM - например:

      import * assert from assert / strict;
    import * как путь из 'path';
    
    assert.equal (
      path.join ('a / b / c', '../d'), 'a / b / d');  
    27.11.3.1 Расширения имен файлов на Node.js

    Node.js поддерживает следующие расширения файлов по умолчанию:

    • .mjs для модулей ES
    • .cjs для модулей CommonJS

    Расширение имени файла .js означает ESM или CommonJS. Какой из них настраивается через «ближайший» package.json (в текущем каталоге, родительском каталоге и т. Д.). Использование package.json таким образом не зависит от пакетов.

    В этом package.json есть свойство "type" , которое имеет два параметра:

    • "commonjs" (по умолчанию): файлы с расширением .js или без расширения интерпретируются как модули CommonJS.

    • «модуль» : файлы с расширением .js или без расширения интерпретируются как модули ESM.

    27.11.3.2 Интерпретация нефайлового исходного кода как CommonJS или ESM

    Не весь исходный код, выполняемый Node.js, исходит из файлов. Вы также можете отправить его код через stdin, --eval и --print . Параметр командной строки --input-type позволяет указать, как интерпретируется такой код:

    • Как CommonJS (по умолчанию): --input-type = commonjs
    • Как ESM: - тип входа = модуль

    27.12 Динамическая загрузка модулей через

    import () [ES2020]

    До сих пор единственным способом импорта модуля был оператор import . У этого утверждения есть несколько ограничений:

    • Вы должны использовать его на верхнем уровне модуля. То есть, например, вы не можете импортировать что-либо, находясь внутри блока.
    • Спецификатор модуля всегда фиксирован. То есть вы не можете изменить то, что вы импортируете, в зависимости от условия. И вы не можете собрать спецификатор динамически.

    Оператор import () меняет это. Давайте посмотрим на пример его использования.

    27.12.1 Пример: динамическая загрузка модуля

    Рассмотрим следующие файлы:

      библиотека / my-math.mjs
    main1.mjs
    main2.mjs  

    Мы уже видели модуль my-math.mjs :

      // Не экспортируется, частный для модуля
    function times (a, b) {
      вернуть a * b;
    }
    квадрат функции экспорта (x) {
      время возврата (x, x);
    }
    экспорт const LIGHTSPEED = 2997
  • ;
  • Вот как выглядит при использовании import () в main1.mjs :

      const dir = './lib/';
    const moduleSpecifier = dir + 'my-math.mjs';
    
    function loadConstant () {
      вернуть импорт (moduleSpecifier)
      .then (myMath => {
        const результат = myMath.LIGHTSPEED;
        assert.equal (результат, 2997
  • ); вернуть результат; }); }
  • Метод .then () является частью Promises , механизма обработки асинхронных результатов, который рассматривается далее в этой книге.

    Две вещи в этом коде раньше были невозможны:

    • Мы импортируем внутри функции (не на верхнем уровне).
    • Спецификатор модуля берется из переменной.

    Затем мы реализуем точно такую ​​же функциональность в main2.mjs , но с помощью так называемой асинхронной функции , которая обеспечивает более удобный синтаксис для Promises.

      const dir = './lib/';
    const moduleSpecifier = dir + 'my-math.mjs';
    
    асинхронная функция loadConstant () {
      const myMath = ожидание импорта (спецификатор модуля);
      const результат = myMath.LIGHTSPEED;
      assert.equal (результат, 2997
  • ); вернуть результат; }
  • Почему import () - это оператор, а не функция?

    Несмотря на то, что он работает во многом как функция, import () является оператором: чтобы разрешить спецификаторы модуля относительно текущего модуля, ему необходимо знать, из какого модуля он вызывается.Обычная функция не может получать эту информацию так же неявно, как оператор. Например, потребуется параметр.

    27.12.2 Варианты использования для импорта

    ()

    27.12.2.1 Код загрузки по запросу

    Некоторые функции веб-приложений не обязательно должны присутствовать при запуске, они могут быть загружены по запросу. Тогда import () поможет, потому что вы можете поместить такую ​​функциональность в модули - например:

      button.addEventListener ('щелчок', событие => {
      Импортировать('./dialogBox.mjs ')
        .then (dialogBox => {
          dialogBox.open ();
        })
        .catch (error => {
          /* Обработка ошибок */
        })
    });  
    27.12.2.2 Условная загрузка модулей

    Мы можем захотеть загрузить модуль в зависимости от того, выполняется ли условие. Например, модуль с полифилом, который делает новую функцию доступной на устаревших платформах:

      if (isLegacyPlatform ()) {
      импорт ('./ my-polyfill.mjs')
        .тогда(···);
    }  
    27.12.2.3 Спецификаторы вычисляемых модулей

    Для таких приложений, как интернационализация, полезно, если вы можете динамически вычислять спецификаторы модуля:

      импорт (`messages _ $ {getLocale ()}. Mjs`)
      .тогда(···);  

    27.13

    import.meta - метаданные для текущего модуля [ES2020]

    Объект import.meta содержит метаданные для текущего модуля.

    27.13.1

    import.meta.url

    Важнейшее свойство импорта .meta - это .url , который содержит строку с URL-адресом текущего файла модуля - например:

      'https://example.com/code/main.mjs'  
    27.13.2

    import.meta.url и class URL

    Class URL доступен через глобальную переменную в браузерах и на Node.js. Вы можете ознакомиться с его полной функциональностью в документации по Node.js. При работе с import.meta.url особенно полезен его конструктор:

      новый URL (ввод: строка, база ?: строка | URL)  

    Параметр input содержит URL-адрес для анализа.Он может быть относительным, если указан второй параметр base .

    Другими словами, этот конструктор позволяет нам разрешить относительный путь по базовому URL:

     > новый URL ('other.mjs', 'https://example.com/code/main.mjs').href
    https://example.com/code/other.mjs
    > новый URL ('../ other.mjs', 'https://example.com/code/main.mjs').href
    https://example.com/other.mjs  

    Таким образом мы получаем экземпляр URL , который указывает на файл data.txt , который находится рядом с текущим модулем:

      const urlOfData = новый URL ('data.txt', import.meta.url);  
    27.13.3

    import.meta.url на Node.js

    На Node.js import.meta.url всегда представляет собой строку с файлом : URL-адрес - например:

      'файл: ///Users/rauschma/my-module.mjs'  
    27.13.3.1 Пример: чтение файла-брата модуля

    Многие операции файловой системы Node.js принимают либо строки с путями, либо экземпляры URL .Это позволяет нам читать родственный файл data.txt текущего модуля:

      импорт * как fs из 'fs';
    function readData () {
      // data.txt находится рядом с текущим модулем
      const urlOfData = новый URL ('data.txt', import.meta.url);
      вернуть fs.readFileSync (urlOfData, {кодировка: 'UTF-8'});
    }  
    27.13.3.2 Модуль

    fs и URL-адреса

    Для большинства функций модуля fs мы можем обращаться к файлам через:

    • Пути - в строках или экземплярах Buffer .
    • URL-адресов - в экземплярах URL (с протоколом файл: )

    Дополнительную информацию по этой теме см. В документации API Node.js.

    27.13.3.3 Преобразование между файлом

    : URL-адресов и путей

    Модуль Node.js url ​​ имеет две функции для преобразования между файлами: URL и пути:

    • fileURLToPath (url: URL | string): string
      Преобразует файл : URL в путь.
    • pathToFileURL (путь: строка): URL-адрес
      Преобразует путь в файл : URL-адрес .

    Если вам нужен путь, который можно использовать в локальной файловой системе, то свойство .pathname из экземпляров URL не всегда работает:

      assert.equal (
      новый URL ('file: ///tmp/with%20space.txt') .pathname,
      '/tmp/with%20space.txt');  

    Следовательно, лучше использовать fileURLToPath () :

      import * как url ​​из 'url';
    утверждать.равный(
      url.fileURLToPath ('file: ///tmp/with%20space.txt'),
      '/ tmp / с пробелом .txt'); // результат на Unix  

    Точно так же pathToFileURL () делает больше, чем просто добавляет 'file: //' к абсолютному пути.

    27.14 Полифиллы: эмуляция собственных функций веб-платформы (дополнительно)

    Бэкенды тоже имеют полифиллы

    Этот раздел посвящен фронтенд-разработке и веб-браузерам, но аналогичные идеи применимы и к бэкэнд-разработке.

    Polyfills помогает с конфликтом, с которым мы сталкиваемся при разработке веб-приложения на JavaScript:

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

    Учитывая функцию веб-платформы X:

    • Полифилл для X - это фрагмент кода. Если он выполняется на платформе, которая уже имеет встроенную поддержку X, он ничего не делает.В противном случае функция становится доступной на платформе. В последнем случае функция полифилла (по большей части) неотличима от нативной реализации. Для этого полифилл обычно вносит глобальные изменения. Например, он может изменять глобальные данные или настраивать загрузчик глобального модуля. Полифиллы часто упаковываются в виде модулей.
      • Термин polyfill был придуман Реми Шарпом.
    • Спекулятивный полифилл - это полифилл для предлагаемой функции веб-платформы (которая еще не стандартизирована).
      • Альтернативный термин: prollyfill
    • Реплика X - это библиотека, которая воспроизводит API и функциональность X локально. Такая библиотека существует независимо от собственной (и глобальной) реализации X.
      • Реплика - новый термин, введенный в этом разделе. Альтернативный термин: ponyfill
    • Существует также термин шайба , но для него нет общепринятого определения.Часто это примерно то же самое, что polyfill .

    Каждый раз, когда запускается наше веб-приложение, оно должно сначала выполнять все полифиллы для функций, которые могут быть доступны не везде. Впоследствии мы можем быть уверены, что эти функции доступны изначально.

    27.14.1 Источники раздела

    Понимание module.exports и экспорта в Node.js

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

    В этой статье я исследую, как работать с модулями в Node.js, уделяя особое внимание тому, как их экспортировать и использовать.

    Различные форматы модулей

    Поскольку изначально в JavaScript не было концепции модулей, со временем появилось множество конкурирующих форматов.Вот список основных, о которых следует знать:

    • Формат определения асинхронного модуля (AMD) используется в браузерах и использует функцию define для определения модулей.
    • Формат CommonJS (CJS) используется в Node.js и использует , требуется модуль и .exports для определения зависимостей и модулей. Экосистема npm построена на этом формате.
    • Формат модуля ES (ESM). Начиная с ES6 (ES2015), JavaScript поддерживает собственный формат модуля.Он использует ключевое слово export для экспорта общедоступного API модуля и ключевое слово import для его импорта.
    • Формат System.register был разработан для поддержки модулей ES6 в ES5.
    • Формат универсального определения модуля (UMD) можно использовать как в браузере, так и в Node.js. Это полезно, когда модуль необходимо импортировать несколькими различными загрузчиками модулей.

    Имейте в виду, что эта статья касается исключительно формата CommonJS , стандарта в Node.js. Если вы хотите читать в любом другом формате, я рекомендую эту статью автора SitePoint Юргена Ван де Моэра.

    Требуется модуль

    Node.js поставляется с набором встроенных модулей, которые мы можем использовать в нашем коде, не устанавливая их. Для этого нам нужно потребовать модуль, использующий ключевое слово require , и присвоить результат переменной. Затем это можно использовать для вызова любых методов, которые предоставляет модуль.

    Например, чтобы вывести список содержимого каталога, вы можете использовать модуль файловой системы и его метод readdir :

      const fs = require ('fs');
    const folderPath = '/ home / jim / Desktop /';
    
    фс.readdir (folderPath, (err, files) => {
      files.forEach (file => {
        console.log (файл);
      });
    });
      

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

    Создание и экспорт модуля

    Теперь давайте посмотрим, как создать наш собственный модуль и экспортировать его для использования в других частях нашей программы. Начните с создания файла user.js и добавления следующего:

      const getName = () => {
      вернуть «Джим»;
    };
    
    экспорт.getName = getName;
      

    Теперь создайте файл index.js в той же папке и добавьте это:

      const user = require ('./ user');
    console.log (`Пользователь: $ {user.getName ()}`);
      

    Запустите программу, используя node index.js , и вы должны увидеть следующий вывод на терминал:

      Пользователь: Джим
      

    Итак, что здесь произошло? Что ж, если вы посмотрите на файл user.js , вы заметите, что мы определяем функцию getName , а затем используем ключевое слово exports , чтобы сделать ее доступной для импорта в другом месте.Затем в файле index.js мы импортируем эту функцию и выполняем ее. Также обратите внимание, что в операторе require к имени модуля добавляется префикс ./ , поскольку это локальный файл. Также обратите внимание, что нет необходимости добавлять расширение файла.

    Экспорт нескольких методов и значений

    Таким же образом можно экспортировать несколько методов и значений:

      const getName = () => {
      вернуть «Джим»;
    };
    
    const getLocation = () => {
      вернуть «Мюнхен»;
    };
    
    const dateOfBirth = '12.01.1982 ';
    
    export.getName = getName;
    export.getLocation = getLocation;
    export.dob = dateOfBirth;
      

    А в index.js :

      const user = require ('./ user');
    console.log (
      `$ {user.getName ()} живет в $ {user.getLocation ()} и родился в $ {user.dob}. '
    );
      

    Приведенный выше код дает следующее:

      Джим живет в Мюнхене, родился 12.01.1982.
      

    Обратите внимание, что имя, которое мы даем экспортируемой переменной dateOfBirth , может быть любым, что нам нравится (в данном случае dob ).Оно не обязательно должно совпадать с исходным именем переменной.

    Варианты синтаксиса

    Я также должен упомянуть, что можно экспортировать методы и значения по мере продвижения, а не только в конце файла.

    Например:

      exports.getName = () => {
      вернуть «Джим»;
    };
    
    export.getLocation = () => {
      вернуть «Мюнхен»;
    };
    
    exports.dob = '12 .01.1982 ';
      

    И благодаря назначению деструктуризации мы можем выбрать то, что мы хотим импортировать:

      const {getName, dob} = require ('./Пользователь');
    console.log (
      `$ {getName ()} родился $ {dob}. '
    );
      

    Как и следовало ожидать, это логи:

      Джим родился 12.01.1982.
      

    Экспорт значения по умолчанию

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

      класс Пользователь {
      конструктор (имя, возраст, адрес электронной почты) {
        это.name = name;
        this.age = возраст;
        this.email = электронная почта;
      }
    
      getUserStats () {
        return `
          Имя: $ {this.name}
          Возраст: $ {this.age}
          Электронная почта: $ {this.email}
        `;
      }
    }
    
    module.exports = Пользователь;
      

    А в index.js :

      const User = require ('./ user');
    const jim = новый пользователь ('Джим', 37, '[email protected]');
    
    console.log (jim.getUserStats ());
      

    Приведенный выше код регистрирует это:

      Имя: Джим
    Возраст: 37
    Электронная почта: jim @ example.ком
      

    В чем разница между

    module.exports и export ?

    Путешествуя по сети, вы можете встретить следующий синтаксис:

      module.exports = {
      getName: () => {
        вернуть «Джим»;
      },
    
      getLocation: () => {
        вернуть «Мюнхен»;
      },
    
      доб: '12. 01.1982 ',
    };
      

    Здесь мы назначаем функции и значения, которые хотим экспортировать, в свойство exports в модуле - и, конечно же, это отлично работает:

      const {getName, dob} = require ('./Пользователь');
    console.log (
      `$ {getName ()} родился $ {dob}. '
    );
      

    Это регистрирует следующее:

      Джим родился 12.01.1982.
      

    Так в чем же разница между module.exports и export ? Один просто удобный псевдоним для другого?

    Ну, вроде бы, но не совсем…

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

      консоль.журнал (модуль);
      

    Это дает:

    Модуль

      {
      я бы: '.',
      экспорт: {},
      родитель: нуль,
      имя файла: '/home/jim/Desktop/index.js',
      загружено: ложь,
      дети: [],
      пути:
       ['/ home / jim / Desktop / node_modules',
         '/ home / jim / node_modules',
         '/ home / node_modules',
         '/ node_modules']}
      

    Как видите, модуль имеет свойство exports . Добавим к нему что-нибудь:

     
    export.foo = 'foo';
    консоль.журнал (модуль);
      

    Это выводит:

    Модуль

      {
      я бы: '.',
      экспорт: {foo: 'foo'},
      ...
      

    Присвоение свойств экспорту также добавляет их к module.exports . Это связано с тем, что (по крайней мере, изначально) exports является ссылкой на модуль module.exports .

    Так что же мне использовать?

    Поскольку module.exports и экспортируют , оба указывают на один и тот же объект, обычно не имеет значения, какой вы используете.Например:

      exports.foo = 'foo';
    module.exports.bar = 'бар';
      

    Этот код приведет к тому, что экспортируемый объект модуля будет {foo: 'foo', bar: 'bar'} .

    Однако есть нюанс. Независимо от того, что вы назначаете module.exports , это то, что экспортируется из вашего модуля.

    Итак, возьмем следующее:

      exports.foo = 'foo';
    module.exports = () => {console.log ('бар'); };
      

    Это приведет к экспорту только анонимной функции.Переменная foo будет проигнорирована.

    Если вы хотите больше узнать о различиях, я рекомендую эту статью.

    Заключение

    Модули

    стали неотъемлемой частью экосистемы JavaScript, позволяя нам составлять большие программы из более мелких частей. Я надеюсь, что эта статья дала вам хорошее представление о работе с ними в Node.

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *