Битва JS-модулей

JavaScript

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

Давайте, детки, проведите небольшой тест:CommonJS·AMD·CMD·UMD·ES6, со многими из этих спецификаций модулей вы знакомы?

Примечание: Данная статья представляет собой субъективно написанное автором веселое офлайн-познание, которое может отличаться от истории становления реального модуля.

корень всего

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

Рождение модуля

Так родился JS-модуль, термин любви-ненависти. Сам JS не имеет концепции модулей в своем дизайне.Позже, чтобы сделать JS мощным языком, лидеры отрасли продемонстрировали свои способности и установили спецификацию под названием CommonJS для реализации чего-то, называемого модулями. К сожалению, большинство браузеров не поддерживают его и могут использоваться только для nodejs, поэтому CommonJS начал разделять и видоизменять модуль с именем спецификация AMD, который можно использовать на стороне браузера, и поскольку AMD далека от спецификации CommonJS, AMD установила свой собственный портал, а также представила инфраструктуру requireJS для реализации и продвижения спецификаций AMD. Поскольку AMD и CommonJS настолько разные и используются в разных средах, для совместимости с обеими платформами появился UMD, но я думаю, что это просто полифилл, совместимый с обеими платформами. В настоящее время сторонники CommonJS считают, что браузер также может реализовать спецификацию CommonJS, поэтому они внесли небольшое изменение в спецификацию CMD и запустили фреймворк seajs. Когда AMD и CMD ссорились, ECMAScript6 предоставил JS функцию загрузки модулей, ES6 сказал: «Не спорьте, модули JS имеют собственный синтаксис».

истинная норма

Среди многих спецификаций только импорт/экспорт CommonJS и ES6 являются реальными спецификациями, а остальные являются смоделированными средами, использующими существующие поддерживаемые методы JS для реализации соответствующих спецификаций.

Что касается того, почему импорт/экспорт CommonJS и ES6 являются реальными спецификациями? Потому что их спецификация может быть реализована только в том случае, если их синтаксис изначально поддерживается.

Для CommonJS среда выполнения должна иметь поддержку require и module.exports для запуска. Это основная причина, по которой браузерам некуда деваться с CommonJS.

Что касается ES6, потеря поддержки ключевых слов импорта и экспорта равна нулю. Например, nodejs не поддерживает импорт и экспорт. Очевидно, что nodejs поддерживает другие синтаксисы ES6. Почему так недружелюбно импортировать и экспортировать? Автор считает, чтоnodejs是为了实现commonJS的规范,因此不能接受ES6的模块扰乱nodejs的模块规范.

Так что модули CommonJS и ES6 — это настоящие спецификации.

Что касается модулей CommonJS и ES6, автор когда-то писал о них статью, поэтому здесь я не буду вдаваться в подробности.Прочитайте и поймите загрузку модуля CommonJS.

О принципе браузерной реализации модулей CommonJS

Поскольку в браузере отсутствуют два ключевых слова CommonJS, модуль не вмещается, создайте модульную среду. использоватьdefineЭтот метод имитирует функцию в среде CommonJS, предоставляяrequireа такжеmodule.exportМетоды. Модули, будь то seajs или requirejs, реализуются путем имитации среды посредством определения.

AMD сама по себе

Раньше автор собирал настольный компьютер своими руками, выбирая видеокарту, я колебался между картой А и картой N, а потом решил выбрать карту А, потому что карта А дешевле. Карта A здесь относится к AMD, так связана ли она здесь с AMD JS? не относится! Просто потому, что аббревиатура модуля AMD для JS совпадает с названием AMD в США, это просто красивое совпадение.

Полное название AMDAsynchronous Module Definition, китайское название异步模块定义В отличие от требований Commonjs, он также загружен после запроса, а AMD работает для загрузки всех потенциально необходимых пакетов, что является высокопроизводительным, как для того, используется ли он, чтобы рассмотреть его. Есть в пределах объема. TreeJS является представителем AMD:

Крит от AMD:

