zoukankan      html  css  js  c++  java
  • require的加载顺序

    模块

    一个nodejs文件就是一个模块。nodejs的模块分为两类:

    (1)原生(核心)模块

    (2)文件模块

    文件模块分为三类:

    • .js。通过fs模块同步读取js文件并编译执行。
    • .node。通过C/C++进行编写的Addon。通过dlopen方法进行加载。
    • .json。读取文件,调用JSON.parse解析加载。

    export公开模块的接口,require从外部获取一个接口。

    require查找策略

    原生模块在Node.js源代码编译的时候编译进了二进制执行文件,加载的速度最快。另一类文件模块是动态加载的,加载速度比原生模块慢。但是Node.js对原生模块和文件模块都进行了缓存,于是在第二次require时,是不会有重复开销的。尽管require方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。

    require方法接受以下几种参数的传递:

    • http、fs、path等,原生模块。
    • ./mod或../mod,相对路径的文件模块。
    • /pathtomodule/mod,绝对路径的文件模块。
    • mod,非原生模块的文件模块。

    当require一个文件模块时,从当前文件目录开始查找node_modules目录;然后依次进入父目录,查找父目录下的node_modules目录;依次迭代,直到根目录下的node_modules目录。

    简而言之,如果require绝对路径的文件,查找时不会去遍历每一个node_modules目录,其速度最快。其余流程如下:

    1. 从module path数组中取出第一个目录作为查找基准。
    2. 直接从目录中查找该文件,如果存在,则结束查找。如果不存在,则进行下一条查找。
    3. 尝试添加.js、.json、.node后缀后查找,如果存在文件,则结束查找。如果不存在,则进行下一条。
    4. 尝试将require的参数作为一个包来进行查找,读取目录下的package.json文件,取得main参数指定的文件。
    5. 尝试查找该文件,如果存在,则结束查找。如果不存在,则进行第3条查找。
    6. 如果继续失败,则取出module path数组中的下一个目录作为基准查找,循环第1至5个步骤。
    7. 如果继续失败,循环第1至6个步骤,直到module path中的最后一个值。
    8. 如果仍然失败,则抛出异常。

    何为核心? 重要的/不可缺的!

    node.js作为一门跨平台服务器端编程语言,必然也有它的核心.

    node.js继承了javascript 客户端语言该有的优势,同时摒弃了客户端javascript的一些缺点,比如在客户端javascript环境下,全局变量可以到处被定义,随意被覆盖,代码污染严重,所以node.js有了模块的概念,在模块里定义的全局变量如果没有被export ,那么此变量是私有的,只能在定义的模块是使用.

    不用提醒我,我并没有跑题,正因为模块机制,导致node.js的一些核心模块也编译成各自独立的二进制文件,他们就放在 node 源代码中 lib 文件夹下.

    为啥是二进制文件?你不用吃惊,reqire 可以引入 核心二进制模块,第三方模块,js文件,json文件及编译的好的c++模块(扩展名.node)

    我们上面提到核心的就是重要的/不可缺的. 比如 'http' 模块, 'fs' 模块,这些内置的核心模块有优先载入的权限,也就是说,如果你自己创建了一个 http.js 的文件,然后通过require('http') 来引用的话,系统会优先把内置的 http 模块加载进来,而对于你这个同名的文件根本不理不睬.

    如此看来,node的加载机制是有顺序的,我们不妨来理一下.

    (1)首先加载核心模块

    (2)试图在require 的名称后面加上.js 去搜索并加载.

    (3)试图在require 的名称后面加上.json 去搜索并加载.

    (4)试图在require 的名称后面加上 .node 去搜索并加装编译好的c++模块.

    在加载文件模块时我们提到了搜索,如果搜索,node遵循什么规则?

    常见的加载方式有3种类型

    require('http');

    require('./dbApi');

    require('express');

    上面列出的三种加载方式其实顺便也给出了加载顺序规则.

    首先加载核心模块,不管有没有同名/同目录的情况下,核心模块优先加载.

    注意:核心模块在node.js安装是已经被编译成二进制模块,程序启动时已经被自动加载到系统内存中,所以速度快,效率高.

    其次按照相对路径/绝对路径加载文件模块(加载顺序,首先试图按照路径查找 .js 扩展名的文件,如果没有,试图按照路径查找 .json 扩展名的文件,如果还是没有,就按照路径查找 .node 扩展名的c++模块了)

    注意:按照common.js 规范指示,模块加载过程中,模块名需要遵守小驼峰命名规则,且最好不带扩展名,但是上面三种文件中,因为有优先搜索规则,而文件io操作都是同步过程,也就是说,如果你要加载的是一个 config.json 文件,那么你的require('./config') 这样写导致系统首先在同层目录下搜索config.js 文件,直到系统搜索失败后才会继续搜索 config.json 文件,而这个由于IO同步操作会导致加载模块延迟,所以当你要加载 .json文件或者 .node 文件时,最好加上扩展名.

    最后搜索 node_modules 目录下通过npm下载的第三方模块. 

    注意:首次加载这类模块最慢,因为执行文件所在目录的node_mondel 文件夹下找不到时,会去父级node_mondel 文件夹里查找,如果还是找不到会去父级的父级node_mondel 文件夹里查找.......但是,只要首次加载成功后,node就会缓存起来,它缓存的是编译后的二进制模块,所以以后的加载速度和效率都的有保证的.

    参考文献

    https://www.cnblogs.com/520yang/articles/5039394.html

  • 相关阅读:
    linux常用命令(一)
    并发与高并发(十九) 高并发の消息队列思路
    鉴别web服务器的工具类
    并发与高并发(十八) 高并发之缓存思路
    记一次多线程下使用while出现的问题
    并发与高并发(十七)高并发之扩容思路
    并发与高并发(十六)多线程并发拓展
    并发与高并发(十五)线程池
    并发与高并发(十四)J.U.C组件拓展
    基于springboot实现Java阿里短信发送
  • 原文地址:https://www.cnblogs.com/zhoulixue/p/8757546.html
Copyright © 2011-2022 走看看