Объяснение спецификации модуля CommonJS и ES6, которое не является заголовком

JavaScript ECMAScript 6
Объяснение спецификации модуля CommonJS и ES6, которое не является заголовком

предисловие

Ты с нетерпением ждешь мира, надеюсь, у тебя нетbug. Всем привет! Я Лин Дуан.

Да, вы можете не захотеть читать заголовок этой статьи,CommonJSа такжеES6 ModulesСпецификации это все познания, а вы еще тут пишете...

плакать 😢...

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

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

текст

Прочитав эту статью, вы сможете узнать:

  • Некоторые методы написания оригинального модуля моделирования
  • Спецификация CommonJS
  • Технические характеристики AMD
  • Спецификация CMD
  • Разница между AMD и CMD
  • Спецификация модулей ES6
  • Разница между спецификацией модулей CommonJS и ES6

📒 Эта статья добавленаniubility-coding-jsКраткое изложение личного блога Лин, добро пожаловать в Star~

🌰 Адрес кейса учебника в этой статье:CommonJS-example

🔥Этот инструмент для верстки статей:mdnice, спасибо авторухудожник😊.

оригинальное письмо

В отсутствиеCommonJSа такжеES6Когда мы хотим добиться модульности, могут быть три возможных эффекта:

1. Функция — это модуль

<script>
  function m1 () {
    // ...
  }
  function m2 () {
    // ...
  }
</script>

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

2. Объект — это модуль

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

index.html

<script>
  var module1 = new Object({
    _sum: 0,
    foo1: function () {},
    foo2: function () {}
  })
</script>

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

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

В настоящее время,_sumМожет быть переписан извне:

module1._sum = 2;

3. Немедленно выполнить функцию как модуль

<script>
  var module1 = (function() {
    var _sum = 0;
    var foo1 = function () {};
    var foo2 = function () {};
    return {
      foo1: foo1,
      foo2: foo2
    }
  })();
</script>

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

На данный момент внешний код не может его прочитать_sumсейчас:

console.log(module1._sum) // undefined

Спецификация CommonJS

Я не буду делать здесь конкретное введение, я просто упомяну некоторые важные сведения и непонятные моменты.

В основном из этих четырех аспектов:

  • открытый модуль
  • эталонный модуль
  • идентификатор модуля
  • Особенности спецификации CommonJS

1. Выставление (определение) модулей

Правильная экспозиция:

Есть два способа открыть модули:

  • module.exports = {}
  • exports.xxx = 'xxx'

Например, естьm1.jsдокумент:

Первый метод воздействия:

module.exports = {
    name: 'lindaidai',
    sex: 'boy'
}

Второй способ воздействия:

exports.name = 'lindaidai';
exports.sex = 'boy'

Почему есть два способа написать это?

Я понимаю это так:moduleЭта переменная представляет весь модуль, т.е.m1.js. На самом деле этоmoduleПеременная имеет свойствоexportsДа, этоexportsСсылка на переменную, мы можем написать псевдокод:

var exports = {};
var module = {
    exports: exports
}
return module.exports

(Конечно, это всего лишь псевдокод. На самом деле вы не найдете никакого эффекта, если будете использовать его так)

Окончательный экспортmodule.exports, вместоexports.

Запутанные методы воздействия:

Если вы попробуете в своем кодеexports = { name: 'lindaidai' }, вы обнаружите, что вы не можете получить его вообще там, где он был введенnameАтрибуты.

// m1.js
exports = {
    name: 'lindaidai'
}
// test.js
const math = require('./m1.js')

console.log(m1); // {}

Выполнить на консолиnode test.js, обнаружил, что печатныйm1является пустым объектом.

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

module.exports -> {} // 指向一个空的对象
exports -> { name: 'lindaidai' } // 指向的是另一个对象

так что ты правexports = {}Никакие действия не повлияютmodule.exports.

Давайте рассмотрим несколько примеров правильного и неправильного:

// m1.js
// 1. 正确
module.exports = {
    name: 'lindaidai',
    sex: 'boy'
}

// 2. 正确
exports.name = 'lindaidai';
exports.sex = 'boy'

// 3. 正确
module.exports.name = 'lindaidai';
module.exports.sex = 'boy'

// 4. 无效
exports = {
    name: 'lindaidai',
    sex: 'boy'
}

