关于这篇文章早在去年年初的时候我就想写一片关于模块化的文章,但是推到现在才来完成也有很多好处,巩固之前对Node的理解。

一、什么是模块化

现实生活中的模块化例子, 手机组装: 摄像头、话筒、听筒、显示器、电池。。。。。。

非模块化开发的问题 :

1、命名冲突,比如全局污染。。。;

2、文件依赖,团队变大后,维护大量的文件依赖关系非常困难,公共模块的维护、升级很不方便团队变大后,维护大量的文件依赖关系非常困难,公共模块的维护、升级很不方便;

3、可维护性低等等。

二、模块化的演变过程

  从最简单的加减乘除运算来举例说明,为了方便理解这里都没有采用ES6的语法。

1、全局函数的方式——最原始的写法

    // 早期的开发过程中就是将重复使用的代码封装到函数中
// 再将一系列的函数放到一个文件中,称之为模块
// 缺点:存在命名冲突,可维护性也不高的问题
// 仅仅从代码角度来说:没有任何模块的概念
function convertor(a) {
return parseFloat(a);
} function add(a, b) {
return convertor(a) + convertor(b);
}

2、封装对象的方式

    // 有了传统编程语言中的命名空间
// 从代码层面就已经有了模块的感觉
// 避免了多处全局污染
// 缺点:没有私有空间,没有抽离私有成员
var calculator = {
add: function (a, b) {
return this.convertor(a) + this.convertor(b);
},
convertor:function(a){
return parseFloat(a)
}
};

3、私有空间的划分

    // 这里形成一个单独的私有的空间
// 高内聚,低耦合:模块内部相关性强,模块之间没有过多相互牵连,如convertor和add
// 缺点:可扩展性低
var calculator = (function () {
// 将一个成员私有化,外部无法访问和修改
function convertor(a) {
return parseFloat(a);
}
// 抽象公共方法(其他成员中都会用到的)
function add(a, b) {
return convertor(a) + convertor(b);
}
return {
add:add
}
})();

4、模块的扩展

  // calc_v2016.js
(function (window,calculator) {
function convert(input) {
return parseFloat(input);
}
calculator = {
add: function (a, b) {
return convert(a) + convert(b);
}
}
window.calculator = calculator;
})(window, {}); // 新增需求 remain
// calc_v2017.js
// 开闭原则:对新增开放,对修改关闭
(function (calculator) {
function convert(input) {
return parseInt(input);
}
// calculator 如果存在的话,我就是扩展,不存在我就是新加
calculator.remain = function (a, b) {
return convert(a) % convert(b);
}
window.calculator = calculator;
})(window.calculator || {});

5、第三方依赖

  // calc_v2016.js
(function (window,calculator) {
//对全局产生依赖,不能这样用
console.log(document);
function convert(input) {
return parseFloat(input);
}
calculator = {
add: function (a, b) {
return convert(a) + convert(b);
}
}
window.calculator = calculator;
})(window, {}); // 新增需求
// calc_v2017.js
(function (calculator,document) {
// 依赖函数的参数,是属于模块内部
console.log(document);
function convert(input) {
return parseInt(input);
}
calculator.remain = function (a, b) {
return convert(a) % convert(b);
}
window.calculator = calculator;
})(window.calculator || {},document);

  以上通过一些简短的代码介绍了模块化发展大致情况。

三、模块化规范

1、服务器端规范

   CommonJS---nodejs