define("module1",function(require) {
    'use strict';
    console.log("cccc")
});
define("module2",function(require) {
    'use strict';
    console.log("aaaa")
    if(false){
        console.log(require("module1"))
    }
    console.log("bbbb")
});

require(["module2"])

распечатать сейчасcccc,aaaa,bbbb, видно, что AMD загружает все модули до того, как модули будут выполнены, поэтому у AMD также есть способ записи всех зависимых модулей в заголовок.

define("module1",function(require) {
    'use strict';
    console.log("cccc")
});
define("module2",[module1],function(module1) {
    'use strict';
    console.log("aaaa")
    if(false){
        console.log(require("module1"))
    }
    console.log("bbbb")
});

Взгляните на исходный код requirejs:

У requirejs есть два способа получения зависимостей: один — конфигурация, другой — использование регулярных выражений для соответствия всему содержимому require, а затем добавление зависимостей. При вызове текущего модуля он сначала проверяет, запущены ли зависимые модули.

cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,

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

Кэшировать результаты после выполнения

defined[id] = exports;

Для второго выполнения сначала проверьте, существует ли оно уже, и если да, то не повторяйте выполнение.

function callGetModule(args) {
    //Skip modules already defined.
    if (!hasProp(defined, args[0])) {
        getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]);
    }
}

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

req.createNode = function (config, moduleName, url) {
    var node = config.xhtml ?
            document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
            document.createElement('script');
    node.type = config.scriptType || 'text/javascript';
    node.charset = 'utf-8';
    node.async = true;
    return node;
};

Итак, что же такое существование UMD?

Первый раз столкнулся с UMD в упаковке webpack.Хотелось сгенерировать библиотеку.Вариантов много,CommonJS,amd,umd. В то время я был немного в замешательстве, что такое UMD? Сам того не подозревая, появилась очередная спецификация модуля, от которой у автора сильно закружилась голова.

Взгляд из вебпака:

output: {
    path: path.join(__dirname),
    filename: 'index.js',
    libraryTarget: "umd",//此处是希望打包的插件类型
    library: "Swiper",
}

Взгляните на упакованный эффект:

!function(root,callback){
"object"==typeof exports&&"object"==typeof module?//判断是不是nodejs环境
    module.exports=callback(require("react"),require("prop-types"))
    :
    "function"==typeof define&&define.amd?//判断是不是requirejs的AMD环境
        define("Swiper",["react","prop-types"],callback)
        :"object"==typeof exports?//相当于连接到module.exports.Swiper
            exports.Swiper=callback(require("react"),require("prop-types"))
            :
            root.Swiper=callback(root.React,root.PropTypes)//全局变量
}(window,callback)

Такой полифилл мгновенно совместим с CommonJS, AMD и глобальным модулем. Это UMD, а не спецификация, это совместимость, полифилл, поддержка множественных спецификаций модулей.

где ЦМД?

Зоркие друзья должны были обнаружить, что CMD нигде нет, и в пакете webpack нет опции для CMD-модулей. CMD фактически соответствует спецификации CommonJS, а затем преобразует ее так, чтобы она поддерживала спецификацию на стороне браузера.

Поговорим об основных отличиях его от AMD:

Порядок выполнения контента, импортированного по ключевому слову require. AMD — это концепция, основанная на ранней загрузке, в то время как CMD — это синхронное выполнение, а текущий модуль выполняется после возникновения требования.

define("c",function(require, exports, module) {
    console.log("bbb")
});
define("b",function(require, exports, module) {
    console.log("aaa")
    require("c")
    console.log("ccc")
});
seajs.use("b")

Это печатает aaa, bbb и ccc. Выполняйте код в том порядке, в котором он появляется.

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

if (!data.debug) {
    head.removeChild(node)
}

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

постскриптум

Позаимствовав фразу Лу Синя: «В мире нет дороги, и если будет больше людей, идущих, она станет дорогой». То же самое верно и для спецификации JS MODUDLE, которая является решением по умолчанию, когда ее использует больше людей.

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

Справочный адрес

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

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