CNode

实现自己的require函数

Lleizongmin发布于15 年前最后回复12 年前3 回复11740 浏览0 收藏

最近在写一个类似于“计划任务”之类的程序,需要动态载入js文件来运行,但是NodeJs中的require函数加载文件后会将其缓存,而且无法访问这些缓存,只得自己写了个require函数来代替。  基本功能 载入指定文件名的NodeJs模块,可自动搜索路径(与require函数差不多); 自动缓存载入的模块,当模块文件被修改时,自动清除该缓存; 沙箱功能允许在在模块里面调用自己扩展的函数; 实现原理  使用vm模块来编译并运行JavaScript代码 利用vm.runInNewContext()的沙箱参数来获取被载入模块文件中的module.exports 使用fs.watchFile()来监视文件是否被改动   程序代码 /** * require函数 * 兼容 NodeJs的 require 函数 * * $Id$ * @author 雷宗民 <leizongmin@gmail.com> * @version 0.23 * @date 2011-07-22 10:10:36 / // 使用说明: // ExtModule.require('文件名'); // ExtModule.require('文件名', {ExtFunc: function () {...}}); // ExtModule.require('文件名', {沙箱}, '目录'); // 1、如果文件名不是以 '.'、'/' 开头,则认为是系统内置模块,直接返回 require('模块名'); // 2、沙箱可以加入扩展的函数、变量等,以便在被载入的模块中使用 // 3、会自动匹配 .js、.node 后缀的文件,以及该目录的 node_modules 子目录、父目录的 node_modules 等,直至回溯到根目录 // 4、如果载入模块出错,则返回 string 类型的出错信息 if (typeof (m = ExtModule.require('xxx')) == 'string') console.log('Error: ' + m); // 5、首次载入文件,自动将其缓存;当该文件被改动时,自动删除该文件的缓存 var vm = require('vm'), path = require('path'), fs = require('fs'); ExtModule = {}; CACHE = {}; ExtModule.debug = function (x) { console.log('[ExtModule] ' + x.toString()); } ExtModule.wrap = function (c) { return '(function () { \n ' + //'var require = ' + c + '\n });'; } // 加载程序 n=名称, d=所在目录, sandbox=沙箱 ExtModule.require = function () { if (arguments.length < 1) { return 'Not enough arguments!'; } if (arguments.length == 1) { var n = arguments[0], d = __dirname, sandbox = {}; } else if (arguments.length == 2) { var n = arguments[0], d = __dirname, sandbox = arguments[1]; } else { var n = arguments[0], d = arguments[1], sandbox = arguments[2]; } //ExtModule.debug('require dir: ' + d); try { // 如果是系统模块 var _c = n.charAt(0); if (_c != '.' && _c != '/') { return require(n); } // 取得实际文件名 if (n.charAt(0) != '/') { n = path.resolve(d, n); } var filename = ExtModule.findModule(n); // 没有找到模块文件 if (!filename) { ExtModule.debug('Cannot find the module "' + n + '".'); return 'Cannot find the module "' + n + '".'; } // 检查是否在缓存中 if (!(filename in CACHE)) { // 首次编译 ExtModule.debug('compile ' + filename); var c = fs.readFileSync(filename); var s = vm.createScript(ExtModule.wrap(c), filename); for (var k in global) { sandbox[k] = global[k]; } var requireModule = { exports: {}, parent: module}; sandbox.exports = requireModule.exports; sandbox.__filename = filename; sandbox.__dirname = path.dirname(filename); sandbox.module = requireModule; sandbox.global = sandbox; sandbox.root = sandbox; sandbox.require = function (n) { return ExtModule.require(n, sandbox.__dirname, {}); }; var f = s.runInNewContext(sandbox); f.apply(); CACHE[filename] = { filename: filename, script: s, exports: requireModule.exports } // 监视文件是否改动 fs.unwatchFile(filename); fs.watchFile(filename, function (curr, prev) { ExtModule.debug('removeCache ' + filename); delete CACHE[filename]; }); } return CACHE[filename].exports; } catch (err) { ExtModule.debug('compile error: \n' + err.stack); return err.stack; // throw err; } } // 查找模块路径 n = 文件名, 必须以/开头 ExtModule.findModule = function(n) { var paths = []; var pdir = n; var basename = path.basename(n); var notParentDir = true; do { pdir = path.dirname(pdir); if (notParentDir) { paths.push(pdir); notParentDir = false; } if (pdir == '/') { paths.push(pdir + 'node_modules'); break; } else { paths.push(pdir + '/node_modules'); } } while (true); for (i in paths) { if (path.extname(basename) == '') { var files = [basename + '.node', basename + '.js', basename + '/index.js' ]; } else { var files = [basename]; } for (j in files) { var filename = path.resolve(paths[i], files[j]); try { //console.log('test ' + filename); var stat = fs.statSync(filename); if (stat.isFile()) { return filename; } } catch (err) {} } } } /* 模块输出 */ exports.require = ExtModule.require;

查看回复

回复 (3)

H
hainee#1·12 年前

哦也

K
kingapple#2·12 年前

node中require也是用JS实现的么?

A
anonymous#3·15 年前

just for a notifying. a new feature: 'require.cache' was added to 'require' to let you manipulate the module cache since v0.4.9. (it's a little hard to use pinyin for me so i type in english for this comment. sorry.)

参与回复
登录后即可参与回复。登录