Разное

Export default: 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 в другом файле (скрипте)).

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

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

Касательно экспорта по умолчанию (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

Почему я перестал использовать экспорт по умолчанию в моих JavaScript-модулях | by Zhenya Telegin | devSchacht

Перевод блог-поста Nicholas C. Zakas: Why I’ve stopped exporting defaults from my JavaScript modules.

После нескольких лет борьбы с экспортом по умолчанию (export default), я переосмыслил свои подходы.

На прошлой неделе я написал в Твиттере кое-что, что получило немало неожиданных ответов.

Я написал это после того как понял, что большое количество моих проблем с JavaScript-модулями могут быть связанны с экспортом по умолчанию. И нет разницы, использовал ли я модули JavaScript (так называемые ECMAScript-модули) или CommonJS-модули, я все еще спотыкался об импорты из модулей с дефолтными экспортами. Я получил множество ответов на твит, многие из которых спрашивали как я пришел к такому решению. Этот пост — попытка прояснить мои мысли.

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

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

Надеюсь, что этих уточнений достаточно, чтобы избежать путаницу в оставшейся части поста.

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

Этот код экспортирует класс LinkedList но не указывает имя, которое будет использовано пользователем модуля. Предпологая, что имя файла linked-list.js, дефолтное значение можно импортировать в другой CommonJS модуль вот так:

Функция require() возвращает значение, которое я назвал LinkedList, чтобы оно совпадало с тем, что находится в файле linked-list.js, но я мог и назвать его foo или Mountain, или любой другой идентификатор.

Популярность дефолтных экспортов в CommonJS означала что модули в JavaScript были разработаны с целью поддержки этого шаблона:

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

— Дэвид Херман (David Herman) 19 июня 2014

Поэтому, в JavaScript-модулях можно использовать дефолтный экспорт:

А затем, импортировать:

Еще раз, имя переменной LinkedList — это произвольный выбор пользователя (если он не аргументированный), оно может быть как Dog, так и simphony.

CommonJS и JavaScript-модули, в добавок к дефолтным экспортам, поддерживают и именованные экспорты.

В CommonJS именованный экспорт создается при помощи добавления имени к объекту экспорта, например так:

Затем можно импортировать в другой файл:

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

В JavaScript модулях, именованный экспорт выглядит так:

А импорт — так:

В этом коде, LinkedList не может быть случайно выбранным идентификатором и должен совпадать с именованным экспортом LinkedList. Это и есть существенное отличие от CommonJS.

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

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

  1. Явное лучше неявного. Я не люблю код с секретами.
  2. Имена должны быть консистентны во всех файлах. Если что-то является яблоком (Apple) в одном файле, то я не должен называть это апельсином (Orange) в другом файле. Яблоко (Apple) всегда должно оставаться яблоком (Apple).
  3. Обрабатывайте ошибки сразу и чаще. Если чего-то может не быть, то лучше узнать об этом как можно раньше, и в лучшем случае, вернуть ошибку, которая укажет о проблеме. Я не хочу ждать, пока код закончит выполнение и потом обнаружить, что что-то пошло не так и искать проблему.
  4. Меньше решений — быстрее разработка. Каждое принимаемое решение замедляет тебя, поэтому мы считаем, что такие вещи, как правила написания кода ускоряют разработку. Я хочу решать вещи сразу и идти дальше.
  5. «Прогуляться по коду» замедляет разработку. Под «Прогуляться по коду» я подразумеваю тот случай, когда приходится останавливаться и искать что-то в другом куске кода. Иногда это необходимо, но бывает, что можно обойтись и без этого, тем самым не замедлять процесс. Я пытаюсь писать код, в котором мало таких вещей.
  6. Ненужная когнитивная нагрузка тормозит разработку. Проще говоря: чем больше деталей необходимо запомнить, чтобы писать продуктивно, тем медленнее будет разработка

Примечание

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

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

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

Чем является list в этом случае? Маловероятно, что это будет примитив, но это может быть функцией, классом или любым другим объектом. Как я могу знать наверняка? Мне потребуется «прогуляться по коду»:

  • Если я автор list.js, то я могу открыть файл и посмотреть его экспорт.
  • Если автором list.js являюсь не я, то я могу почитать документацию.

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

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

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

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

В JavaScript модулях:

В обоих видах модулей, придется явно указать, что LinkedList теперь будет называться MyList

Когда именование постоянно во всей кодовой базе, легко делать такие вещи, как:

  1. Поиск по кодовой базе с целью найти информацию об использовании
  2. Рефакторинг имени чего-либо во всей кодовой базе

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

Именованные экспорты в JavaScript-модулях имеют значимое преимущество перед дефолтными экспортами в том, что ошибка будет видна при попытке импорта того, чего не существует в модуле. Рассмотрим код:

Если LinkedList не существует в list.js, то ошибка будет видна. Кроме того, инструменты как IDE и ESLint1 могут с легкостью обнаружить отсутствующую ссылку еще до исполнения кода.

Если говорить об IDE, WebStorm может писать import за вас.2. При написании идентификатора, который не был объявлен в файле, WebStorm выполнит поиск модулей в проекте, чтобы определить является ли идентификатор именованным экспортом в другом файле. В этом случае WebStorm сделает одно из двух действий:

  1. Подчеркнет идентификатор, у которого нет определения и покажет import, который пофиксит это исправит.
  2. Автоматическое добавление правильного import‘а (если у вас включены автоматические импорты) может автоматически добавить import на основе введенного идентификатора.

Существует плагин для Visual Studio Code3, который предоставляет аналогичный функционал. Такой функционал невозможен при использовании дефолтных экспортов, потому что не существует канонического имени для вещей, которые вы хотите импортировать.

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

Как и ранее, я напоминаю, что это всего лишь моё мнение и вы можете не посчитать мои доводы убедительными. Эта запись не предназначалась для того, чтобы убедить кого-либо прекратить использовать дефолтные экспорты, а скорее для того, чтобы объяснить тем, кто заинтересовался, почему я перестану использовать дефолтные экспорты в своих модулях.

  1. esling-plugin-import import/named rule
  2. WebStorm: Auto Import in JavaScript
  3. Visual Studio Extension: Auto Import

Разделение кода – 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 с