Рубрики
Без рубрики

Понимание систем разрешения модуля JavaScript с DinoSaurs

Узнайте разницу между общими системами разрешения модуля с примерами кода и помогите наших доисторических друзей.

Автор оригинала: Elliot Plant.

Модули делают Code Cleaner, более многоразовый и более весело работать с. Они позволяют вам отделить ваш JavaScript на отдельные файлы, и они защищают вас и всех остальных в вашей команде от Muddying по глобальной области. Также стоит учитывать тот факт, что динозавры не использовали модули и теперь они вымерли.

Чтобы воспользоваться модулями в Modern ES6 JavaScript, просто используйте Импорт и от Ключевые слова:

import { pterodactly } from 'dinotopia';

Это просто работает! Как-то JavaScript знает, как найти Pterodactly Экспорт в Динотопия Модуль и вписывайтесь вместе легко. Но как?

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

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

Теперь, прежде чем закрыть эту вкладку, потому что вы думали, что это был пост JavaScript, и вы только что увидели Word TeampScript, я хочу заверить вас, что на странице не будет никакого нечего JavaScript. Мы просто собираемся воспользоваться вариантами компилятора Systemscript, чтобы быстро превратить код JavaScript в различные типы модулей, которые используются сегодня.

Немного истории

Вернуться в доисторические дни 2014 года, модули не существовали в стандарте Ecmascript (JavaScript). Многие библиотеки, такие как jQuery, поставили бы объект или функцию в глобальной области как способ экспорта их функциональности. Это решение, как и все, что полагается на глобальную область, является плохой кодовым запахом и похоже на метеор, размер Аризоны, урвающейся к обращенному заливу Мексики вашей кодовой базы.

Оформление художника использования глобального пространства имен, вежливости Wikimedia.org

jquery это метеор

Библиотеки, такие как узлы, добавленные модульные функциональные возможности, которые могут быть доступны с Module.exports и требуется , но не было не договоренной отрасли.

Вы увидите, что некоторые из этого наследие (и путаница), сияющие к тому, как сегодня используются модули.

Состояние отрасли

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

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

Чтобы решить разрешение модуля в браузере, создан стандарт определения асинхронного модуля (AMD). Библиотека AMD Ebrance.js используется множеством интерфейсных библиотек, поскольку оно намного быстрее, чем Commonjs, но это, к сожалению, создало два разных стандарта.

Чтобы решить проблему конкурирующих стандартов, был создан стандарт универсального модуля (UMD). Этот работает как в браузере, так и на сервере, используя Commentjs или AMD в зависимости от того, что доступно.

Как стандарты воспроизводятся, вежливость XKCD.com

Стандарты beget Стандарты

Поскольку JavaScript стал более сложным, SystemJS вступила в сцену как более полное разрешение модуля, которая может импортировать все стандартные типы модулей JavaScript, а также «глобальные» модули в его собственное пространство имен. У него есть хорошие колокола и свистки, которые не имеют других систем, но принимает немного конфигурации для настройки в вашем проекте.

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

Почему typeycript?

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

Это означает, что мы можем написать какой-то код JavaScript, компилируйте его с помощью Typeycript Compiler, используя каждый из различных систем разрешений модуля и получить фрагмент кода JavaScript для каждого стандарта.

Видеть? Здесь ничего не происходит.

Наш код

Мы будем использовать этот фрагмент JavaScript как способ понять, как выглядит каждая система разрешения модуля:

// Imports tRex as the 'default' export of "./best-dino"
import tRex from "./best-dino";

// Imports pterodactly as a named export of "./flying-dinos"
import { pterodactly } from "./flying-dinos";

// Uses the two imports and exports the result as a named const
export const moreAwesome = tRex.awesomeness + pterodactly.screech;

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

Обратите внимание, что это полностью JavaScript, без какого-либо типографии.

Commonjs.

Давайте установим компилятор Tymdercript для использования формата разрешения модуля Commonjs. Весь выход мы получаем:

exports.__esModule = true;
var best_dino_1 = require("./best-dino");
var flying_dinos_1 = require("./flying-dinos");
exports.moreAwesome = best_dino_1["default"].awesomeness + flying_dinos_1.pterodactly.screech;

