Прочитайте и поймите загрузку модуля CommonJS

Node.js внешний интерфейс JavaScript браузер

Немного поговорим о 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. Следует отметить, что экспортируемый объект является статическим и непостоянным значением, которое может быть изменено позже, пожалуйста, используйте функцию для его динамического получения, в противном случае измененное значение не может быть получено. Импортированные параметры можно изменять по желанию, поэтому при их использовании следует соблюдать осторожность.