javascript это Учитывая ES2015, инъекцию зависимостей и абстракцию библиотеки, какой должен быть мой идеальный модуль в 2016 году?




загрузчик модулей js (3)

Если нет, с одной стороны, я буду на борту для написания всех моих модулей, таких как

import A from './a.js';

var B = function(){
  //use A
};

export default B;

а затем с помощью компилятора для его создания в какой-либо формат браузера или сервера.

Однако моя проблема с вышеизложенным заключается в явной спецификации ./a.js в import .

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

  1. Как уже было сказано выше, при частом переработке модулей из проекта в проект очень вероятно, что вы не сможете поддерживать согласованный путь к этому ресурсу в своем дереве проектов. Выпечка вызова import myModule from './../../vendor/lib/dist/mod.js' как import myModule from './../../vendor/lib/dist/mod.js' в код модуля, не совсем точно import myModule from './../../vendor/lib/dist/mod.js' будущее.
  2. Помимо самого пути, указание имени файла также связывает вас. Что-то вроде этого кажется невинным:

    import $ from 'vendor/jquery.js'

    Но как насчет того дня, когда я хочу использовать Zepto вместо jQuery ? Я нашел абстракцию, особенно вокруг библиотек поставщиков, очень полезную при работе с большими кодовыми базами, упрямыми разработчиками и постоянно меняющейся экосистемой JavaScript. Возможно, мне захочется импортировать React в качестве моей библиотеки компонентов сегодня, но как насчет завтра? Более того, что, если я собираюсь использовать тот же модуль как на клиенте, так и на сервере, но мне нужны разные версии зависимой библиотеки?

Я настаиваю на надежной (но четкой и последовательной) абстракции в моих командах. Часто время абстракция принимала форму своего рода пространств имен. Я немного фантазирую об этом:

//BAD: Bakes React into my component modules
import ComponentLib from './React.js';

//GOOD: Leaves me free to use any React-like library
import ComponentLib from 'vendor.lib.component';

Где vendor.lib.component , vendor.lib.component на Java, был зарегистрирован где-то ранее.

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

Подобные вопросы привели к предложению библиотеки, которая использует спецификацию системы , например SystemJS . Затем вы можете использовать что-то вроде jspm, чтобы ввести карту модуля, чтобы получить абстракцию. Но как только я это сделаю, я пишу все свои модули по-разному:

System.import('a', function(A){
  //use 'A'
});

Это вдруг будущее? Если да, почему бы мне просто не использовать AMD? Зачем даже беспокоиться с модулями ES2015 и запуском транспиляторов, если я только вернусь к использованию асинхронного API-интерфейса загрузчика?

Больше взгляда, я не вижу большого упоминания о том, как использовать стандарт API загрузчика модулей в спецификации ES2017 .

( EDIT: Вопрос пересмотрен для соответствия стандартам ответа, не основанного на мнениях )

Учитывая все вышеизложенное, я спрашиваю сообщество - как написать модуль JavaScript, который (i) соблюдает стандарт ES2015, (ii) не ссылается на зависимый модуль по его имени файла или пути, и (iii) не полагается на обширные промежуточные инструменты / конфигурацию, которые сделали бы использование модуля с несколькими командами непомерно высокими.

-