можно увидеть

  • exports.name = xxxдаmodule.exports.name = xxxаббревиатура от.
  • exports = {}точно нетmodule.exports = {}аббревиатура от.

2. Справочные (импортные) модули

Используйте глобальные методы для ссылок на модулиrequire()Вот и все.

Примечание ⚠️ Этот глобальный методnodeметод в Ха, это неwindowниже, поэтому, если вы не выполняете никакой обработки и хотитеhtmlЕго точно нельзя использовать:

index.html:

<body>
    <script>
        var m1 = require('./m1.js')
        console.log(m1);
    </script>
</body>

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

Uncaught ReferenceError: require is not defined
	at index.html:11

а если ты в другомjsупоминается в документе (например,test.js) и выполнить в терминалеnode test.jsдоступен:

test.js:

var m1 = require('./m1.js')

console.log(m1);

Это потому, что он установлен глобально на вашем компьютереNode.js, так что вы можете играть так.

Итак, мы можем найтиrequire()этоNode.jsГлобальный метод в , не уникальный для CommonJS, CommonJS является лишь одной из многих спецификаций.

Эта спецификация позволяет нам:

  • использоватьmodule.exports = {}илиexports.name = xxxмодуль экспорта
  • использоватьconst m1 = require('./m1')модуль импорта

Примечание ⚠️:

Еще один важный момент заключается в том, чтоrequire()Параметр даже позволяет вам быть выражением.

То есть вы можете установить его как переменную:

test.js:

var m1Url = './m1.js';
var m1 = require(m1Url);

// 甚至做一些字符串拼接:
var m1 = require('./m' + '1.js');

Но следует отметить, что этот параметр может быть выражением, а не выражением.requireУникальный. потому чтоJSЯзык вызывается по значению, и параметры функции или метода будут вычисляться первыми при вызове функции или метода, поэтому, когда мы используемrequireКогда выражение передается в метод, значение выражения будет сначала вычислено, а затем передано в метод.require. (Спасибо копателюКатание на велосипедеуказал)

3. Идентификатор модуля (идентификация)

Идентификатор модуля — это то, что вы вызываете при импорте модуля.require()параметры функции.

Вы увидите, что мы часто используем это:

// 直接导入
const path = require('path');
// 相对路径
const m1 = require('./m1.js');
// 直接导入
const lodash = require('lodash');

На самом деле это потому, что модули, которые мы представляем, будут иметь разные категории, напримерpathтак оно и естьNode.jsвстроенный модуль,m1модуль пути,lodashэто то, что мы используемnpm i lodashзагрузить вnode_modulesмодуль в .

Он подразделяется на следующие три типа:

  • основной модуль (Node.jsвстроенный модуль)
  • Модули пути (модули, которые начинаются с относительного или абсолютного позиционирования)
  • пользовательский модуль (node_modulesмодуль в)

Есть три способа найти модули:

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

Процесс поиска пользовательских модулей:

Этот процесс также называетсяпуть анализа.

Теперь я поставилtest.jsДавайте изменим это:

// var m1 = require('./m1.js');

// console.log(m1);
console.log(module.paths)

Затем выполните в терминале:

node test.js

Вы обнаружите, что выводится следующий массив:

LinDaiDaideMBP:commonJS lindaidai$ node test.js
[
  '/Users/lindaidai/codes/test/CommonJS和ES6/commonJS/node_modules',
  '/Users/lindaidai/codes/test/CommonJS和ES6/node_modules',
  '/Users/lindaidai/codes/test/node_modules',
  '/Users/lindaidai/codes/node_modules',
  '/Users/lindaidai/node_modules',
  '/Users/node_modules',
  '/node_modules'
]

Упомянутый здесь поиск относится к поиску модуля, который вы сейчас используете.test.js, вы можете не увидеть никакого эффекта. Теперь давайте смоделируем тот, который мы используемnpm iУстановлена ​​функция пользовательского модуля.

Сначала я создал новый файл в корневом каталоге с именемnode_modulesпапку и создал новую папку с именемlindaidai.jsфайл для имитацииnpmУстановленные зависимости.

Структура каталога:

напиши немногоlindaidai.js:

module.exports = {
  print: function () {
    console.log('lindaidai')
  }
}
console.log('lindaidai模块:', module.paths)

