当前位置: 首页 > news >正文

深入浅出node(2) 模块机制

这部分主要总结深入浅出Node.js的第二章  

 一)CommonJs

    1.1CommonJs模块定义

  二)Node的模块实现

    2.1模块分类

    2.2 路径分析和文件定位

      2.2.1 路径分析

      2.2.2 文件定位

    2.3 模块编译

      2.3.1 javascript模块编译

      2.3.2 exports和module.exports

  三)前后端共用的模块

    3.1 前后端侧重点

    3.2 AMD和CMD

    3.3 兼容多种模块规范  

一) CommonJs  在CommonJs的官网上写着这样一句话 javascript:not just for browsers any more  CommonJs是一种规范,它涵盖了模块.二进制.Buffer.文件系统.包管理等,node就是借鉴了CommonJs的Modules规范实现了一套非常易用的模块系统

  1.1 CommonJs模块的定义  主要分模块引用.模块定义.模块标识  CommonJs的模块导入导出机制可以使用户不必考虑变量污染等问题

/*模块引入*/
var
math = require("math"); /*模块标识 传递给require的参数*/ /*模块定义*/ exports.add = function(a,b) { return a + b; }

二)Node的模块实现   在Node中对规范进行了一定的取舍,也增加了一定自身需要的特性 node中引入模块主要分为3个步骤

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

  2.1 Node中的模块分类  在node中模块分为两类 Node提供的模块,核心模块. 用户编写的模块,文件模块

  • 核心模块 在node的源代码编译的过程中被编译进了二进制执行文件,在node进程启动的时候,部分的核心模块被直接加载到内存,所以引用这部分模块不需要文件的定位和编译执行,并且在路径分析中优先判断,所以加载速度最快
  • 文件模块  运行时动态的加载,需要完成的路径解析,文件定位,编译执行过程,加载速度相对较慢    

无论是核心还是文件模块 Node都会采用缓存优先的策略,不同于浏览器中缓存的是文件,Node中缓存的是编译和执行之后的对象

  2.2 路径分析和文件定位

    2.2.1 路径分析 node中根据require()中传入的标识符,来进行模块的查找和定位,对不同类型的标识符查找定位的方式会有一些区别  标识符只要分为下面几类

  1. 核心模块(核心模块的优先级的优先级仅次于缓存加载,在Node的源代码的编译过程中已经被编译成了二进制的代码,加载过程最快 无法加载与核心模块相同标识符的自定义模块,只能通过其他的方式加载与核心模块相同标识符的自定义模块)
  2. 绝对路径或者相对路径的文件模块(通过将相对路径和绝对路径转换成真实路径,并且以真实路径作为索引,将编译后的结果放到缓存中,由于指明了确切的文件位置,所以其加载速度慢于核心模块)
  3. 非路径形式的文件模块,通常为自定义模块(当前文件目录下的node_modules  父目录下的node_modules 父目录的父目录下的node_modules  沿路径向上逐级递归,直到根目录的node_modules   很像原型链的查找 所以自定义模块的查找速度最慢)       

    2.2.2 文件定位

  • 文件的扩展名分析 CommonJs中允许在标识符中不包含扩展名,这种情况下Node会按照.js,.node,.json的次序补足扩展名 在require的时候,是同步阻塞的判断文件是否存在的,此时加入你确定需求的文件的扩展名字是.node,.json,在require的时候补足扩展名,能加快一下访问速度
  • 目录分析和包  如果你通过require()的标示符查找到一个目录,Node会将这个目录当做包处理 Node会在当前的目录下查找package.json文件,通过JSON.parse()解析出包描述对象,从中读取出main属性执行的文件进行定位,如果该文件不存在扩展名,则进入扩展名解析的步骤 如果main执行的文件错误或者不存在package.json文件,Node会将index当做默认的扩展名,然后依次的查找index.js,index.json,index.json
  • 如果在通过上面的方式仍然没有定位到相应的文件或者模块,则上升到下一个模块路径进行查找    

  2.3 模块编译  在node中文件模块都是对象.类似下面的定义 在定位到具体的文件后,node会创建一个模块对象,然后载入和编译

  

function Module(id,parent) {
    this.id = id;
    this.exports = {};
    this.parent = parent;
    if(parent && parent.children) {
        parent.children.push(this);
    }
    this.filename = null;
    this.loaded = false;
    this.children = [];
}

  在引入模块的时候,对不同扩展名的文件node的载入方式也不同

  • .js 通过fs模块同步读取文件后编译执行
  • .node C++编写的扩展文件,通过dlopen()方法加载最后编译生成的文件  (.node模块是通过C/C++编译后生成的,所以只有加载和执行的过程,C/C++模块的优势是执行效率更高但相对于javascript模块来说开发门槛更高)
  • .json 通过fs模块同步读取文件后,用JSON.parse()解析返回结果
  • 其余扩展名 都会当做.js文件载入

  2.3.1 javascript模块编译 

     在编译的过程中,node会对获取到的内容进行封装 类似于下面的样子

