거의 모든 Node.js 개발자는 require() 함수의 기능을 알려줄 수 있지만 실제로 작동 방식을 아는 사람은 몇 명이나 될까요? 우리는 라이브러리와 모듈을 로드하기 위해 매일 이를 사용하지만 그 동작은 우리에게 미스터리입니다.
호기심에서 노드의 핵심 코드를 파헤쳐 내부에서 무슨 일이 벌어지고 있는지 알아봤습니다. 하지만 이것은 단일 기능이 아닙니다. node의 모듈 시스템에서 module.js를 찾았습니다. 이 파일에는 각 파일의 로드, 컴파일 및 캐싱을 제어하는 놀랍도록 강력하고 상대적으로 익숙하지 않은 핵심 모듈이 포함되어 있습니다. require()의 등장은 빙산의 일각에 불과합니다.
module.jsfunction Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent; // ...
Module.js는 Node.js 내에서 주로 두 가지 역할을 합니다. 첫째, 모든 Node.js 모듈의 기반을 제공합니다. 각 파일은 파일이 실행된 후에도 지속되는 기본 모듈의 새 인스턴스입니다. 이것이 바로 우리가 module.exports에 속성을 첨부하고 필요할 때 이를 반환할 수 있는 이유입니다.
이 모듈의 두 번째 주요 작업은 노드의 모듈 로딩 메커니즘을 처리하는 것입니다. 우리가 사용하는 독립적인 작업 "require" 함수는 실제로 module.require의 추상 개념이며, 그 자체는 단지 Module._load 함수의 간단한 캡슐화일 뿐입니다. 이 로드 메소드는 각 파일의 실제 로드를 처리하고 그곳에서 여정을 시작합니다.
Module._load Module._load = function(request, parent, isMain) { // 1. Check Module._cache for the cached module. // 2. Create a new Module instance if cache is empty. // 3. Save it to the cache. // 4. Call module.load() with your the given filename. // This will call module.compile() after reading the file contents. // 5. If there was an error loading/parsing the file, // delete the bad module from the cache // 6. return module.exports };
Module._load는 새 모듈을 로드하고 모듈 캐시를 관리하는 역할을 담당합니다. 로드된 각 모듈을 캐싱하면 중복된 파일 읽기 수가 줄어들고 애플리케이션 속도가 크게 향상될 수 있습니다. 또한 공유 모듈 인스턴스를 사용하면 모듈의 싱글톤 기능을 프로젝트 상태로 유지할 수 있습니다.
캐시에 모듈이 없으면 Module._load는 해당 파일의 새 기본 모듈을 생성합니다. 그런 다음 새 파일의 내용을 module._compile로 보내기 전에 읽도록 모듈에 지시합니다. [1]
위의 6단계를 확인하면 module.exports가 사용자에게 반환된 것을 볼 수 있습니다. 이것이 사용할 공용 인터페이스를 정의할 때 내보내기 및 module.exports를 사용하는 이유입니다. Module._load가 다음에 필요한 콘텐츠를 반환하기 때문입니다. 여기에 더 많은 기능이 없다는 것에 놀랐습니다. 그러나 있었다면 좋을 것입니다.
module._compile Module.prototype._compile = function(content, filename) { // 1. Create the standalone require function that calls module.require. // 2. Attach other helper methods to require. // 3. Wraps the JS code in a function that provides our require, // module, etc. variables locally to the module scope. // 4. Run that function};
이곳이 진짜 마법이 일어나는 곳입니다. 먼저, 이 모듈에 대해 특별한 독립형 require 함수가 생성됩니다. 이는 우리 모두에게 필요하고 친숙한 기능입니다. 함수 자체는 Module.require의 래퍼일 뿐이며 사용하기 쉬운 몇 가지 잘 알려지지 않은 보조 메서드도 포함되어 있습니다.
· require(): 외부 모듈 로드
· require.resolve(): 해결 절대 경로에 대한 모듈 이름
· require.main: 메인 모듈
· require.cache: 캐시된 모든 모듈
· require.extensions: 확장자에 따라 각 유효한 파일 형식에 사용할 수 있는 컴파일 방법
한 번 require가 준비되면 로드된 전체 소스 코드는 require, 모듈, 내보내기 및 기타 노출된 모든 변수를 매개변수로 받아들일 수 있는 새로운 함수로 캡슐화됩니다. Node.js 환경과의 충돌을 방지하기 위해 모듈을 캡슐화하기 위해서만 만들어진 기능입니다.
(function (exports, require, module, __filename, __dirname) { // YOUR CODE INJECTED HERE! });
Module._compile 메서드는 동기식으로 실행되므로 Module._load에 대한 호출은 이 코드가 끝날 때까지만 기다렸다가 module.exprts를 사용자에게 반환할 수 있습니다.
결론
그래서 우리는 require의 전체 코드를 보았고 그것이 어떻게 작동하는지에 대한 사전 이해를 가졌습니다.
이 모든 내용을 따랐다면 마지막 비밀인 require('모듈')을 사용할 준비가 된 것입니다. 맞습니다. 모듈 시스템 자체는 모듈 시스템을 통해서 로딩이 가능합니다. 처음. 이상하게 들릴 수도 있지만 이를 통해 사용자 공간은 Node.js 코어를 탐구하지 않고도 모듈 로딩 시스템과 상호 작용할 수 있습니다. 인기 있는 모듈은 이렇게 만들어졌습니다. [2]
자세한 내용을 알고 싶으시면 module.js 소스코드를 직접 확인해보세요. 한동안 머리를 아프게 할 일들이 충분히 있습니다. NODE_MODULE_CONTEXTS가 무엇인지, 왜 추가되었는지 먼저 알려주는 사람이 보너스 포인트를 받게 됩니다. :)
[1] module._compile 메소드는 JavaScript 파일을 실행하는 데만 사용되며 JSON.parse( ) 및
[2]을 반환합니다. 그러나 두 모듈 모두 Module._resolveLookupPaths 및 Module._findPath와 같은 비공개 모듈 메서드를 기반으로 구축되었습니다. 이는 그다지 좋지 않다고 생각할 수 있습니다...
几乎所有的Node.js开发人员可以告诉你require()函数做什么,但我们又有多少人真正知道它是如何工作的?我们每天都使用它来加载库和模块,但它的行为,对于我们来说反而是一个谜。
出于好奇,我钻研了node的核心代码来找出在引擎下发生了什么事。但这并不是一个单一的功能,我在node的模块系统的找到了module.js。该文件包含一个令人惊讶的强大的且相对陌生的核心模块,控制每个文件的加载,编译和缓存。require() 它的横空出世,只是冰山的一角。
module.jsfunction Module(id, parent) { this.id = id; this.exports = {}; this.parent = parent; // ...
在module.js在Node.js内部主要承担两个角色。首先,它为所有的Node.js模块提供了一个基础。每个文件是基本模块new出的一个新实例,即使在该文件已经运行之后,仍然存在。这就是为什么我们能够性为module.exports附加属并在需要时返回它们。
该模块的第二大任务是处理node的模块加载机制。我们使用的独立操作的“require”函数实际上是一个抽象概念的module.require,这本身就是只是一个简单的关于Module._load功能的封装。此load方法处理每个文件的实际加载,并在那里开始我们的旅程。
Module._load Module._load = function(request, parent, isMain) { // 1. Check Module._cache for the cached module. // 2. Create a new Module instance if cache is empty. // 3. Save it to the cache. // 4. Call module.load() with your the given filename. // This will call module.compile() after reading the file contents. // 5. If there was an error loading/parsing the file, // delete the bad module from the cache // 6. return module.exports };
Module._load负责加载新的模块和管理模块的缓存。缓存加载的每个模块减少冗余文件的读取次数,并可以显著地加快您应用程序的速度。此外,共享模块实例允许单例特性的模块,保持在项目中的状态。
如果某个模块没有在缓存中存在,Module._load将创建该文件的一个新的基本模块。然后,它会告诉模块在将它们发送到module._compile之前阅读新文件的内容。[1]
如果您注意到上面的步骤#6,你会看到module.exports已被返回给用户。这就是为什么当你在定义公共接口使用时,你使用exports和module.exports,因为Module._load将接下来返回require的内容。我很惊讶,这里没有更多的功能,但如果有的话那更好。
module._compile Module.prototype._compile = function(content, filename) { // 1. Create the standalone require function that calls module.require. // 2. Attach other helper methods to require. // 3. Wraps the JS code in a function that provides our require, // module, etc. variables locally to the module scope. // 4. Run that function};
这是真正的奇迹发生的地方。首先,一个特殊的独立操作的require函数是为该模块创建的。这是我们需要的并且都熟悉的功能。而函数本身只是一个在Module.require的封装,它也包含了一些便于我们使用的鲜为人知的辅助方法:
· require():加载一个外部模块
· require.resolve():解析一个模块名到它的绝对路径
· require.main:主模块
· require.cache:所有缓存好的模块
· require.extensions:根据其扩展名,对于每个有效的文件类型可使用的编制方法
一旦require准备好了,整个加载的源代码就会被封装在一个新的函数里,可以接受require,module,exports和所有其他暴露的变量作为参数。这是一个仅仅为封装模块的而创建的函数,以便于在防止与Node.js的环境产生冲突。
(function (exports, require, module, __filename, __dirname) { // YOUR CODE INJECTED HERE! });
该Module._compile方法是同步执行的,所以对Module._load的调用只能等到这段代码运行结束,并将module.exprts返回给用户。
结论
因此,我们已经了解了require的全部代码,并已经初步了解它是如何工作的。
如果你已经按照这一切的方式做了,那么你已经为最后的秘密做好准备:require(‘module’)。这是正确的,该模块系统本身可以通过模块系统被加载。盗梦空间。这可能听起来很奇怪,但它可以让用户空间同模块加载系统互动起来,并不需要钻研Node.js核心。受欢迎的模块都像这样被建立。[2]
如果您想了解更多,请自己查看module.js源代码。还有很多东西足够你头痛一段时间了。第一个可以告诉我什么是NODE_MODULE_CONTEXTS“以及它为什么被添加的人可以得到加分奖励 :)
[1] module._compile方法只用于运行JavaScript文件。 JSON文件需通过JSON.parse() 解析并返回
[2]然而,这两个模块都建立在私有模块的方法,如Module._resolveLookupPaths和Module._findPath。你可以认为这并没有好多了…
위 내용은 Node.js에서 require() 메서드가 어떻게 작동하는지 살펴보세요.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!