затем вtest.jsимпортировать этоlindaidaiМодуль:

// var m1 = require('./m1.js');
// console.log(m1);
// console.log(module.paths)

var lindaidai = require('lindaidai');
lindaidai.print();

выполнить сейчасnode test.js, вы найдете вывод:

LinDaiDaideMBP:commonJS lindaidai$ node test.js
lindaidai模块: [
  '/Users/lindaidai/codes/test/CommonJS和ES6/commonJS/node_modules',
  '/Users/lindaidai/codes/test/CommonJS和ES6/node_modules',
  '/Users/lindaidai/codes/test/node_modules',
  '/Users/lindaidai/codes/node_modules',
  '/Users/lindaidai/node_modules',
  '/Users/node_modules',
  '/node_modules'
]
lindaidai

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

Расположение файла:

Анализ пути был введен выше 👆, но есть еще проблема, то есть суффикс (расширение) импортируемого нами модуля может быть опущен, тогдаNodeОткуда мы знаем, что мы импортировалиjsеще одинjsonШерстяная ткань? На самом деле это включает в себя позиционирование файла.

В NodeJS, если расширение файла опущено, .js, .node и .json будут добавлены к попытке.Если каталог передан, NodeJS будет рассматривать его как пакет, и он будет определен следующим образом: имя

Первый шаг — найти package.json в каталоге и использовать JSON.parse() для разбора основного поля.

На втором шаге, если файл, указанный в основном поле, по-прежнему не имеет расширения, то по очереди будут добавлены .js, .node и .json.

В-третьих, если основное поле для разработки файла не существует, или не существует Package.json, он будет загружен по умолчанию index.js, index.node, index.json файлы в этом каталоге.

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