2、浏览器端规范

  AMD---RequireJS 国外相对流行(http://www.requirejs.cn/)

  CMD---SeaJS 国内相对流行(http://seajs.org/)

3、ES6的module规范

在这里就不具体展示每种规范的具体写法了。

四、CommonJS规范

1、Node 采用的模块化结构是按照 CommonJS 规范

模块与文件是一一对应关系,即加载一个模块,就是加载对应的一个模块文件;

CommonJS 就是一套约定标准,不是技术; 用于约定我们的代码应该是怎样的一种结构;

2、CommonJS 模块的特点

所有代码都运行在模块作用域,不会污染全局作用域;

模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。module.exports不会再次执行该模块;

模块加载的顺序按照其在代码中出现的顺序;

3、模块的分类

自定义模块:就是我们自己写的功能模块文件。

核心模块:Node 平台自带的一套基本的功能模块。

第三方模块:社区或第三方开发好的功能模块,可以直接拿回来用。

4、模块的定义

(1)Node 内部提供一个 Module 构建函数。所有模块都是 Module 的实例,属性如下:

  module.id 模块的识别符,通常是带有绝对路径的模块文件名。

  module.filename 模块定义的文件的绝对路径。

  module.loaded 返回一个布尔值,表示模块是否已经完成加载。

  module.parent 返回一个对象,表示调用该模块的模块。

  module.children 返回一个数组,表示该模块要用到的其他模块。

  module.exports 表示模块对外输出的值。

(2)载入一个模块就是构建一个 Module 实例,一个新的 JS 文件就是一个模块

  导出方式:module.exports 和 exports

  exports.name = value;

  module.exports = { name: value };

  module.exports 是用于为模块导出成员的接口;

  exports 是指向 module.exports 的别名,相当于在模块开始的时候执行:var exports = module.exports。

5、用Node手写一个简单的require

function $require(files) {
const fs = require('fs');
const path = require('path');
let filename = path.join(__dirname, files);
   //注意,这里实现的缓存不是Node的缓存机制
$require.cache=$require.cache||{};
if($require.cache[filename]) return $require.cache[filename].exports; let dirname=path.dirname(filename);
let file = fs.readFileSync(filename);
let module = {
id:filname,
exports: {}
};
//保存module.exports重新赋值前的值
let exports = module.exports;
let code = `(function (module,exports,__dirname,__filename) {
${file}
})(module,exports,dirname,filename)`;
eval(code); $require.cache[filename]=module;
return module.exports;
}

五、Node加载文件规则

Node 使用 CommonJS 模块规范,内置的 require 函数用于加载模块文件。

require 的基本功能是,读入并执行一个 JavaScript 文件,然后返回该模块的 exports 对象。 如果没有发现指定模块,会报错。

require 加载文件规则:

(1)require 加载js文件时可以省略扩展名;可以直接加载json文件;

(2)通过 ./ 或 ../ 开头:则按照相对路径从当前文件所在文件夹开始寻找模块;

    require('../file.js'); => 上级目录下找 file.js 文件

(3)通过 / 开头:则以系统根目录开始寻找模块;

    require(‘/Web/Vue/vue-full/src/main.js'); => 以绝对路径的方式找;

(4)如果 require 传入的是一个目录的路径,会自动查看该目录的 package.json 文件,然后加载 main 字段指定的入口文件;

(5)如果package.json文件没有main字段,或者根本就没有package.json文件,则默认找目录下的 index.js 文件作为模块;

(6)如果参数字符串不以“./“ 或 ”/“ 开头,则表示加载的是一个默认提供的核心模块(位于 Node 的系统安装目录(node_modules)中;

(7)缓存文件的加载优先级最高,同名的系统模块要比自定义模块优先级高;

(8)Node在加载系统模块的时候,如果当前文件夹里面没有node_modules文件夹就会去逐层向上查找至项目根目录直到找到为止,如果没有就会报错。

六、总结

以上就是我对模块化加载的理解,当然这里没有去过多的写案例,目的是为了以后回来查阅方便,方便自己记忆。

浅谈Node中的模块化的更多相关文章

  1. 浅谈JS中的!=、== 、!==、===的用法和区别 JS中Null与Undefined的区别 读取XML文件 获取路径的方式 C#中Cookie,Session,Application的用法与区别? c#反射 抽象工厂

    浅谈JS中的!=.== .!==.===的用法和区别   var num = 1;     var str = '1';     var test = 1;     test == num  //tr ...

  2. 浅谈Java中的equals和==(转)

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str ...

  3. 浅谈Linux中的信号处理机制(二)

    首先谢谢 @小尧弟 这位朋友对我昨天夜里写的一篇<浅谈Linux中的信号处理机制(一)>的指正,之前的题目我用的“浅析”一词,给人一种要剖析内核的感觉.本人自知功力不够,尚且不能对着Lin ...

  4. 浅谈Java中的对象和引用

    浅谈Java中的对象和对象引用 在Java中,有一组名词经常一起出现,它们就是“对象和对象引用”,很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起 ...

  5. 浅谈Java中的equals和==

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: String str1 = new String("hello"); String str2 = ...

  6. 转【】浅谈sql中的in与not in,exists与not exists的区别_

    浅谈sql中的in与not in,exists与not exists的区别   1.in和exists in是把外表和内表作hash连接,而exists是对外表作loop循环,每次loop循环再对内表 ...

  7. 浅谈iOS中的userAgent

    浅谈iOS中的userAgent   User-Agent(用户代理)字符串是Web浏览器用于声明自身型号版本并随HTTP请求发送给Web服务器的字符串,在Web服务器上可以获取到该字符串. 在公司产 ...

  8. 浅谈JavaScript中的闭包

    浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...

  9. 浅谈sql中的in与not in,exists与not exists的区别

    转 浅谈sql中的in与not in,exists与not exists的区别   12月12日北京OSC源创会 —— 开源技术的年终盛典 »   sql exists in 1.in和exists ...

随机推荐

  1. opencv::点多边形测试

    点多边形测试 测试一个点是否在给定的多边形内部,边缘或者外部 double pointPolygonTest( InputArray contour, // 输入的轮廓 Point2f pt, // ...

  2. Spring Boot 2.X(十):自定义注册 Servlet、Filter、Listener

    前言 在 Spring Boot 中已经移除了 web.xml 文件,如果需要注册添加 Servlet.Filter.Listener 为 Spring Bean,在 Spring Boot 中有两种 ...

  3. vue——前端跨域

    ***针对的是不同域名.不同协议的跨域: 1.找到config文件中开发环境的配置文件——dev.env.js,在里面将要跨域的域名配置进去 2.找到config文件中线上环境的配置文件——prod. ...

  4. 图片放大缩小插件 zoom.js 怎么用

    代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf ...

  5. C#+Selenium抓取百度搜索结果前100网址

    需求 爬取百度搜索某个关键字对应的前一百个网址. 实现方式 VS2017 + Chrome .NET Framework + C# + Selenium(浏览器自动化测试框架) 环境准备 创建控制台应 ...

  6. python3 requests_html 爬取智联招聘数据(简易版)

    PS重点:我回来了-----我回来了-----我回来了 1. 基础需要: python3 基础 html5 CS3 基础 2.库的选择: 原始库  urllib2  (这个库早些年的用过,后来淡忘了) ...

  7. windows 360浏览器打开网站白屏

    1.场景 使用windows的360浏览器打开网页白屏 使用mac 谷歌,360,火狐浏览器打开均正常 2.原因 windows浏览器默认使用的是ie浏览器内核渲染的,js执行时发生错误 3.添加he ...

  8. webPack 4.0的零基础学习

    webPack 也更新到了4.0阶段,今天看了一下官网,总结一下,零基础的学习路径吧. (1)首先需要下载 webPake和webpack cli npm install webpack webpac ...

  9. 关于Map集合注意事项

    今日代码中循环Map时,采用循环主键 Map<Integer,Map<Integer,String>> status =  new HashMap<>(); Set ...

  10. CentOS6.5下安装JDK1.7+MYSQL5.5+TOMCAT7+nginx1.7.5环境安装文档

    ----------------CentOS6.5下安装JDK1.7+MYSQL5.5+TOMCAT7+nginx1.7.5环境安装文档----------------------- [JDK1.7安 ...