本文同步自我的个人博客:
上一篇讲了模块是如何被寻找到然后加载进来的,这篇则是如何导出属性方法以及自定义模块后缀等一系列模块问题。
exports 对象
这是最常见的对象了,以往导出都是这样写的。
// a.jsexports.bar = '属性';exports.fn = function () { return '方法' };// b.jsvar a = require('./a');console.log(a.bar); // 属性console.log(a.fn()); // 方法
一直都是这么用非常简单,但是不能直接用对象覆盖他,如下代码是不对的。
// a.jsexports = { bar: '属性', fn: function () { return '方法' }};// b.jsvar a = require('./a');console.log(a.bar); // undefinedconsole.log(a.fn()); // 报错
为什么不能直接覆盖呢,因为 exports
只是 module.exports
对象的引用。
module.exports
对象
如果要导出一整个对象或者构造函数之类的,就需要用到 module.exports
对象了。
// a.jsmodule.exports = { bar: '属性', fn: function () { return '方法' }};// b.jsvar a = require('./a');console.log(a.bar); // 属性console.log(a.fn()); // 方法
导出构造函数:
// klass.jsfunction klass () {}klass.prototype.say = function () { return '我是klass' };module.exports = klass;// b.jsvar Klass = require('./klass');var klass = new Klass();console.log(klass.say()); // 我是klass
module 其他属性
module 还有些其他属性,简单说下吧。
module.id
模块id,往往是模块路径module.children
子模块module.filename
模块路径module.loaded
是否加载完毕module.parent
父模块module.paths
各级 node_modules 目录路径
require 函数
说模块,当然离不开 require
函数,除了加载模块之外,他还有一些其他用法。
require.resolve
不会加载执行,只会返回模块的绝对路径。require.cache
缓存所有已经加载的模块,如果你要更新模块就要删除这里的缓存。
var a = require('./a');var p = require.resolve('./a'); // 得到模块路径delete require.cache[p]; // 删除模块缓存
虽然不清楚什么时候用,但是至少是这么用的,删除缓存后,下次加载这个模块,就会重新加载模块文件了。
require.extensions
自定义模块后缀
还有个 require.extensions
非常好用的属性,但遗憾的是官方提示弃用了这个属性,不过值得庆幸的是,这个属性永远不会被移除,只是不推荐使用而已。
我第一次注意到这个属性是在 ejs 源码里,当时很好奇这是个什么东西,去翻了下官方文档,发现捡到宝了。后来又发现 node 命令行下的 babel 模块也注册了自定义模块,后缀分别是 .es
, .es6
, .jsx
,可以直接 require('./a.es6')
或者是 require('./a.jsx')
得到编译好的 js 代码。
好了,不说废话了,来看个官方简单例子吧。
require.extensions['.json'] = function(module, filename) { var content = fs.readFileSync(filename, 'utf8'); try { module.exports = JSON.parse(content); } catch (err) { err.message = filename + ': ' + err.message; throw err; }};
这是 node 源码的 json 模块实现,我只是删除了一个 stripBOM 函数,为了方便大家理解。
当然官方不推荐这样做是有道理的,官方推荐编译为兼容的 js 代码模块运行,性能更佳,而不应该采用这样的自定义模块实现。
不过看用途而定了,有时候也不一定追求极致的性能。