Во-первых, мы видим, что Commonjs предполагает, что будет называется объект Экспорт Доступно в объеме файла. Библиотеки, такие как узел, убедитесь, что это возможно, но Экспорт не зарезервированное слово в Ecmascript. Попробуйте печатать Экспорт , модуль и стегозавр В консоль ваших инструментов Chrome dev, и вы увидите, что JavaScript не имеет представления, что кто-либо из них (если вы не строите веб-сайт Dino Twiz, и вы поставили Stegosaurus в глобальном масштабе).

Затем мы видим, что мы устанавливаем __esmodule Собственность Экспорт к правда Отказ Это просто говорит о любой системе, которая импортирует этот файл, который он просто импортировал модуль. Если эта опция выключена, некоторые системы разрешения модуля предполагают, что этот файл поместит объект в глобальную область и выполнит его, не пытаясь получить какой-либо из его экспорта напрямую.

Далее мы видим, что Commonjs использует требуется , который также не в стандарте Ecmascript. Commonjs полагается на узле или другой библиотеке, чтобы определить требуется функция. Если вы заинтересованы в том, как требуется Работает под капотом, проверить Фред К Шотт Отличный пост об этом. Важная часть для нас это то, что требуется Принимает FilePath в качестве строкового аргумента и синхронно возвращает значение, которое мы храним в переменной Best_dino_1 Отказ

Подождите, что случилось с Trex и Pterodactly ? Typeyctry повернул каждый из импорта в свои собственные объекты имен. В следующей строке мы видим, что Trex был заменен Best_dino_1 [«По умолчанию»] , так как мы импортировали Trex Как экспорт по умолчанию из “./best-dino”.

Наконец, экспортирует Commonjs с Экспорт Объект, используя имя нашей экспортируемой переменной Moreawesome Как название недвижимости на Экспорт Отказ

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

Amd.

Использование настройки модуля AMD на TypeScript Compiler, наш код становится:

define(["require", "exports", "./best-dino", "./flying-dinos"], function (require, exports, best_dino_1, flying_dinos_1) {
    exports.__esModule = true;
    exports.moreAwesome = best_dino_1["default"].awesomeness + flying_dinos_1.pterodactly.screech;
});

AMD выглядит совсем отличается от того, что мы вложили, но мы увидим, что это не все, что отличается от Commonjs.

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

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

Самая большая разница между Commonjs и AMD в том, что Определить Метод обернуть наш модуль в функции обратного вызова, что позволяет нам загружать наши зависимости асинхронно таким образом, чтобы было бы невозможно только с требуется метод. В веб-браузере это позволяет библиотеку AMD, как require.js (да, имя супер запутана) Запросить все зависимости модуля сразу вместо того, чтобы дождаться каждой зависимости завершить загрузку, прежде чем запрашивать следующую. Это экономит время и делает ваш Дино-викторина Сайт загружен быстрее.

Umd.

Мы будем использовать настройку модулей UMD в нашем компиляторе, чтобы получить новый пакет кода:

(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./best-dino", "./flying-dinos"], factory);
    }
})(function (require, exports) {
    exports.__esModule = true;
    var best_dino_1 = require("./best-dino");
    var flying_dinos_1 = require("./flying-dinos");
    exports.moreAwesome = best_dino_1["default"].awesomeness + flying_dinos_1.pterodactly.screech;
});

WHOA. Как три линии JavaScript превращаются в это гнездо VeloCiraptor? Посмотрим, сможем ли мы выяснить, что здесь происходит.

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

function myModuleCreationFunction(require, exports) {
    exports.__esModule = true;
    var best_dino_1 = require("./best-dino");
    var flying_dinos_1 = require("./flying-dinos");
    exports.moreAwesome = best_dino_1["default"].awesomeness + flying_dinos_1.pterodactly.screech;
};

function createModuleWithCommonJsOrAmd(factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var createdModule = factory(require, exports);
        if (createdModule !== undefined) {
          module.exports = createdModule;
        }
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./best-dino", "./flying-dinos"], factory);
    }
}

createModuleWithCommonJsOrAmd(myModuleCreationFunction);

Теперь мы можем видеть, что существуют две основные части к стратегии разрешения UMD. Первый – это функция создания модуля или «фабричная функция». Эта функция выглядит почти точно так же, как вывод Commonjs.

