欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Node基础(1)--模块机制

程序员文章站 2022-03-03 17:40:30
...

JavaScript从最开始的表单校验到现在的大型应用,大概经历工具类库、组件库、前端框架、前端应用的变迁。在经过了很长时间的努力,JavaScript不断被类聚和抽象,通过script标签的方式去引用代码的方式显得杂乱无章。不得不使用命名空间的方式去约束代码,以达到安全易用的目的。
JavaScript的痛点在

  • 没有模块系统
  • 标准库较少
  • 没有标准的接口
  • 缺乏包管理系统

CommonJS规范

CommonJS规范制定了一个良好的愿景——希望JavaScript在任何地方都可以去运行

Node借鉴CommonJS的Module规范实现一套易用模块,NPM对package的完好支持提高了Node应用的开发效率。

模块导出

export.add = function(a,b){
    return a + b
}

模块引用

var math = require('math');
var sum  = math.add(1,2);
console.log(sum);
// 3

模块标识

require()方法的参数,必须是小驼峰命名的字符串,相对路径和绝对路径。可以没有.js后缀。CommonJS引入的模块不用考虑变量污染和命名空间等。

Node的模块实现

Node模块分为两类,一种是Node提供的模块(fs,buffer等)称为核心模块,用户自己编写的模块叫文件模块。引入模块经过三个步骤

  1. 路径分析
  2. 文件定位
  3. 编译执行

** 核心模块在Node源代码编译过程中就加入了内存中,所以核心模块会跳过第一步和第二步在,在路径分析中优先判断,执行会更快。而文件模块运行中动态加载,需要完整的执行三步,会比核心模块慢。**

优先从缓存加载

在执行第一步和第二步之前,浏览器端会缓存文件提高性能,Node对引入过的模块会进行缓存,减少第二次引入带来的性能开销。浏览器是仅仅缓存文件,而Node会缓存编译和执行过后的文件。require会优先对缓存读取,核心模块的核心检查先于文件模块的缓存检查。

路径分析和文件定位

模块标识符分析

require接受不同的标识符作为参数。

  • 核心模块,如http,fs,path等Node自带的模块
    核心模块优先级仅次于缓存加载,加载过程最快。 原因在核心模块是对底层的内建模块的封装,内建模块是用C/C++编写,性能上优于脚本语言,且Node开始执行的时候,文件编译生成的二进制文件就被加入到内存中,直接可以执行。

  • .和…开头的相对路径,/开头的绝对路径的文件模块
    **以.、…和/开头的都会当成文件模块来处理,分析模块的时候会把路径转换为真实路径,将编译后的结果缓存。 **

  • 非路径形式的文件模块,自定义的connect模块。

模块路径的生成规则

  1. 当前文件目录下查找目标文件
  2. 父目录下查找目标文件
  3. 父目录的父目录下查找目标文件
  4. 递归查找,知道到根目录
    自定义模块是最慢的一种,查找方式类似JS的原型链和作用域链的查找。

文件定位

从缓存加载的优化策略中,不需要路径分析、文件定位和编译执行,加快了再次加载模块的效率。当文件没有扩展名的时候,会按照.js、.json、.node的形式去补充扩展名,依次尝试去查找。

Tips: 引用.json和.node文件的时候加上后缀名会更快。同步配合缓存可以大幅度缓解Node单线程中阻塞式调用的缺陷。

当分析标识符过程中,没有找到相应的文件,却找到了一个目录,这时候会把目录当成一个包来处理。Node会查找目录中的package.json文件(包描述文件),再去解析出包对象。

模块编译

JavaScript的每一个文件模块都是一个对象,每一个模块文件中存在import,exports,module三个变量。export对象是通过形参的方式传入的,直接赋值会改变形参的引用。

var sum = function(a,b){
    return a+b;
}
module.exports = sum; //good
exports = sum; //bad

还有C/C++模块和JSON文件的编译,后续会介绍。

内建模块

Node模块类型中,存在一种依赖层级关系,即文件模块可能依赖核心模块,核心模块依赖内建模块。

Node在启动时,会生成全局变量process,提供Binding()方法来协助加载内建模块。

AMD规范和CMD规范

AMD规范

AMD规范是CommonJS模块规范的一个延伸。
define(id?,dependencies?,factory)
模块id和依赖是可以选择的,跟Node定义模块不一样的是需要用一个define来明确定义一个模块,而在Node中实现是隐式包装。为了进行作用域隔离,避免变量污染。

CMD规范

CMD支持动态引入,CMD模块更接近Node对CommonJS规范的定义。
define(factory)
require、export和module通过形参传递给模块,需要模块时随时调用require就可以。

包与NPM

CommonJS的包规范,一定程度上解决了变量依赖、依赖关系,进一步组织了JavaScript代码。

包结构

  • package.json 包描述文件
  • bin 存放二进制文件的目录
  • lib 存放JavaScript代码的目录
  • doc 存放文档
  • test 存放测试用例的代码
相关标签: node