Оригинальный адрес: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
объект, чтобы сделать кеш недействительным. Если мы удалим из кешаmodule
Object, при повторном запросе узел все равно перезагрузит модуль и повторно кэширует модуль.
Однако для этого случая вышеописанный способ модификации кеша не лучший способ. Самый простой способ - положитьascii-art.js
Оберните его в функции и разоблачите ее, чтобы, когда мы ссылаемся наascii-art.js
, вы получите функцию, которая будет выводить рисунок персонажа при каждом выполнении.