Вторая часть – это функция, которая принимает решение о том, следует ли использовать Commonjs или AMD. Это делает это путем первой проверки, чтобы увидеть, если Commonjs Module.exports Объект существует, и если это не удается, он проверяет, если AMD Определить функция существует. Таким образом, UMD позволяет вам использовать Commonjs и AMD для разрешения модуля без необходимости много думать. Это может быть полезно для проекта, который разделяет код между клиентом и сервером.

Systemjs.

Давайте использовать Tymdercript для создания модуля SystemJS:

System.register(["./best-dino", "./flying-dinos"], function (exports_1, context_1) {
    var __moduleName = context_1 && context_1.id;
    var best_dino_1, flying_dinos_1, moreAwesome;
    return {
        setters: [
            function (best_dino_1_1) {
                best_dino_1 = best_dino_1_1;
            },
            function (flying_dinos_1_1) {
                flying_dinos_1 = flying_dinos_1_1;
            }
        ],
        execute: function () {
            exports_1("moreAwesome", moreAwesome = best_dino_1["default"].awesomeness + flying_dinos_1.pterodactly.screech);
        }
    };
});

Сначала SystemJs выглядит совершенно отлично от всего, что мы видели до этого момента и уродливого, чем указысавшего:

Может быть, уродливый динозавр, вежливость одной из моих сайтов избранного, Dinosaurpictures.org

Масиаказавр

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

Как AMD, Systemjs обернет наш весь модуль в функциональном звонке – звонок Система. Регистрация – и проходит как параметры в списке зависимостей и функция для выполнения, когда эти зависимости разрешаются. Также, как AMD, это позволяет Systemjs разрешит эти зависимости асинхронно.

Название функции SystemJS использует, Регистрация , рассказывает нам немного больше о том, как работает система (каламбур). Регистрация подразумевает, что результатом выполнения зависимости будет как-то хранить SystemJS, что позволяет вещам, подобным кэшированию и горячей перезагрузке. Хотя они доступны с другими методами разрешения модуля, они оптимизированы в SystemJS.

Далее мы видим, что наша функция обратного вызова немного больше, чем раньше. Он объявляет некоторые переменные в объеме, который доступен для некоторых Сеттер функции, а также Выполнить функция. Это похоже на немного излишек по сравнению с Commonjs, AMD и даже UMD, но позволяет SystemJS перезагрузить модуль без необходимости перезапускаться на всей графике зависимости, просто выполняя новый модуль и вызов соответствующей функции в Соседниты Отказ

Наконец, наша функция Execute довольно похожа на заводскую функцию в UMD. Это чуть более четко о названии экспорта. Эта функция может вызвать Systemjs один раз, и экспорт может быть сохранен и быстро доступен в другие модули.

ES6

Поскольку наш вход был модулем ES6, вывод из компилятора TypeScript должен быть довольно похожим:

import tRex from "./best-dino";
import { pterodactly } from "./flying-dinos";
export var moreAwesome = tRex.awesomeness + pterodactly.screech;

Ага. Единственное отличие в том, что Tymdercript повернул наш Const в var Отказ

Но так как я не объяснил их ранее, стоит говорить о Импорт , от и Экспорт Отказ С помощью ES6/ES2015 все зарезервированные слова. Ваш интерпретатор JavaScript, будь то браузер, узел или что-то еще, что может запустить ES6, понимает их в результате чего. Спецификация ничего не говорит о кэшировании импортированных модулей, но большинство браузеров и узлов достаточно умны, чтобы справиться с этим.

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

Для большинства переводчиков JavaScript эта функция была самым сложным для реализации и заняла все больше всего из всех новых спецификаций ES6. Если вы действительно хотите понять, как Импорт Работает под капотом, взгляните на модуль парсера в узле C ++ Источник И застрелить мне сообщение, если вы найдете что-нибудь интересное.

Заключение

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

Если вы строете для веб-клиента, вы можете рассмотреть полностью удалять разделение модуля из вашего производственного кода. Если вы подключите все ваши зависимости с Grunt, Gulp или WebPack, вы помешаете браузеру изготовить несколько запросов, потому что весь ваш JavaScript содержится в одном файле. Бандарь дает вам скоростное повышение и предотвращает головные боли с модулями в клиенте, но вам придется выбрать стратегию разрешения модулей для вашего Bundler.