nodejs学习笔记之包、模块实现

    简单了解了node的安装和一些基本的常识之后,今天学习了node中很重要的包和模块的一些知识点。
 
    首先学习一下包的规范,它由包结构和包描述两部分组成。包结构用于组织包的各种文件,包描述用于描述包的信息,供外部读取分析。
    
    完全符合CommonJS规范的包目录包含一下结构:
    
    package.json: 包的描述文件

    bin: 用于存放可执行的二进制文件的目录 

    lib: 用于存放javascript的目录 

    doc: 用于存放文档的目录 

    test: 用于存放单元测试用例的代码

    node_modules: 第三方模块

    README.md: 关于描述

  

    下面以知名框架express项目的package.json文件,讲解一下个参数的含义:
  
{

  "name": "express", //包名由小写字母和数字组成,包含._-,但不允许空格,包名须是唯一的

  "description": "Sinatra inspired web development framework", //包介绍

  "version": "4.4.4", //版本号,用于版本控制,一般是major.minor.revision格式

  "author": { //包作者

    "name": "TJ Holowaychuk",

    "email": "[email protected]"

  },

  "contributors": [ //贡献者列表,每个维护者由name、email和web组成

    {

      "name": "Aaron Heckmann",

      "email": "[email protected]"

    },

    {

      "name": "Ciaran Jessup",

      "email": "[email protected]"

    },

    {

      "name": "Douglas Christopher Wilson",

      "email": "[email protected]"

    },

    {

      "name": "Guillermo Rauch",

      "email": "[email protected]"

    },

    {

      "name": "Jonathan Ong",

      "email": "[email protected]"

    },

    {

      "name": "Roman Shtylman"

    }

  ],

  "keywords": [ //关键词数组,有利于用户快速查找到

    "express",

    "framework",

    "sinatra",

    "web",

    "rest",

    "restful",

    "router",

    "app",

    "api"

  ],

  "repository": { //托管源代码的位置

    "type": "git",

    "url": "git://github.com/visionmedia/express"

  },

  "license": "MIT", //许可证列表

  "dependencies": { //当前包所依赖的包列表

    "accepts": "~1.0.5",

    "buffer-crc32": "0.2.3",

    "debug": "1.0.2",

    "escape-html": "1.0.1",

    "methods": "1.0.1",

    "parseurl": "1.0.1",

    "proxy-addr": "1.0.1",

    "range-parser": "1.0.0",

    "send": "0.4.3",

    "serve-static": "1.2.3",

    "type-is": "1.2.1",

    "vary": "0.1.0",

    "cookie": "0.1.2",

    "fresh": "0.2.2",

    "cookie-signature": "1.0.3",

    "merge-descriptors": "0.0.2",

    "utils-merge": "1.0.0",

    "qs": "0.6.6",

    "path-to-regexp": "0.1.2"

  },

  "devDependencies": { //一些模块只有在开发的时候需要依赖,用于提示后续开发者

    "after": "0.8.1",

    "istanbul": "0.2.10",

    "mocha": "~1.20.1",

    "should": "~4.0.4",

    "supertest": "~0.13.0",

    "connect-redis": "~2.0.0",

    "ejs": "~1.0.0",

    "jade": "~1.3.1",

    "marked": "0.3.2",

    "multiparty": "~3.2.4",

    "hjs": "~0.0.6",

    "body-parser": "~1.4.3",

    "cookie-parser": "~1.3.1",

    "express-session": "~1.5.0",

    "method-override": "2.0.2",

    "morgan": "1.1.1",

    "vhost": "2.0.0"

  },

  "engines": { //支持的javascript引擎列表

    "node": ">= 0.10.0"

  },

  "scripts": { //脚本说明对象

    "prepublish": "npm prune",

    "test": "mocha --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",

    "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",

    "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/"

  },

  "bugs": { //反馈bug的地址

    "url": "https://github.com/visionmedia/express/issues"

  },

  "homepage": "https://github.com/visionmedia/express" //主页地址

}

  

    其次学习一下模块的实现,尽管规范中exports、require、module听起来很简单,我们还是需要了解一下这个过程中究竟经历了什么。
 
    node中引入模块需要经历三个步骤:路径分析、文件定位、编译执行。
    
    我们知道在node中模块主要分为两大部分:核心模块由node本身提供,文件模块由用户编写。它们的执行速度明显核心模块优于文件模块,因为核心模块在node编译的过程中,编译进了二进制执行文件,省略掉了文件定位和编译执行,并且在路径分析中优先判断。文件模块需要完整的路径分析、文件定位和编译执行。需要注意的是node中也有缓存机制,相同的模块在第二次加载的时候,优先从缓存加载,并且核心模块的缓存检查优于文件模块。
 
    第一个步骤:路径分析
    require接收一个表示符作为参数,标识符在node中分为以下几类:
  • 核心模块:如http、fs
  • .或..开始的相对路径文件模块
  • 以/开始的绝对路径文件模块
  • 分路径形式的文件模块
    前三类都很明确根据路径查找,不需要做过多的讲解。文件模块的路径生成规则是:首先查找当前目录下的node_modules目录;其次查找父目录下的node_modules目录;再次查找父目录的父目录下的node_modules;最后不断向上递归查找,直到根目录的node_modules目录。总而言之一句话:一直向上找,直到找到根目录,如果找不到会进入文件文件定位阶段。
 
    第二个步骤:文件定位
    我们知道在我们写require的时可以不叫扩展名,这个时候就需要一个规则,来判断到底使用的是什么后缀的文件,这里就会用到文件定位。首先会补全扩展名查找,补全的顺序是:.js、.node、.json。如果补全之后还没有找到的话,会把这个表示符作为一个目录查找,找到这个目录后会,查找当前目录下是否有package.json,提取main的属性值进行定位,如果没有main的话,会查找index,然后一次查找index.js、index.json、index.node,如果还是找不到的话,就会抛出查找失败的异常。
 
    第三个步骤:编译执行
    编译和执行是引入模块的最后一个阶段。定位到文件后,会新建一个模块对象,然后根据路径载入并编译。对于不同的扩展名,载入方式也不同。
  • .js文件,通过fs模块同步读取文件后编译执行
  • .node文件,这是C/C++编写的扩展文件,通过dlopen()方法加载最后编译生成文件文件
  • .json文件,通过fs模块同步读取文件后,用JSON.parse()解析返回结果
  • 其他文件,它们都被当作.js文件载入
    以上是学习过程中整理的笔记,方便以后学习。
 
 
 
    参考文献:
    深入浅出nodejs -- 朴灵

 

你可能感兴趣的:(nodejs)