Browserify是否提供了将变量添加到它生成的最顶层闭包的方法?
我想定义一个不全局的变量,但我的所有模块都可以看到。 "使用的标准答案要求(......)"在我的情况下不起作用,因为变量的值在处理依赖关系时尚未准备就绪。我真的需要一种方法来在闭包中声明一个包含我所有模块的变量。
这是一个非常简化的例子,说明如果我没有使用Browserify并且我将所有模块放在一个文件中会是什么样子。我试图弄清楚如何在Browserify世界中定义类似myVar的内容,我不会在其中定义外部范围:
(function() {
var myVar; <-- This is what I want in the Browserify world. Non-global, outside the individual module closure.
var ModuleA = function() {
function a() {
console.log(myVar);
};
return { a: a };
}();
var ModuleB = function() {
function b() {
console.log(myVar);
};
return { b: b };
}();
function doItToIt() {
// Psuedo-code... assume I'm loading myVar asynchronously.
ajaxRequest(function(result) {
myVar = result;
ModuleA.a();
ModuleB.b();
});
}
doItToIt();
})();
答案 0 :(得分:2)
有关转换方法的详细信息,请参阅下面的编辑
抱歉迟到了。
有很多方法可以做到这一点。我不确定你要完成什么,因为你没有说你的代码试图做什么。以下是一些方法,按“最佳选择优先”排序:
(global||window)['myVar'] = result;
要小心全球名称冲突。// compile.js
var fs = require('fs'),
stream = require('stream'),
browserify = require('browserify'),
b = browserify({
basedir: './'
});
//Setup browserify transform, see NodeJS Stream transforms:
//https://nodejs.org/api/stream.html#stream_class_stream_transform
b.transform(function(file, opts) {
function MyTransform(opts) {
//Create buffer array
this.streamParts = [];
//Call Transform constructor on this class
stream.Transform.bind(this, opts)();
}
//Assign stream.Transform as parent prototype
MyTransform.prototype = Object.create(stream.Transform.prototype);
//Override _transform function to collect all chunks and store in our array
MyTransform.prototype._transform = function(data, encoding, done) {
//Store this chunk
this.streamParts.push(data);
done();
};
//On completion, merge (concat) chunks and turn into a string
MyTransform.prototype._flush = function(done) {
//Concat all chunks into a single buffer
var buf = Buffer.concat(this.streamParts);
//Turn buffer into a utf8 string
var src = buf.toString('utf8');
//Prefix with 'var myVar;' and push out the source body contents
this.push('var myVar;' + src);
done();
};
//Create and return our transform
return new MyTransform();
}, {
global: true //Warning: This will transform EVERY required file
});
//Require (and transform)
b.require('./main.js');
//Pipe to stdout... feel free to pipe to a file instead
b.bundle().pipe(process.stdout);
现在通过:nodejs compile.js
运行 browserify main.js | sed -e 's/function(require,module,exports){/(function(require,module,exports){var myVar;/g'
修改强>
为了回应你的评论,我做了这个快速简单的装载机功能。使用此匿名函数包装browserify包:
(function() {
var resources = {}, testCallbacks = [];
//This function loops through all "test" functions and calls
//them. If the test function returns "true", than the callback
//has been fired, and can be removed from the test list,
//otherwise all resources for that callback are not ready
//and will need to be tested again upon the load of the next
//resource
function fireReadyCallbacks() {
var remainingTests = [];
for (var i = 0, len = testCallbacks.length; i < len; i++) {
var cb = testCallbacks[i];
if (cb() !== true)
remainingTests.push(cb);
}
testCallbacks = remainingTests;
}
//Our fancy require function
function smartRequire(urls, readyCallback) {
function insertScript(url) {
//If resource is currently loading/loaded, just return
if (resources[url])
return;
//Add to 'global' resource list
var res = resources[url] = {
url: url,
loaded: false
};
//Insert script into page
var scr = document.createElement('script');
var eventName = (typeof scr.onload !== 'undefined') ? 'load' : 'readystatechange';
scr.addEventListener(eventName, function(e) {
//If IE says it isn't ready, return
if (eventName === 'readystatechange' && e.readyState !== 'complete')
return;
//TODO: test for error
//Mark resource as being fully loaded
res.loaded = true;
//Check if any callbacks are ready to fire
fireReadyCallbacks();
});
//Set async to true, and src to the url
scr.setAttribute('async', 'true');
scr.setAttribute('src', url);
//Insert into document
document.head.appendChild(scr);
}
//Setup a test callback and add to testCallbacks array.
//This function calls the specified callback ONLY
//if all resources defined have been loaded
testCallbacks.push(function() {
//Have all specified urls been successfully loaded?
for (var i = 0, len = urls.length; i < len; i++) {
var url = urls[i];
var res = resources[url];
if (!res || !res.loaded)
return false; //Nope!
}
//Call the callback
readyCallback();
//Inform fireReadyCallbacks that this callback is not needed anymore
return true;
});
//Add all resources to "resources" and insert all scripts into DOM
for (var i = 0, len = urls.length; i < len; i++)
insertScript(urls[i]);
//Test if we are ready to fire callback.
//This can happen if all resources are already loaded.
fireReadyCallbacks();
}
(function(){
/*Browserify bundle output goes here*/
})();
})();
然后,您可以设置browserify转换以包装所需的模块,如下所示:
smartRequire(['path/to/jquery','whatever/else/is/required'], function(){/*Place module output from browserify transform here*/});
以下是完成上述操作的完整代码:
// compile.js
var fs = require('fs'),
stream = require('stream'),
browserify = require('browserify'),
b = browserify(['main.js'], {
basedir: './'
});
//Setup browserify transform, see NodeJS Stream transforms.
//https://nodejs.org/api/stream.html#stream_class_stream_transform
b.transform(function(file, opts) {
function MyTransform(opts) {
//Create buffer array
this.streamParts = [];
//Call Transform constructor on this class
stream.Transform.bind(this, opts)();
}
//Assign stream.Transform as parent prototype
MyTransform.prototype = Object.create(stream.Transform.prototype);
//Override _transform function to collect all chunks and store in our array
MyTransform.prototype._transform = function(data, encoding, done) {
//Store this chunk
this.streamParts.push(data);
done();
};
//On completion, merge (concat) chunks and turn into a string
MyTransform.prototype._flush = function(done) {
//Concat all chunks into a single buffer
var buf = Buffer.concat(this.streamParts);
//Turn buffer into a utf8 string
var src = buf.toString('utf8');
//Wrap with our smartRequire... change resources as needed, can even
//place resources into a variable
this.push('smartRequire(["http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"],function(){' + src + '});');
done();
};
//Create and return our transform
return new MyTransform();
}, {
global: true //Warning: This will transform EVERY required file
});
var outFile = 'bundle.js';
console.log('Writing ' + outFile + '...');
var outputStream = fs.createWriteStream(outFile, {
flags: 'w',
encoding: 'utf8'
});
//Get the output stream from browserify
var dataStream = b.bundle(), firstChunk = true;
//Our smart require blob... minified for sanity's sake
var prefixBlob='function fireReadyCallbacks(){for(var c=[],e=0,f=testCallbacks.length;e<f;e++){var a=testCallbacks[e];!0!==a()&&c.push(a)}testCallbacks=c}function smartRequire(c,e){function f(b){if(!resources[b]){var c=resources[b]={url:b,loaded:!1},d=document.createElement("script"),a="undefined"!==typeof d.onload?"load":"readystatechange";d.addEventListener(a,function(b){if("readystatechange"!==a||"complete"===b.readyState)c.loaded=!0,fireReadyCallbacks()});d.setAttribute("async","true");d.setAttribute("src",b);document.head.appendChild(d)}}testCallbacks.push(function(){for(var b=0,a=c.length;b<a;b++){var d=resources[c[b]];if(!d||!d.loaded)return!1}e();return!0});for(var a=0,g=c.length;a<g;a++)f(c[a]);fireReadyCallbacks()};var resources={},testCallbacks=[];';
//Intercept the stream, and inject our prefix code
//before the first chunk
dataStream.on('data', function(chunk) {
if (firstChunk) {
outputStream.write('(function(){' + prefixBlob);
outputStream.write(chunk);
firstChunk = false
} else {
outputStream.write(chunk);
}
});
dataStream.on('end', function(chunk) {
//Add end of closure wrapper
outputStream.end((chunk) ? (chunk.toString('utf8') + '})();') : '})();');
});
dataStream.on('error', function(file, err) {
console.log('Error: ', file, err);
});
outputStream.on('finish', function() {
outputStream.close();
console.log('Compiled Success!');
});