(function(exports,require,module,__filename,__dirname){
    /**
     * 你的js代码
     */
})

这样模块之间就进行了作用域的隔离 然后通过vm原生模块的runInThisContext()方法执行返回一个function对象 然后将之前的模块对象的exports,require()方法,module,文件路径等信息传入给函数执行,执行之后将exports属性返回给调用方,并且将编译后的结果根据路径索引缓存到Module._cache上

  exports和module.exports 

    简单理解就是exports是module.exports的引用 具体看 exports和module.exports的区别

三)前后端共用的模块

  3.1 前后端侧重点

  • 浏览器端的javascript需要从同一个服务器分发到客户端,受限于带宽,读取速度慢
  • 服务器端的javascript是相同的代码需要多次的执行,受限于CPU和内存,后端代码直接从硬盘中读取加载速度快
  • node中模块的引入是同步的,但浏览器中同步的方式不可行    

  3.2 AMD和CMD规范

  • AMD模块定义的方式如下,它是CommonJs规范的一个延伸
define(id?,dependencies?,factory)

这里的factory就是实际代码的内容  在AMD中需要显示的定义一个模块,在node中这个过程是隐式包装

  • CMD CMD在引入的时候支持动态引入  

  3.3 兼容多种模块规范

;(function(name,definition){
    var hasDefine = typeof define === 'define',
        hasExports = typeof module !== 'undefined' && module.exports;
    if(hasDefine) {
        //AMD CMD
        define(definition);
    } else if(hasExports){
        //node环境
        module.exports = definition()
    } else {
        //挂在在window
        this[name] = definition();
    }
})(name,function(){
    var obj = {};
    retuen obj;
});

 

转载于:https://www.cnblogs.com/tiantianwaigong/p/6226675.html

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 基数排序LSD_Radix_Sort
  • python的httplib、urllib和urllib2的区别及用
  • Android Context 到底是什么?
  • react-native 基础:react-native-router-flux 的使用[意译]
  • 【Scrapy】 Feed exports 学习记录四
  • 使用jquery.PrintArea.js打印网页的样式问题
  • 2013年回顾,关于敏捷实践,关于j2ee等等
  • 解决 Springboot Unable to build Hibernate SessionFactory @Column命名不起作用
  • 读书笔记 --TCP :传输控制协议(一)
  • spring data mongo groupby实例
  • 缓冲器的学习
  • 理解 Linux shell 中的一个方言:21
  • HBase 数据读写流程
  • Jquery中$.get(),$.post(),$.ajax(),$.getJSON()的用法总结
  • powershell: 生成随机字符串
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • javascript 哈希表
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • 案例分享〡三拾众筹持续交付开发流程支撑创新业务
  • 前端代码风格自动化系列(二)之Commitlint
  • 首页查询功能的一次实现过程
  • 体验javascript之美-第五课 匿名函数自执行和闭包是一回事儿吗?
  • 通过git安装npm私有模块
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 中文输入法与React文本输入框的问题与解决方案
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • 基于django的视频点播网站开发-step3-注册登录功能 ...
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • ​ssh免密码登录设置及问题总结
  • ![CDATA[ ]] 是什么东东
  • #ifdef 的技巧用法
  • (CPU/GPU)粒子继承贴图颜色发射
  • (补充)IDEA项目结构
  • (定时器/计数器)中断系统(详解与使用)
  • (六)Hibernate的二级缓存
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (一)Neo4j下载安装以及初次使用
  • (转)详解PHP处理密码的几种方式
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .NET 4.0中的泛型协变和反变
  • .NET C# 配置 Options
  • .net core IResultFilter 的 OnResultExecuted和OnResultExecuting的区别
  • .NET Core 项目指定SDK版本
  • .NET 给NuGet包添加Readme
  • @manytomany 保存后数据被删除_[Windows] 数据恢复软件RStudio v8.14.179675 便携特别版...
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [ C++ ] 继承
  • [100天算法】-二叉树剪枝(day 48)
  • [Algorithm][动态规划][子序列问题][最长递增子序列][摆动序列]详细讲解
  • [bzoj4010][HNOI2015]菜肴制作_贪心_拓扑排序
  • [BZOJ4554][TJOI2016HEOI2016]游戏(匈牙利)
  • [BZOJ5125]小Q的书架(决策单调性+分治DP+树状数组)
  • [C++]STL之map
  • [Day 43] 區塊鏈與人工智能的聯動應用:理論、技術與實踐