(Краткий источник: https://zhuanlan.zhihu.com/p/27644026)

4. Особенности спецификации CommonJS

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

  • Весь код выполняется в области модуля и не загрязняет глобальную область;

  • Модуль загружается синхронно, то есть только после завершения загрузки можно выполнять следующие операции;

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

  • Вывод CommonJS — это копия значения (т. е.requireВозвращаемое значение является копией выходного значения, и изменения внутри модуля не повлияют на это значение).

(Краткий источник: https://juejin.cn/post/6844903983987834888)

Первый момент понять несложно, разве это не важная функция нашего модуля?

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

Случай синхронной загрузки:

m1.js:

console.log('我是m1模块')
module.exports = {
    name: 'lindaidai',
    sex: 'boy'
}

test.js

var m1 = require('./m1');
console.log('我是test模块');

можно увидеть,testмодуль зависит отm1, и был загружен первымm1модуль, поэтому, если я выполнюnode test.js, будут следующие результаты выполнения:

LinDaiDaideMBP:commonJS lindaidai$ node test.js
我是m1模块
我是test模块

Это также подтверждаетCommonJS, модуль загружается синхронно, то есть только после завершения загрузки можно выполнять последующие операции.

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

Дело кешируется после первого выполнения модуля:

m1.js:

var name = 'lindaidai';
var sex = 'boy';

exports.name = name;
exports.sex = sex;

test.js:

var m1 = require('./m1');
m1.sex = 'girl';
console.log(m1);

var m2 = require('./m1');
console.log(m2);

testВ зависимости отm1, но я бы дважды импортировал в негоm1, который был изменен при первом импортеm1.sexЗначение , вторично названное какm2, но результатm1а такжеm2оказалось равным:

LinDaiDaideMBP:commonJS lindaidai$ node test.js
{ name: 'lindaidai', sex: 'girl' }
{ name: 'lindaidai', sex: 'girl' }

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

Тогда некоторые друзья будут озадачены.На самом деле, вы не можете доказать это, написав так, потому что вы изменились.m1.sexтакже может повлиять на оригиналm1в модулеsexАтрибуты, это второй разm2То, что вы получаете, определенно является значением, которое было изменено.

唔...我正想证明来着呢。 потому чтоCommonJSЧетвертая особенность заключается в том, что вы можете быть хорошим решением этого вопроса.

четвертая точкаВывод CommonJS — это копия значения, то есть вы используетеrequire()Модули введены, но то, что вы измените в последнем модуле, не повлияет на ваши существующиеrequire()модуль. Давайте рассмотрим случай.

Вывод CommonJS — это копия регистра значений:

m1.js:

var name = 'lindaidai';
var sex = 'boy';
var advantage = ['handsome']

setTimeout(function () {
  sex = 'girl';
  advantage.push('cute');
}, 500)

exports.name = name;
exports.sex = sex;
exports.advantage = advantage;

test.js:

var m1 = require('./m1');
setTimeout(function () {
  console.log('read count after 1000ms in commonjs is', m1.sex)
  console.log('read count after 1000ms in commonjs is', m1.advantage)
}, 1000)

воплощать в жизньnode test.jsРезультат выполнения после этого:

LinDaiDaideMBP:commonJS lindaidai$ node test.js
read count after 1000ms in commonjs is boy
read count after 1000ms in commonjs is [ 'handsome', 'cute' ]

То есть в началеvar m1 = require('./m1')когда,m1было введено, но500msЗатем я изменил оригиналm1некоторые свойства вsexЭтот тип данных не будет существенно изменен, ноadvantageЭтот ссылочный тип использует один и тот же адрес памяти. (Такого рода отношения репликации напоминают мне о том, когда я раньше изучал наследование цепочки прототипов, оно также присутствует, оно повлияет наFather.prototypeтип ссылки включен)

(Это также еще раз доказывает, что Линь Дуодао действительноboy, даже если вы попытаетесь изменить его пол, это не сработает)

Если вы напишете здесь:

m1.js:

var name = 'lindaidai';
var sex = 'boy';
var advantage = ['handsome']

setTimeout(function () {
  sex = 'girl';
  // advantage.push('cute');
  advantage = ['cute'];
}, 500)

exports.name = name;
exports.sex = sex;
exports.advantage = advantage;

Текущий результат выполнения определенно:

LinDaiDaideMBP:commonJS lindaidai$ node test.js
read count after 1000ms in commonjs is boy
read count after 1000ms in commonjs is [ 'handsome' ]

потому что это эквивалентноm1изadvantageпереназначен.

Конечно, или если вашm1.jsЕсли возвращаемое значение является функцией, вtest.jsВы также можете получить значение после изменения, например, здесь:

var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  get counter() {
    return counter
  },
  incCounter: incCounter,
};

Потому что здесь фактически формируется замыкание, иcounterЗначение свойства - это функциональное устройство.

ОК, это в основномCommonJSХарактеристики, резюме писать не буду, было сказано в начале, но на последний пункт:Вывод CommonJS — это копия значения, это все еще будет немного неоднозначно для переменных ссылочного типа, таких как вышеadvantageЭтот пример знают все.

Технические характеристики AMD

1. Причина

описано вышеCommonJSСпецификации кажутся очень полезными, зачем другие спецификации? НапримерAMD、CMD, то они иCommonJSКаково происхождение?

Мы знаем, что концепция модульности применяется не только на стороне сервера, но и на стороне клиента.

а такжеCommonJSСпецификация не подходит для использования в клиентской (браузерной) среде, такой как приведенный выше пример, а именно:

test.js:

const m1 = require('./m1.js')
console.log(m1);

// 与m1模块无关的一些代码
function other () {}
other();

Как этот код работает в среде браузера?

  • загрузить сначалаm1.js
  • Ждатьm1.jsВыполните следующий контент после завершения загрузки

Это на самом делеОсобенности спецификации CommonJSуже упоминалось в.

Следующий контент должен подождатьm1Он будет выполнен после загрузки, еслиm1Очень медленно загружается? То есть не стать Катоном, согласно которому клиент заведомо недоброжелателен. Поскольку в случае с таким дополнением ждать, пока они закончат реализацию контента, мы можем назвать"同步加载", очевидно, здесь мы предпочитаемother()Исполнение не нужно ждатьm1Выполняется после загрузки, то есть надеемсяm1это"异步加载"да, этоAMD.

ПредставляемAMDПосмотрим раньшеCommonJSСпецификация отличается для серверной части и браузера, это поможет вам понять, почему она говоритCommonJSНе очень подходит для клиентов:

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

2. Определите и выставьте модули

С учетом вышеизложенного мы знаем, что,AMDБольшая часть причины этого заключается в том, чтобы позволить нам принятьЗагружать модули асинхронно.

Итак, теперь давайте взглянем на его введение.

AMDдаAsynchronous Module Definitionсокращение от"异步模块定义". (переднийAЭто легко запомнить, это заставило меня задуматься об этом неосознанноasyncЭто определяет модификатор для асинхронных функций)

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

На данный момент необходим еще один важный метод для определения нашего модуля:define().

На самом деле он имеет три параметра:

define(id?, dependencies?, factory)
  • id: строка, представляющая имя модуля, но необязательная.
  • зависимости: массив модулей, от которых зависит наш текущий определенный модуль. Каждый элемент в массиве представляет относительный путь зависимого модуля, и этот параметр также является необязательным.
  • factory: фабричный метод, функция, которая является конкретным содержимым модуля.

яма один:

Тогда собственно проблема.Я столько учебников прочитала,но когда захотела написать кейс,то подумала вот этоdefineпрямо какrequireЯ использовал его таким же образом и обнаружил, что консоль продолжает сообщать об ошибке:

ReferenceError: define is not defined

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

В настоящее время существуют две основные библиотеки Javascript, реализующие спецификацию AMD: require.js и curl.js.

я кислый...

пойдемrequirejsОфициальный сайт, чтобы узнать, как его использовать, так как мои дела все вNodeсреда выполнения, поэтому я используюnpm installспособ загрузки:

Я создал новую под названиемAMDпапка, какAMDкейс.

Выполнить в корневом каталоге проекта:

npm i requirejs

(осмотрелсяNPMя не видел, чтобы это работалоCDNимпортируется удаленно)

После завершения выполнения в корневом каталоге проекта были зависенные пакеты.

Теперь вы можете с удовольствием использовать его в своем проектеdefine()😊.

Давайте посмотрим на небольшой пример, я переопределяюmath.js:

math.js

define(function () {
  var add = function (a, b) {
    return a + b;
  }
  return {
    add: add
  }
})

Этот модуль прост и экспортирует функцию сложения.

(почему здесьadd: addвместо того, чтобы просто сокращать какaddШерстяная ткань? Не забывайте, что сокращение для этого свойства объекта с тем же именемES6только что вышел)

3. Справочные модули

яма два:

Хорошо👌, теперь, когда модуль можно экспортировать, давайте посмотрим, как на него ссылаться.Согласно учебнику, я вtest.jsвведен вmathмодуль и хочу позвонитьadd()метод:

test.js:

require(['math'],function(math) {
  console.log(math)
  console.log(math.add(1, 2));
})

искусное исполнение послеnode test.js.

я кислый...

Опять ошибка, стереть...

 throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
 TypeError [ERR_INVALID_ARG_TYPE]: The "id" argument must be of type string. Received an instance of Array

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

Неудобно 😣...Оказывается вышеrequire([modules], callback)Написано так это иdefineЭто всего лишь трюк, если вам действительно нужно его использовать, вы все равно должны его использовать.JavaScriptметоды в библиотеке.

Поскольку он был установлен вышеrequirejsТеперь я могу использовать его прямо здесь, и теперь я изменил его.test.jsдокумент:

var requirejs = require("requirejs"); //引入requirejs模块

requirejs(['math'],function(math) {
  console.log(math)
  console.log(math.add(1, 2));
})

Ну, сделай это сейчасnode test.jsЕго можно использовать как обычно...

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

4. Зависит от определения других модулей

можно увидетьdefineУ него есть два других параметра, первый — это имя модуля, ничего не говоря, давайте посмотрим на второй модуль, от которого он зависит.

помнить вCommonJSСпект, где мы написалиm1.js? Теперь давайте воспользуемся этим модулем, используя его какmath.jsодна из зависимостей.

m1.js:

console.log('我是m1, 我被加载了...')
module.exports = {
    name: 'lindaidai',
    sex: 'boy'
}

затем измените егоmath.js:

math.js:

define(['m1'], function (m1) {
  console.log('我是math, 我被加载了...')
  var add = function (a, b) {
    return a + b;
  }
  var print = function () {
    console.log(m1.name)
  }
  return {
    add: add,
    print: print
  }
})

Кроме того, для удобства всех модифицируем предыдущийtest.js:

var requirejs = require("requirejs"); //引入requirejs模块

requirejs(['math'],function(math) {
  console.log('我是test, 我被加载了...')
  console.log(math.add(1, 2));
  math.print();
})
function other () {
  console.log('我是test模块内的, 但是我不依赖math')
};
other();

Итак, мы видим, что зависимости по порядку:

test -> math -> m1

Если согласноAMDСпецификация модуля, загрузка модуля должна полагаться на загрузку предыдущего модуля для выполнения содержимого в функции обратного вызова, тогда мы можем представить, что когда я набираю в терминалеnode test.js, должен появиться результат:

LinDaiDaideMBP:commonJS lindaidai$ node test.js
我是test模块内的, 但是我不依赖math
我是m1, 我被加载了...
我是math, 我被加载了...
我是test, 我被加载了...
3
lindaidai

(Это, я считаю, что все должны ясно видеть зависимости друг друга 😢)

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

кислый...

 ReferenceError: module is not defined

Я посмотрел содержимое этого отчета об ошибке, он находится вm1.jsВ... Задержался на несколько секунд, прежде чем среагировать...

Поскольку он используетсяAMDнорма, то надо быть едиными до конца,m1.jsдо сих пор используется вCommonJSспецификации, конечно, нет.

Хорошо, давайте изменим егоm1.js:

m1.js:

define(function () {
  console.log('我是m1, 我被加载了...')
  return {
    name: 'lindaidai',
    sex: 'boy'
  }
})

ОК👌, на этот раз проблем нет, реализовано так, как мы и ожидали...😊.

(Конечно, насколько я знаю,requirejsтакже можно использовать вscriptОбратитесь к, а затем определите основной модуль веб-программы и т. Д., Вы можете взглянуть:

http://wuwuwu.Ruan Yifeng.com/blog/2012/11/require_is.HTML)

AMDТочки знаний .CMDБар.

Спецификация CMD

CMD (Общее определение модуля) — это спецификация, которую уважает seajs, CMD зависит от ближайшего, а затем требует ее при использовании.

Давайте посмотрим на код и почувствуем, как он работает:

define(function(require, exports, module) {
  var math = require('./math');
  math.print()
})

глядя на иAMDВроде как, да, на самом делеdefine()Параметры даже те же:

define(id?, dependencies?, factory)

Но в чем разница? Давайте посмотрим на последнийfactoryего параметры.

factoryФункция получит три параметра:

  • require

  • exports

  • module

    Эти три хорошо понятны, что соответствует предыдущемуCommonJSРазве это не:

  • require: импортировать модуль

  • exports: текущий модульexports, то есть,module.exportsсокращение для

  • module: текущий модуль

Теперь давай поговоримAMDа такжеCMDразница.

Хотя ихdefine()Параметры методов все те же, но:

  • AMDпоместит зависимые модули текущего модуля вdependenciesзагружается и входитfactoryПолучить успешно загруженные зависимости в обратном вызове
  • CMDобычно нетdependenciesзагружается, но пишетсяfactoryв использованииrequireЗагрузить зависимый модуль

Отсюда поговорка, которую мы часто видим:

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

(Ну, прочитав два раза, я все еще не совсем понимаю, ничего страшного, я расскажу об этом подробно позже)

более известный,seajs, давайте посмотрим на рекомендуемый формат записи модуля CMD:

// 所有模块都通过 define 来定义
define(function(require, exports, module) {

  // 通过 require 引入依赖
  var $ = require('jquery');
  var Spinning = require('./spinning');

  // 通过 exports 对外提供接口
  exports.doSomething = ...

  // 或者通过 module.exports 提供整个接口
  module.exports = ...

});

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

Разница между AMD и CMD

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

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

то же самое сmathМодуль должен быть загруженm1модуль.

существуетAMDВ мы бы написали:

math.js

define(['m1'], function (m1) {
  console.log('我是math, 我被加载了...')
  var add = function (a, b) {
    return a + b;
  }
  var print = function () {
    console.log(m1.name)
  }
  return {
    add: add,
    print: print
  }
})

Но дляCMD, мы бы написали:

math.js

define(function (require, exports, module) {
  console.log('我是math, 我被加载了...')
  var m1 = require('m1');
  var add = function (a, b) {
    return a + b;
  }
  var print = function () {
    console.log(m1.name)
  }
  module.exports = {
    add: add,
    print: print
  }
})

если в это времяm1.jsЕсть заявление вm1Распечатать, когда модуль загружен"我是m1, 我被加载了...".

Разница в результате выполнения:

  • AMD, сначала загрузитсяm1,"我是m1"будет выполняться первым
  • CMD"我是math"будет выполнен первым, потому что в этом вопросеconsole.log('我是math, 我被加载了...')размещенrequire('m1')передний.

Теперь разница хорошо видна.

AMDзависит от префикса,jsОчень удобно знать какой модуль загружать, т.к. он уже есть вdefineизdependenciesопределяется в параметрах, он будет загружен немедленно.

CMDЭто близлежащая зависимость. Вам нужно использовать модуль, чтобы преобразовать его в строку, чтобы проанализировать его, чтобы узнать, какие модули зависят.

Хорошо👌, давайте посмотрим резюме:

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

1. AMD учитывает предварительную зависимость и объявляет модули, от которых она зависит, при определении модулей. 2. CMD учитывает ближайшие зависимости и требует только при использовании модуля.

Спецификация модулей ES6

ES6После выхода стандартаES6 ModulesСпецификации стали основным направлением интерфейса, поэтомуimportмодуль импорта,exportИнтерфейс экспорта используется все большим количеством людей.

Ниже я также представлю из этих аспектовES6 ModulesТехнические характеристики:

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

1. модуль экспорта экспорта

Существует два способа экспорта модулей:

  • Именованный экспорт (экспорт имени)
  • Экспорт по умолчанию (пользовательский экспорт)

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

Давайте посмотрим на некоторые правильные и неправильные варианты написания:

// 以下两种为错误
// 1.
export 1;
// 2.
const a = 1;
export a;

// 以下为正确
// 3.
const a = 1;
export { a };

// 4. 接口名与模块内部变量之间,建立了一一对应的关系
export const a = 1, b = 2;

// 5. 接口名与模块内部变量之间,建立了一一对应的关系
export const a = 1;
export const b = 2;

// 或者用 as 来命名
const a = 1;
export { a as outA };

const a = 1;
const b = 2;
export { a as outA, b as outB };

Это может быть легко спутать2а также4Есть два способа написания, они очень похожи, но2Но нет.2напрямую экспортировать значение1Переменная такая же, как и в случае 1, что бессмысленно, потому что вы не сможете завершить деструктурирование, когда будете использовать ее позже.

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

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

Экспорт по умолчанию будет вexportдобавить один послеdefault:

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

// 2.
const a = 1;
export default { a };

// 3.
export default function() {}; // 可以导出一个函数
export default class(){}; // 也可以出一个类

На самом деле экспорт по умолчанию можно понимать как другую форму именованного экспорта, то естьaЭто имя свойства эквивалентно тому, что я переписал его наdefault:

const a = 1;
export defalut a;
// 等价于
export { a as default }

Поэтому мы можем использоватьconst a = 1; export default a;Экспортируйте значение таким образом.

2. модуль импорта импорта

Импорт модуля импорта соответствует функции экспорта модуля экспорта.Также существует два метода импорта модуля: именованный импорт (импорт имени) и импорт по умолчанию (импорт определения).

Смотрим на надпись:

// 某个模块的导出 moudule.js
export const a = 1;

// 模块导入
// 1. 这里的a得和被加载的模块输出的接口名对应
import { a } from './module'

// 2. 使用 as 换名
import { a as myA } from './module'

// 3. 若是只想要运行被加载的模块可以这样写,但是即使加载2次也只是运行一次
import './module'

// 4. 整体加载
import * as module from './module'

// 5. default接口和具名接口
import module, { a } from './module'

Четвертый способ написания получитmoduleвсе экспортировано в , и присвоеноmoduleэту переменную, чтобы мы могли использоватьmodule.aпроцитировать таким образомa.

3. export ... from...

На самом деле, есть другой способ написатьexportа такжеfromОбъедините это.

Например, у меня есть три модуляa、b、c.

cТеперь модуль хочет импортироватьaмодуль, но на него напрямую не ссылаютсяa, но черезbмодуль для ссылки, то вы можете подуматьbЭто должно быть написано так:

import { someVariable } from './a';

export { someVariable };

представлятьsomeVariableЗатем снова экспортируйте.

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

Таким образом, это время может быть достигнуто следующим образом:

export { someVariable } from './a';

Однако в этом подходе есть одна оговорка:

  • Этот способ не добавляет данные в область действия модуля агрегации, то есть вы не можетеb) используется вsomeVariable.

4. Особенности спецификации модулей ES6

Кратко опишите его особенности:

  • выходное использованиеexport
  • ввод с использованиемimport
  • можно использоватьexport...from...этот способ письма для достижения"中转"Эффект
  • Переменная модуля ввода не может быть переназначена, это только читаемая ссылка, но она может перезаписывать свойства.
  • exportкоманда иimportКоманды могут появляться в любом месте модуля, если они находятся на верхнем уровне модуля. Если он находится на уровне блока, будет сообщено об ошибке, потому что в условном блоке кода статическая оптимизация не может быть выполнена, что нарушает первоначальный замысел дизайна модуля ES6.
  • importКоманда имеет эффект подъема и будет поднята на голову всего модуля, который выполняется первым.

5. Преобразование модуля ES6 под Bable

Другой момент заключается в том, что если вы использовали какой-либо ES6 Babel, вы обнаружите, что при использованииexport/import, Babel также преобразует его вexports/requireформа.

Например, мой вывод:

m1.js:

export const count = 0;

мой вклад:

index.js:

import {count} from './m1.js'
console.log(count)

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

m1.js:

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.count = void 0;
const count = 0;
exports.count = count;

index.js:

"use strict";

var _m = require("./m1.js");

console.log(_m.count);

Именно благодаря этим трансформационным отношениям мы можемexportsа такжеimportКомбинировать с:

Это означает, что вы можете использовать его следующим образом:

// 输出模块 m1.js
exports.count = 0;

// index.js中引入
import {count} from './m1.js'
console.log(count)

Разница между спецификацией модулей CommonJS и ES6

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

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

  • Вывод CommonJS — это копия значения; вывод ES6 Modules — это ссылка на значение, а внутренние изменения в экспортируемом модуле повлияют на изменение ссылки

  • Путь к модулю, импортированный CommonJs, может быть выражением, поскольку он используетrequire()методы; Модули ES6 могут быть только строками

  • CommonJS thisУказывает на текущий модуль, Модули ES6thisнаправлениеundefined

  • И в модулях ES6 нет переменных верхнего уровня:arguments,require,module,exports,__filename,__dirname

Что касается первого отличия, то это потому, что CommonJS загружает объект (т.е.module.exportsсвойство), объект не будет создан, пока скрипт не завершит работу. Модуль ES6 не является объектом, его внешний интерфейс — это просто статическое определение, которое будет сгенерировано на этапе статического анализа кода.

(Должны быть какие-то отличия, которых я не ожидал, добавляйте 👏😊)

Справочная статья

Знания бесценны и поддерживают оригинальность.

Параметрическая статья:

послесловие

Ты с нетерпением ждешь мира, надеюсь, у тебя нетbug. Эта статья представлена ​​здесь.

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

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

подобноЛин ДуанТакже парень надеется обратить внимание на паблик Лина.LinDaiDaiИли отсканируйте QR-код ниже👇👇👇.

Время от времени я буду обновлять некоторый внешний контент знаний и свои собственные оригинальные статьи🎉

Ваша поддержка - главная мотивация для меня продолжать творить 😊.

связанное предложение:

"Самый подробный учебник по bpmn.js во всей сети"

«[Предлагаемое изменение] Прочитав это, вы все еще не понимаете Вавилон, я пришлю вам маску»

«[Рекомендуемые звезды] Подойдите к концу 45 вопросов интервью Promise (1,1 Вт слов, тщательно организованных)»

«[Предложение👍] Давайте продолжим с еще 40 вопросами из этого интервью (1,2 Вт сортировки слов вручную)»

«[Почему не Sanlian] Вопросы о наследовании JS, которые проще, чем наследование семейного бизнеса — инкапсуляция (тест коровьего ножа)»

«[Почему не Sanlian] Ответив на эти 48 вопросов, я полностью понимаю наследование JS (слова 1.7w содержат симпатию — вернитесь к оригиналу)»

«[Хорошо] Полностью понять прошлое и настоящее преобразования типов данных из 206 console.log () (1)»

В этой статье используетсяmdniceнабор текста