Примечание 1 Как отмечает @zeroflagL в комментариях, спецификация явно не указывает, что модуль должен быть указан как путь, просто строка (см. ModuleSpecifier - http://www.ecma-international.org/ecma-262/ 6.0 / # таблица-41 ). Тем не менее, существует также четкая инструкция для учета циклических ссылок, что подразумевает какой-то статический анализ ( http://www.ecma-international.org/ecma-262/6.0/#sec-imports ), причем пути к файлам, казалось бы, справочный контекст выбора к этому моменту. Поэтому мы не можем обвинять спецификацию в том, что она здесь жесткая, а наоборот. В то же время бремя может быть на всех остальных для разработки более надежных реализаций import / ModuleSpecifier, которые приводят к вторичному стандарту.


Я знаю, что вы просите решение, которое вы можете использовать специально в случае JS-компилятора, но поскольку вы просите о лучшей практике, я чувствую, что мы должны учитывать полное игровое поле, в котором происходит управление зависимостями JavaScript , включая различные возможные среды хоста, такие как браузер и Node.js, интерфейсные системы упаковки, такие как Webpack, Browserify и jspm, и в некоторой степени соседние технологии, такие как HTML и HTTP / HTTP2.

История модулей ES

Существует причина, по которой вы не найдете API-интерфейс загрузчика в любой спецификации ECMA: исправление зависимостей не было завершено, когда был достигнут крайний срок для спецификации ECMA2015, и было решено, что спецификация ECMA будет описывать только синтаксис модуля и что хост среда (например, browser, node.js, JS-компилятор / транспилер) будет отвечать за разрешение модулей через их спецификаторы через hook, называемый HostResolveImportedModule , который вы сможете найти в спецификациях ECMA2015 и ECMA2017 .

Спецификация семантики, лежащей в основе шаблона модуля ES2015, теперь находится в руках WHATWG. Результатом этих усилий стали два важных события:

  1. Они определили, что модули в HTML могут быть обозначены как <script type="module"> .
  2. Существует спецификация для спецификации загрузчика , которая учитывает различные среды хоста и интерфейсные системы упаковки. Это похоже на спецификацию, которая в конечном итоге поможет ответить на ваш вопрос, но, к сожалению, она далека от завершения и не была обновлена ​​в течение довольно долгого времени.

Текущие реализации

браузер

С добавлением <script type="module"> все выглядит на месте для упрощенной реализации модулей ES6 в браузере. То, как он работает сейчас, не имеет никакого отношения к производительности (см. Этот и этот комментарий). Кроме того, он не поддерживает никаких интерфейсных упаковочных систем. Понятно, что концепция модулей должна быть расширена, чтобы ее можно было использовать вообще на производственных веб-сайтах, и поэтому поставщики браузеров не спешили внедрять ее в своих браузерах.

Node.js

Node.js - совершенно другая история, поскольку с самого начала он реализовал модули стиля CommonJS. В настоящее время поддержка модулей ES2015 отсутствует. Было сделано два отдельных предложения по включению модулей ES6 в Node.js вместе с модулями CommonJS. В этом блоге подробно обсуждаются эти предложения.

компиляторы и упаковочные системы

  • Webpack 2 поддерживает модули ES2015 . Кроме того, он может быть скорректирован для использования пользовательской зависимости .
  • Babel поддерживает модули ES2015 и имеет возможность конвертировать их в модули AMD, CommonJS, SystemJS и UMD. Похоже, что менеджеры пакетов, отличные от Webpack, поддерживают модули ES2015 через Babel.

Вывод

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

  • Вы можете поделиться своим кодом между разными средами.
  • Существует риск того, что какой-то класс разработчиков (например, сторонние разработчики) будут использовать модули ES2015, тогда как другие (например, разработчики Node.js) будут придерживаться другого решения (например, модули CommonJS). Это еще больше снизит вероятность использования кода межсетевой среды.

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

Решение

Однако, если бы у меня была задача прямо сейчас писать модули ES2015, которые компилируются в JS, я бы держался подальше от относительных путей и всегда использовал абсолютные пути от корня проекта как идентификатор модулей, которые я не вижу в качестве проблемных , Java фактически отражает пространства имен и структуру каталогов во многом таким же образом. Я бы использовал Webpack или Babel для компиляции моего источника кода, который запускается в современных средах JS.

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

//  /lib/libs.js
import jQuery from 'vendor/jquery.js'
export const $ = jQuery;

Все остальные модули затем будут импортировать $ из lib / libs.js, и вы сможете переключать библиотеки поставщиков, изменяя ссылку в одном месте.


Для меня это, по-видимому, одна из самых больших нерешенных проблем сообщества JS. В отношении управления зависимостями и зависимостей (по крайней мере, насколько мне известно) не существует оптимальной практики.

В этой ветке есть долгая дискуссия: нужна ли мне инъекция зависимостей в NodeJS или как бороться с ней? , но большинство решений, похоже, работают только для конкретных случаев и требуют изменения способа записи модулей каким-то образом. И самым шокирующим является то, что многие ответы утверждают, что вам даже не нужен DI.

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


Если вы хотите следовать передовым методам, следуйте инструкциям AirBnb JavaScript styleguide. По-моему, лучший и самый полный стиль JavaScript

https://github.com/airbnb/javascript#classes--constructors

Импортировать

Это выглядит плохо для повторного использования модулей: import myModule from './../../vendor/lib/dist/mod.js'

Публикуйте свой модуль на NPM (который также может быть приватным или самостоятельно размещенным NPM) и импортируйте, как этот import myModule from 'my-module';

В конце концов, установите NODE_PATH в качестве корневой папки и обратитесь к модулям относительно от корня.

В пакете.json

'start': 'NODE_PATH=. node index.js'

// in Windows
'start': 'NODE_PATH=. && node index.js'

Теперь импортируйте вот так:

import myModule from 'vendor/lib/dist/mod.js'

переменные

var не является частью ES6. Использование:

  • constant - когда значение переменной не изменится, также объекты и импорт. Даже если параметры объекта изменяются, он по-прежнему является константой.

  • let - когда изменяется значение переменной, т. е. for(let = i; i < 100; i++)

  • Из моего собственного опыта всегда устанавливайте const как значение по умолчанию, и только изменение let если ESLint жалуется (кстати, используйте ESLint http://eslint.org/ )

Классы

Теперь есть правильный способ определения классов в JavaScript

class B {
  constructor() {
  }
  doSomething() {
  }
}

Ваш пример обновлен:

import A from './a';

Class B {
  constructor() {
  }
  doSomething() {
  }
};

export default B;

Если вы хотите расширить A:

import A from './a';

Class B extends A{
  constructor(argumentA, argumentB) {
    super(argumentA, argumentB);
    this.paramA = argumentA;
  }
};

export default B;

Советы

  • Используйте Webpack с NPM как инструмент сборки. Не используйте Gulp или Grunt
  • Используйте Babel для перевода вашего кода (загрузчика JSX может быть недостаточно)
  • Научитесь вообще не использовать jQuery, но вместо этого выбирайте правильные полисы и инструменты для работы, которые вам нужно делать из NPM
  • Есть тонны хорошо написанных плановых репозиториев на github, поэтому украсть из лучших. Вот некоторые из них, которые я использую.
    • https://github.com/davezuko/react-redux-starter-kit
    • https://github.com/kriasoft/react-starter-kit
    • как вы можете видеть, я большой поклонник React. Также полный рабочий день профессиональный пользователь уже более двух лет.
    • Чтобы найти хороший пример кода, поиск по github, т.е. «реагировать» или «узел», затем сортировать результаты по звездам)

Мой ответ в основном таков:

Образец / библиотека, которую вы просили, это AirBnb JavaScript styleguide и забыть о jQuery





ecmascript-6