Оригинальный адрес:medium.free код camp.org/requireing - нет...
Node использует два основных модуля для управления зависимостями модулей:
-
requireМодуль: виден глобально, дополнительное использование не требуетсяrequire('require') -
moduleМодуль: виден глобально, дополнительное использование не требуетсяrequire('module')
Это можно считатьrequireМодуль — это команда,moduleМодули являются организаторами необходимых модулей.
Ссылаться на модули в Node несложно:const config = require('/path/to/file');
requireМодуль предоставляет функцию (как показано выше). когдаrequire()Когда функция передает параметр пути, узел последовательно выполняет следующие шаги:
- Resolving: Найти абсолютный путь path.
- Loading: определить содержимое файла.
- Wrapping: Создает частную область. Обертка гарантирует, что каждый раз, когда вам требуется файл, require и exports являются частными.
- EvaluatingСсылка :evaluating — это последняя ссылка в виртуальной машине, обрабатывающая загруженный файл.
- Caching: Во избежание ссылок на один и тот же файл не повторяйте вышеуказанные шаги.
В этой статье автор объяснит различные этапы, упомянутые выше, и влияние этих этапов на разработчиков, разрабатывающих модули узлов, посредством тематических исследований.
Сначала создайте папку через терминалmkdir ~/learn-node && cd ~/learn-nodeВсе команды в этой статье находятся в~/learn-nodeв исполнении.
Resolving a local path
Сначала давайте представимmoduleобъект. Читатели могут видеть через REPLmoduleобъект
Каждый объект модуля имеет атрибут id для различения разных модулей. Атрибут id обычно представляет собой абсолютный путь, соответствующий модулю, который просто устанавливается в REPL как<repl>. Существует однозначное соответствие между модулями Node и файлами на системном диске. Ссылка на модуль фактически загружает содержимое файла в память. Node поддерживает различные способы ссылки на файлы (например, относительные пути или предварительно настроенные пути).Перед загрузкой содержимого файла в память необходимо найти абсолютный путь к файлу.
При прямом обращении без указания путиfind-meКогда модуль:require('find-me');узел будет пройден по очередиmodule.pathsуказан путь для поискаfind-meМодуль:
Указанный выше путь — это текущий путь ко всем каталогам в корневом каталоге.node_modulesПуть к папке в дополнение к некоторым устаревшим, но устаревшим путям. Если узел не найден в указанном выше путиfind-me.jsБудет выдано исключение «не удается найти ошибку модуля».
Если вы создадите папку node_modules в текущей папке и создадите файл find-me.js, тоrequire('find-me')Вы можете найти меня.
Если find-me.js также существует в других путях, например, другой find-me.js существует в папке node_modules в домашнем каталоге пользователя:
При выполнении в каталоге обучающего кодаrequire('find-me'), поскольку файл find-me.js находится в каталоге node_modules под кодом обучения, файл find-me.js в домашнем каталоге пользователя не будет загружен и выполнен.
Если мы удалим папку node_modules из каталога ~/learn-code, а затем выполним ссылку find-me, то будет использоваться Fine-me под node_modules в домашнем каталоге пользователя:
Requiring a folder
Модуль не обязательно должен быть просто файлом, ридер также может создать папку find-me и создать index.js в папке,require('find-me')index.js будет использоваться, когда:
Обратите внимание, что, поскольку в текущем каталоге есть find-me, обращение к find-me в это время будет игнорировать node_modules в домашнем каталоге пользователя. При обращении к каталогу он по умолчанию ищет index.js, но мы можем указать, какой файл использовать, через свойство main в package.json. Например, чтобы позволитьrequire('find-me')Чтобы иметь возможность анализировать другие файлы в папке find-me, нам нужно добавить package.json в каталог find-me и указать, какой файл следует анализировать:
require.resolve
Если вы просто хотите разобрать модуль, но не выполнять его, вы можете использоватьrequire.resolveфункция.resolveа такжеrequireПроизводительность функции такая же, за исключением того, что она не выполняет файл. Если файл не найден, по-прежнему будет выдано исключение, и если файл будет найден, будет возвращен абсолютный путь к файлу.
resolveФункции можно использовать для определения того, установлен ли модуль, и использовать установленный модуль, если он обнаружен.
Relative and absolute paths
В дополнение к разрешению модулей из node_modules, мы также можем размещать модули где угодно, используя относительные пути (./или../начало) или абсолютный путь (/начало) для ссылки на модуль.
Например, если find-me.js находится в каталоге lib, а не в каталоге node_modules, мы можем обратиться к find-me следующим образом:require('./lib/find-me');
Parent-child relation between files
Создаватьlib/util.jsИ добавьте строку console.log, чтобы различать и выводить одновременноmoduleОбъект:
Добавляем аналогичный код в index.js, а затем выполняем index.js через node. упоминается в index.jslib/util.js:
Выполните index.js в узле:
Обратите внимание на индексный модуль(id: '.')даlib/utilРодительский модуль модуля. но выводlib/utilМодуль не отображается вindexв подмодуле модуля. заменен на[Circular]Значение, потому что здесь циклическая ссылка. В этот момент, если узел печатаетlib/utilдляindexЕсли это подмодуль, он войдет в бесконечный цикл. Это также может объяснить, почему необходимо просто использовать[Circular]заменитьlib/util.
то если вlib/utilссылка в модулеindexчто происходит с модулями. Это то, что циклические ссылки разрешены в узле.
Чтобы лучше понять круглые зависимости, вам сначала нужно понимать некоторые концепции о объекте модуля.
exports, module.exports, and synchronous loading of modules
Экспорт в любом модуле — это специальный объект. Обратите внимание, что в приведенных выше результатах каждый раз, когда объект модуля печатается, будет свойство экспорта пустого объекта. Мы можем добавить некоторые свойства к этому конкретному объекту экспорта. Такие какindex.jsа такжеlib/index.jsОткройте свойство id.
Теперь снова запустите index.js, и вы увидите новые добавленные свойства в объекте модуля каждого файла:
Для краткости автор удаляет некоторые атрибуты в выводимом результате, но вы можете видетьexportsТеперь у объекта есть свойства, которые мы определили ранее. Вы можете добавить любое количество свойств к объекту экспорта или заменить весь объект экспорта чем-то другим. Например, если вы хотите заменить объект экспорта функцией, вы можете сделать следующее:
Запустите index.js еще раз, чтобы увидеть, что объект экспорта стал функцией:
Здесь замена объекта экспорта на функцию не черезexports = function(){}завершить. На самом деле мы тоже не можем этого сделать, потому что объект экспорта в модуле простоmodule.exportsцитаты, в то время какmodule.exportsЭто свойство отвечает за разоблачение. Когда мы переназначим объект экспорта, он разорвет паруmodule.exports, что в данном случае просто вводит новую переменную, а не изменяет ее.module.exportsАтрибуты.
Когда мы импортируем модуль, то, что возвращает функция require, на самом делеmodule.exportsобъект. Например, поместите index.js вrequire('./lib/util')изменить на:
Приведенный выше код помещаетlib/utilАтрибуты, представленные в, назначаются константам UTIL. когда мы выполняемindex.js, последняя строка возвращает следующий результат:UTIL: { id: 'lib/util' }
Давайте поговорим о загруженном свойстве каждого объекта модуля. До сих пор каждый раз, когда мы печатаем объект модуля,loadedсвойстваfalse. module对象通过loadedСвойство для записи того, какие модули были загружены (loaded — true), а какие модули не загружены (loaded — false). в состоянии пройтиsetImmediateчтобы увидеть информацию о том, что модуль был загружен в следующем цикле событий.
Результат выглядит следующим образом:
задерживаетсяconsole.logв мы можем видетьlib/util.jsа такжеindex.jsбыл полностью загружен.
Когда узел завершит загрузку модуля, объект экспорта также станет завершенным.requiring和loadingПроцесс синхронный. Вот почему мы можем видеть, что модуль загружает сообщение о завершении после цикла.
Это также означает, что мы не можем изменять объект экспорта асинхронно. Например, мы не можем сделать что-то вроде этого:
Circular module dependency
Давайте ответим на вопрос о циклической зависимости, упомянутый ранее: что произойдет, если модуль 1 зависит от модуля 2, а модуль 2 зависит от модуля 1 в то же время?
Чтобы узнать, мыlibСоздайте два файла в каталоге,module1.jsа такжеmodule2.js, и заставьте их ссылаться друг на друга:
при исполненииmodule1.js, вы увидите следующие результаты:
мы вmodule1Ссылки, которые еще не полностью загруженыmodule2,из-заmodule2в серединеmodule1Ссылается, когда он не был полностью загруженmodule1, в это время вmodule2Может быть полученexportsОбъекты - это атрибуты перед циркой зависимость (то естьrequire('module2')До). Доступно только в это времяaсвойства, потому чтоbа такжеcсобственность вrequire('module2')Позже.
Узел очень прост в цикле в зависимости от этого блока. Вы можете указать, у которых нет полностью загруженных модулей, но вы можете получить только некоторые свойства.
JSON and C/C++ addons
пройти черезrequireфункция, которую мы можем загрузить изначальноJSONа такжеC++расширение. Вам даже не нужно указывать расширение при его использовании. Если расширение файла не указано, узел сначала попытается загрузить.jsдокумент. если.jsфайл не найден, он попытается загрузить.jsonфайл, если он найден.jsonфайл будет проанализирован.jsonдокумент. если.jsonфайл тоже не найден, попытается загрузить.nodeдокумент. Но чтобы избежать семантической двусмысленности, разработчики должны.jsВ случае указания расширения файла.
нагрузка.jsonФайлы полезны для управления статической конфигурацией или периодического чтения конфигурации из внешних файлов. Например, у нас есть следующий файл json:
Мы можем использовать его напрямую:
Запуск приведенного выше кода выведет:Server will run at http://localhost:8080если узел не найден.jsа также.json, буду искать.nodeфайл и проанализируйте его, проанализировав расширение узла.nodeдокумент.
На официальной документации узла написана C ++.Расширенный случай. Дело выявилоhello()функция, выполнениеhello()функция будет выводитьworld. вы можете использоватьnode-gypПучок.ccкомпилировать файл, встроить в.nodeдокумент. Разработчики должны настроитьbinding.gypсказатьnode-gypчто делать. строительствоaddon.nodeВ случае успеха его можно использовать как любой другой модуль:
отrequire.extensionsВы можете видеть, что в настоящее время поддерживаются только три типа расширений:
Вы можете видеть, что каждый тип имеет различную функцию загрузки. для.jsиспользование файлаmodule._compileметод, для.jsonиспользование файлаJSON.parseметод, для.nodeиспользование файлаprocess.dlopenметод.
All code you write in Node will be wrapped in functions
Пакет модулей в узле часто неправильно понимают. Прежде чем понять пакет модулей узла, давайте рассмотримexports/module.exportsОтношение.
мы можем использоватьexportsвыставлять свойства, но не заменять напрямуюexportsобъект, потому чтоexportsобъект в самый разmodule.exporstцитаты.
Точнее,exportsОбъекты являются глобальными для каждого модуля и определяются какmoduleСсылка на свойство объекта.
Прежде чем объяснять процесс упаковки узлов, давайте зададим еще один вопрос.
В браузере, когда мы объявляем переменную в глобальной среде:var answer = 42;по определениюanswerВ скрипте после переменнойanswerПеременные — это глобальные переменные.
В узле такого нет. Когда мы определяем переменную в модуле, другие модули не могут напрямую обращаться к переменным в модуле, так как же переменные локализованы в узле?
Ответ прост. Перед компиляцией модуля node оборачивает код модуля в функцию, что мы можем сделать черезmoduleна объектеwrapperсвойства, чтобы увидеть эту функцию:
узел не выполняет напрямую код, который вы пишете в файле. Вместо этого он выполняет код обернутой функции, которая упаковывает код, который вы написали в теле функции. Это гарантирует, что переменные верхнего уровня в любом модуле являются локальными для других модулей.
wrapperФункция имеет 5 параметров:exports,require,module,__filenameа также__dirname. По этой же причине эти переменные кажутся глобальными для каждого модуля, хотя на самом деле они независимы от каждого модуля.
Когда узел выполняет функцию-оболочку, эти переменные назначаются правильно.exportsопределяется какmodule.exportsцитаты,requireа такжеmoduleоба указывают на функцию, которая должна быть выполнена,__filenameа также__dirnameПредставляет имя файла и путь к каталогу упакованного модуля.
Если вы запустите модуль с ошибкой, функция-обертка будет видна сразу.
Вы можете видеть, что об ошибке сообщается в первой строке функции-оболочки. Кроме того, поскольку каждый модуль обернут функцией, мы можем передатьargumentsдля доступа ко всем параметрам функции-оболочки.
Первый параметрexportsобъект, начиная с пустого объекта, за которым следуетrequire/moduleобъект, эти два объекта не являются глобальными переменными, оба связаны сindex.jsСвязанный созданный объект. Последние два параметра представляют путь к файлу и путь к папке.
Возвращаемое значение обернутой функцииmodule.exporst. Внутри функции-оболочки мы можем передатьexportsобъект для измененияmodule.exportsсвойства, но не можетexportsпереназначить, потому чтоexportsПросто ссылка.
Приведенное выше описание эквивалентно следующему коду:
Если мы изменимexportsобъект, тоexportsобъект больше неmodule.exportsцитаты. Этот способ ссылки отлично работает не только здесь, но и в javascript.
The require object
requireВ объектах нет ничего особенного.requireэто функциональный объект, который принимает имя модуля или имя пути и возвращаетmodule.exportsобъект. Не стесняйтесь переопределять, если мы хотимrequireобъект.
Например, для тестирования мы хотим помокатьrequireПоведение функций по умолчанию, возвращающее фиктивный объект, а не ссылающееся на возврат модуляmodule.exportsобъект. правильноrequireВыполнение задания выполняет следующее:
справаrequireПосле переназначения каждый вызовrequire('something')вернет фиктивный объект.requireОбъекты также имеют свои собственные свойства. Ранее мы видели, что мы используем для разрешения путей модулей.resolveсвойства иrequire.extensionsАтрибуты.
Кроме тогоrequire.mainАтрибуты используются, чтобы различать, ссылаются ли на текущий модуль или запускают его напрямую. Скажем, мыprint-in-frame.jsВ файле есть одинprintInFrameфункция:
Эта функция принимает параметр типа numbernumbericи параметр типа stringheader, функция сначала основана наsizeПараметр печатает указанное число*рамку и печать в рамкеheader.
Мы можем использовать эту функцию двумя способами:
- Вызовите его прямо из командной строки:
~/learn-node $ node print-in-frame 8 Hello, передайте 8 и Hello функции в командной строке и напечатайте 8*составленный кадр и вывод в кадреhello. -
requireвызов метода: предположимprint-in-frame.jsразоблачитьprintInFrameфункцию, мы можем назвать ее так:
Это напечатает в рамке, состоящей из 5 *Hey.
Нам нужен какой-то способ различать, вызывается ли текущий модуль только из командной строки или на него ссылаются другие модули. В этом случае мы можем пройтиrequire.mainсудить:
Таким образом, мы можем реализовать описанный выше сценарий приложения с помощью этого условного выражения:
Если на текущий модуль не ссылаются другие модули в виде модуля, мы можемprocess.argvзвонитьprintInFrameфункция. В противном случае мы устанавливаемmodule.exportsПараметрыprintInFrameфункция.
All modules will be cached
Важно понимать кэширование модуля. Поясним это на простом примере, например, у нас есть файл js со следующим рисунком персонажа:
Мы надеемся каждый разrequireСимволы могут отображаться при документировании. Например, мы обращаемся к js отрисовки персонажа дважды, надеясь вывести отрисовку персонажа дважды:
Вторая ссылка не выводит глиф, потому что в этот момент модуль уже кэширован. После первой ссылки мы можем пройтиrequire.cacheдля просмотра кеша модуля.cacheОбъект — это простая пара ключ-значение, в которой кэшируется каждый модуль, на который ссылаются.cacheЗначение on соответствует каждому модулюmoduleобъект. мы можем начать сrequire.cacheудалить наmoduleобъект, чтобы сделать кеш недействительным. Если мы удалим из кешаmoduleObject, при повторном запросе узел все равно перезагрузит модуль и повторно кэширует модуль.
Однако для этого случая вышеописанный способ модификации кеша не лучший способ. Самый простой способ - положитьascii-art.jsОберните его в функции и разоблачите ее, чтобы, когда мы ссылаемся наascii-art.js, вы получите функцию, которая будет выводить рисунок персонажа при каждом выполнении.