Node.js模版机制简介

简单记录以下Node.js的模版机制。

Node.js使用的是CommonJS的模版规范,CommonJS和JavaScript的关系如图:
在这里插入图片描述
原生js被限制在浏览器的沙盒中,很多功能都缺失,比如最重要的IO功能,这导致了js虽然在前端中无敌,却迟迟不能进入后端开发。

CommonJS是由一系列的库组成的,是对js缺失功能的补充。

CommonJS模块规范

模块导入

CommonJS使用require()方法来实现对模块引用,该方法接受一个模块标识字符串,引入这个模块到程序上下文中,例如:

const fs = require('math');

一般使用const来接收一个模块,以防模块受到修改。

模块标识

模块标识是一个字符串,其实就是模块的路径。

模版标识是省去后缀名的,也就是不需要.js后缀名。

模版标识中的路径可以是相对路径,也可以是绝对路径,这一般是自定义的模版。

  • 对于标准库就需要前缀,直接用它的名字就行,比如:
    require('fs')
    
  • 相对路径,以.../等形式找到需要调用的模版相对于当前文件的路径,比如:
    require('../math/add');
    
  • 绝对路径,从操作系统的根目录出发,找到需要调用的模版位置,比如:
    require('D:/example/lib/math/add');
    

模块导出

exports对象是导出模块的唯一出口,它被定义在任何CommonJS的文件中。

在CommonJS中一个文件就是一个模块

  • 可以分开导出,例如:

    exports.add = (a,b) =>  { return a+b; }
    exports.sub = (a,b) => { return a-b; }
    
  • 也可以先申明,然后最后再一起导出,例如:

    function add(a,b){
        return a+b;
    }
    function sub(a,b){
        return a-b;
    }
    module.exports = {
        add: add,
        sub: sub
    }
    

    从这个例子我们还可以得出一个结论,其实exports只是module的一个属性,module指的是这个文件

    另外在模版内部和模版外部可以用不一样的函数名,例如:

    module.exports = {
        sub: add,
        add: sub
    }
    

    当然,千万不要写这样作用和名字不符的API,不然调用你模版的人会打死你。

模版底层架构

require底层的实现非常复杂,这里简单阐述一下。

我们把node提供的模版称为核心模块,用户编写的模块称为文件模块

模块的加载主要由以下三步组成:

  1. 路径分析

    解析模块路径,转换成真实路径,放入缓存中,第二次调用可以加速。

  2. 文件定位

    因为模版定义的时候没有指定后缀名,所以确定后缀名。
    node会按后缀名为.js、.node、.json的顺序查找路径和名字匹配的文件。

  3. 编译执行

    • 对于js文件,仅仅在外面包装一层函数,以防止全局环境污染。

    • 对于node文件,因为本就是C/C++模版编译生成的,所以不需要编译。经过libuv兼容层,还能让node文件跨平台。

    • 对于json文件,采用JSON.parse()解析成js对象。

模版在第一次运行时三步都要执行,之后借助node的缓存机制,可以直接运行。并且缓存区的调用优先级高于未加载过的模版。

核心模块加载只有路径分析这一步要做,因为大多数库都是底层C/C++封装的,是经过编译的二进制文件,放在内存中。

而且核心模块的优先级高于文件模块,所以你命名一个和标准库一样的模版是不会调用成功的。

发布了43 篇原创文章 · 获赞 20 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_33384402/article/details/105057564