实现自己的require函数
发布于 13 年前 作者 leizongmin 10582 次浏览 最后一次编辑是 8 年前

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

3 回复

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.)

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

回到顶部