Немного поговорим о CommonJS
Я полагаю, что все знают значение английского слова Common, я помню фразу, что общие знания означают здравый смысл, так что CommonJS похож на здравый смысл, и все понимают его значение? Очевидно, нет, этот здравый смысл вовсе не здравый смысл. Я изначально думал, что commonJS — это JS-библиотека с открытым исходным кодом, то есть очень удобная библиотека, которая содержит некоторые часто используемые фронтенд-методы, но я так ошибалась, CommonJS — это не только не библиотека, но и невидимый и неосязаемый вещь, Он просто норма! Так же, как школьные правила и положения, он используется для стандартизации программирования JS и привязки интерфейсов. Как и Promise, это спецификация.Хотя существует множество библиотек с открытым исходным кодом, которые реализуют эти спецификации, эту спецификацию также можно реализовать, полагаясь на наши возможности JS.
Спецификация CommonJs
Так что же определяет CommonJS? Чтобы объяснить эту спецификацию, мы должны начать с характеристик JS. JS — это скриптовый язык буквального перевода, то есть он запускается во время компиляции, поэтому понятия модулей нет. Таким образом, CommonJS — это спецификация, призванная исправить недостаток JS в этом отношении.
CommonJS определяет две основные концепции:
-
require
функция импорта модулей -
module.exports
переменная, используемая для экспорта модуля
Однако эти два ключевых слова не поддерживаются браузерами, поэтому я думаю, что это причина, по которой браузеры не поддерживают CommonJS. Если вы должны использовать CommonJs в браузере, вам понадобятся некоторые скомпилированные библиотеки, такие какbrowserifyЧтобы помочь, мы компилируем CommonJs в синтаксис, поддерживаемый браузером, который на самом деле реализует требования и экспорты.
Итак, для чего можно использовать CommonJS? Хотя CommonJS нельзя использовать непосредственно в браузере, nodejs можно реализовать на основе спецификации CommonJS, и это похоже на сына. В nodejs мы можем напрямую использовать два ключевых слова require и exports для импорта и экспорта модулей.
Реализация модуля CommomJS в Nodejs
require
Импорт, код очень простой,let {count,addCount}=require("./utils")
Вот и все. Так что же произошло при импорте? ? В первую очередь это должен быть разбор пути.Система разбирает нам абсолютный путь.Относительный путь мы пишем для нас, а абсолютный путь для системы.Ведь абсолютный путь такой долго, это очень кропотливо смотреть, особенно когда наш проект находится в N папках. такrequir
Первое, что нужно сделать, это разобрать путь. Мы можем написать это очень лаконично, нужно только написать относительный путь и имя файла, даже суффикс можно опустить, пустьrequire
Помогите нам сопоставить и найти. то естьrequire
Первый шаг — разобрать путь для получения содержимого модуля:
- Если это основной модуль, такой как
fs
, он возвращается прямо в модуль - Если это с таким путем, как
/
,./
Подождите, затем соедините абсолютный путь, а затем сначала прочитайте кешrequire.cache
Прочитайте файл еще раз. Если суффикс не добавляется, суффикс будет добавлен автоматически, а затем распознается один за другим.-
.js
Разобрать текстовый файл JavaScript -
.json
Разбирать объекты JSON -
.node
разрешается в бинарный модуль плагина
-
- Модули, которые загружаются в первый раз, кэшируются в
require.cache
, поэтому загружайте несколько разrequire
, результирующий объект тот же. - Когда код модуля выполняется, модуль будет упакован по следующему шаблону, чтобы область действия находилась в пределах области действия модуля.
(function(exports, require, module, __filename, __dirname) {
// 模块的代码实际上在这里
});
Официальное объяснение, данное nodejs, вы можете сослаться на следующее
module
После разговора о том, что require делает, затемrequire
запущенныйmodule
Что ты сделал? Давайте посмотрим на использование.Сначала напишите простой модуль экспорта.После написания модуля вам нужно только добавить параметры, которые необходимо экспортировать.module.exports
Вот и все.
let count=0
function addCount(){
count++
}
module.exports={count,addCount}
А то что нужно добавить при выполнении кода по require, то наш код на самом деле выглядит так:
(function(exports, require, module, __filename, __dirname) {
let count=0
function addCount(){
count++
}
module.exports={count,addCount}
});
require
Когда точноmodule
Что получилось, можем разбить точку в vscode:
По этой точке останова мы можем отсортировать:
Когда обведено желтымrequire
, который мы называем методом
Когда обведено краснымModule
содержание работы
Module._compile
Module.extesions..js
Module.load
tryMouduleLoad
Module._load
Module.runMain
Синий кружок — это то, что делает nodejs, то естьNativeModule
, для выполненияmodule
объект.
Все мы знаем, что в JS метод стека стека при вызове функции, то есть первый-ближайший-последний-выход, то есть после срабатывания требуемой функции время выполнения на рисунке идет снизу вверх. верх. То есть синий ящик запускается первым. Я взял часть его кода и изучил его.
NativeModule
Собственный код — это код ключа, который используется для инкапсуляции модуля.
NativeModule.wrap = function(script) {
return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
};
NativeModule.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];
ЖдатьNativeModule
вызыватьModule.runMain
После этого наша загрузка модуля начинается, давайте интерпретируем его снизу вверх.
-
Module._load
это новыйmodule
объект, затем поместите этот новый объект вModule
в кэше.var module = new Module(filename, parent); Module._cache[filename] = module;
-
tryMouduleLoad
, а потом новыйmodule
Объект начинает разбор содержимого импортированного модуляmodule.load(filename);
- новый
module
Объект наследует Module.load, этот метод должен анализировать тип файла, а затем выполнять его по категориям. -
Module.extesions..js
Это делает две вещи: читает файл и подготавливает его к компиляции. -
Module._compile
Наконец, пришло время компилировать, так как же JS запускает текст? Чтобы превратить текст в исполняемые объекты, в js есть 3 метода:-
оценочный метод
eval("console.log('aaa')")
-
новый механизм шаблонов Function()
let str="console.log(a)" new Function("aaa",str)
-
node выполняет строку, мы используем расширенный
vm
let vm=require("vm") let a='console.log("a")' vm.runInThisContext(a)
Здесь Module компилируется по способу vm, сначала инкапсулируется, затем выполняется и, наконец, возвращается в require, и мы можем получить результат выполнения.
var wrapper = Module.wrap(content); var compiledWrapper = vm.runInThisContext(wrapper, { filename: filename, lineOffset: 0, displayErrors: true });
-
Поскольку все модули выполняются после инкапсуляции, то есть импортированный модуль может выполняться только в соответствии сmodule.exports
Это внешний интерфейс для доступа к содержимому.
В заключение
У людей, которые видят эти коды, действительно кружится голова.require
После разбора пути, затем запускаModule
этот один класс, тоModule
из_load
Способ сделать это — создать新module
кеш, чтобы в следующий разrequire
Вы можете вернуться напрямую, не выполняя его снова. а потом это新module的load
Метод загружает и выполняет код через виртуальную машину, чтобы вернуть объект вrequire
.
Поскольку это кеш, назначенный ему после компиляции и запуска, если значение экспорта является параметром, а не функцией, то при изменении значения текущего параметра это не вызовет изменения в экспорте, потому что параметр, назначенный для экспорт является статическим, он не вызовет повторной операции.
Разница между модулями CommonJs и модулями ES6
сцены, которые будут использоваться
CommonJS в основном используется на стороне сервера из-за ограничений ключевых слов. Для загрузки модуля ES6 некоторые браузеры уже поддерживают эту функцию, поэтому ES6 можно использовать в браузерах.Если вы столкнулись с браузером, который не поддерживает синтаксис ES6, вы можете перевести его на ES5.
Синтаксические различия
ES6 также является спецификацией JavaScript.Разница между ним и модулем CommonJs очевидна.Прежде всего, отличается код.Импорт и экспорт ES6 очень интуитивен.import
а такжеexport
.
commonJS | ES6 | |
---|---|---|
поддерживаемые ключевые слова | arguments,require,module,exports,__filename,__dirname |
import,export |
импорт | const path=require("path") |
import path from "path" |
экспорт | module.exports = APP; |
export default APP |
импортированный объект | Не стесняйтесь изменять | не может быть изменен по желанию |
Импорт | может быть по желаниюrequire , но кроме первого раза все последующие получаются из кеша модуля |
импортировать в заголовок |
** Внимание всем! Фокус! nodejs является сыном CommonJS, поэтому некоторые функции ES6 не поддерживаются, например, ключевые слова ES6 для модулей.import
а такжеexport
, если вы запустите его в среде nodejs, просто подождите, пока большой красный сообщит об ошибке~**
разница нагрузки
Помимо различий в синтаксисе, различается и природа модулей, на которые они ссылаются. Хотя все они являются модулями, структура модулей сильно отличается.
В ES6, если вы хотите протестировать в браузере, вы можете использовать следующий код:
//utils.js
const x = 1;
export default x
<script type="module">
import x from './utils.js';
console.log(x);
export default x
</script>
первый, чтобы датьscript
Одинtype="module"
Указывает, что это модуль ES6, и этот тег по умолчанию загружается асинхронно, то есть выполняется после полной загрузки страницы, без этого тега код не сможет запуститься. Затем вы можете написать импорт и экспорт напрямую.
Несколько проблем с импортом модуля ES6:
- Один и тот же модуль можно импортировать только один раз, например
x
уже импортированы, вы больше не можете импортировать из utilsx
- Разные модули импортируют один и тот же модуль, этот модуль будет использоваться только в первый раз.
import
в исполнении. - Импортированный модуль является ссылкой на значение, и он является динамическим, после изменения другие связанные значения также изменятся.
- Импортированные объекты не могут быть произвольно обрезаны по ссылке, например той, которую я представил
count
Я не могу изменить его значение, потому что оно импортировано, и вы можете изменить его только вcount
Модифицируется модуль, в котором он находится. но еслиcount
является объектом, то вы можете изменить свойства объекта, такие какcount.one=1
, но нетcount={one:1}
.
Вы можете посмотреть на этот пример, я написал небольшой тест для изменения значения объекта, вы найдетеutils.js
серединаcount
Начальное значение должно быть0
, но работаетaddCount
такcount
Значение изменяется динамически, поэтомуcount
Значение становится2
.
let count=0
function addCount(){
count=count+2
}
export {count,addCount}
<script type="module">
import {count,addCount} from './utils.js';
//count=4//不可修改,会报错
addCount()
console.log(count);
</script>
В отличие от ссылки на модуль commonJS, его характеристики:
- Как объяснялось в предыдущем разделе, фиксированное значение, экспортируемое модулем, является фиксированным значением, которое не будет изменено из-за более поздних модификаций, если только статическое значение не экспортируется, а изменяется на функцию, и каждый вызов вызывается динамически. , то каждое значение является последним.
- Импортированные объекты могут быть изменены по желанию, что эквивалентно простому копированию в импортированном модуле.
Если вы хотите углубиться в изучение, вы можете обратиться к г-ну Руану.Начало работы с ES6 — загрузка реализации модуля.
Обзор модулей CommonJS
Модули CommonJS могут работать только в среде, которая поддерживает эту спецификацию.Nodejs разработан на основе спецификации CommonJS, поэтому он может идеально запускать модуль CommonJS.Затем nodejs не поддерживает спецификацию модуля ES6, поэтому при разработке сервера nodejs обычно используется Спецификация CommonJS.
Импорт модуля CommonJSrequire
, на экспортmodule.exports
. Следует отметить, что экспортируемый объект является статическим и непостоянным значением, которое может быть изменено позже, пожалуйста, используйте функцию для его динамического получения, в противном случае измененное значение не может быть получено. Импортированные параметры можно изменять по желанию, поэтому при их использовании следует соблюдать осторожность.