Nodejs - 模块系统

avatarplhDigital nomad

模块系统及其模式

JavaScript是没有命名空间的,解决方案就是下面的代码,我们称之为揭示模块模式.

/**
 * 模块模式
 */
const module = (()=>{
  const privateFoo = () => {/*...*/}
  const privateBar = [];
  const exported = {
    publicFoo: () => {/**...*/},
    publicBar: () => {/**...*/}
  };
  return exported;
})();
console.log(module);

这个模块模式利用自执行函数创建的私有作用域,module函数只会导出拥有的模块,对于闭包内部变量,外部是无法访问的,这个就是nodejs模块化的基础.

Nodejs模块解释

CommonJs是一个旨在标准化JavaScript生态系统的团队,nodejs就是在这个基础上构建了模块化机制.

const fs = require('fs');
/**
 * 模块模式
 */
const _module = (()=>{
  const privateFoo = () => {/*...*/}
  const privateBar = [];
  const exported = {
    publicFoo: () => {/**...*/},
    publicBar: () => {/**...*/}
  };
  return exported;
})();

console.log(
  exports
);

function loadModule(file, module,require) {
  const wrappedSrc=`(function (module, exports, require) {
    ${fs.readFileSync(file, 'utf-8')}
  })(module, module.exports, require);`;
  eval(wrappedSrc);
}
loadModule('t.js')

modules.js源码简化版如下:

image

require 源码阅读

require仅仅只存在于模块内部的变量

console.log(require);

打印结果如下

{
  // [Function: require]
  resolve: { 
    // [Function: resolve] 
    // paths: [Function: paths] 
  },
  main: /*Module*/ {
    id: '.',
    exports: {},
    parent: null,
    filename: '/Users/pengliheng/www/adb/index.js',
    loaded: true,
    children: [],
    paths: [
      '/Users/pengliheng/www/adb/node_modules',
      '/Users/pengliheng/www/node_modules',
      '/Users/pengliheng/node_modules',
      '/Users/node_modules',
      '/node_modules'
    ]
  },
  extensions:{ 
    '.js': [Function], 
    '.json': [Function], 
    '.node': [Function] 
  },
  cache:{
    '/Users/pengliheng/www/adb/index.js': /*Module*/ {
      id: '.',
      exports: { },
      parent: null,
      filename: '/Users/pengliheng/www/adb/index.js',
      loaded: true,
      children: [],
      paths: [Array]
    }
  }
}

path 就是查找路径...

查找路径次序,依次查找本地,,本地的上层路径.

'/Users/pengliheng/www/adb/node_modules',
'/Users/pengliheng/www/node_modules',
'/Users/pengliheng/node_modules',
'/Users/node_modules',
'/node_modules'

一直找到父元素的顶级目录,依然找不到变量,这个时候就应该去查找全局模块,

1: $HOME/.node_modules 2: $HOME/.node_libraries 3: $PREFIX/lib/node

但是就我自己的macbook来说,真实的全局安装路径是: /usr/local/bin image

nodejs 中的全局变量

console.log('================================================');
console.log(module);
console.log('================================================');
console.log(require);
console.log('================================================');
console.log(__filename);
console.log('================================================');
console.log(__dirname);
console.log('================================================');
console.log(process === global.process);
console.log('================================================');

main 和 cache

一个是读取的,另一个是缓存的,但是并没有看到其他文件的缓存.

路径读取文件

  • 1.如果模块是核心模块,例如require('http');则直接读取,
  • 2.如果模块是'/'开头的,则从系统的root根目录读取文件,

extensions 文件名扩展

顺序依次是 js => json => .node 在相同目录下依次有以下文件
image

    1. If X is a file, load X as JavaScript text. STOP 优先 s文件
    1. If X.js is a file, load X.js as JavaScript text. STOP 其次 s.js文件
    1. If X.json is a file, parse X.json to a JavaScript Object. STOP 再 s.json文件
    1. If X.node is a file, load X.node as binary addon. STOP 再 s.ndoe文件

index文件

首先s不带后缀的文件会和s文件夹相冲突,具体原因我也不清楚,文件目录如下 image 优先读取本地文件,没有的话再读取文件夹的

  1. X/package.json 文件中的main字段. 如果读取的是文件, package.json中的main字段有第一优先权
  2. If X/index.js is a file, load X/index.js as JavaScript text. STOP 优先加载index.js
  3. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP 再index.json
  4. If X/index.node is a file, load X/index.node as binary addon. STOP 再index.node

module 函数源码

{
  id: '.',
  exports: {},
  parent: null,
  filename: '/Users/pengliheng/www/adb/index.js',
  loaded: false,
  children: [],
  paths: [ 
     '/Users/pengliheng/www/adb/node_modules',
     '/Users/pengliheng/www/node_modules',
     '/Users/pengliheng/node_modules',
     '/Users/node_modules',
     '/node_modules' 
  ],
}

模块的wrap 有什么用呢

(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});

1.让模块的顶级变量不是全局变量,而仅仅只是模块内的变量 2.可以更加方便的使用一些模块内部的顶级变量 - 这相当于模块内部的全局变量,但是在每个模块内部,这个变量代表了不同的值

模块内的一些顶级变量

__dirname 当前模块的文件夹路径 __filename 当前模块的文件路径 require module exports

require.resolve.paths('')

[ '/Users/pengliheng/www/adb/node_modules',
  '/Users/pengliheng/www/node_modules',
  '/Users/pengliheng/node_modules',
  '/Users/node_modules',
  '/node_modules',
  '/Users/pengliheng/.node_modules',
  '/Users/pengliheng/.node_libraries',
  '/usr/local/Cellar/node/10.10.0/lib/node' ]

当层层向上还拿不到模块的时候,就会去.nide_modules里面拿,其实全局模块会去系统工具里面拿... image

app.js

exports.dirname = __dirname;
exports.filename = __filename;
exports = {     // 这样赋值无效
  dirname: __dirname,
  filename: __filename, 
}
module.exports.app = 'app'

index.js

const app = require('./app');
console.log(
  app
);

console.log => result

{ 
  dirname: '/Users/pengliheng/www/adb/app',
  filename: '/Users/pengliheng/www/adb/app/index.js',
  app: 'app' 
}

Reference - 参考

require() 源码解读 - 阮一峰

Nodejs官方文档 - Modules