To improve performance in Prebid (or any other header bidding setup), you can inline third-party SDKs/resources that are required to make bids. This removes network requests/roundtrips which can have a significant impact (especially on mobile) — cutting down time to first bid request.
One way to inline the scripts as part of your build process is to replace calls to adloader.loadScript
with a preprocessor directive that downloads the script and inlines its contents into the page. See below for instructions, with AOL/ADTECH as an example.
Add gulp-preprocess & request: npm install --save-dev gulp-preprocess request
, this will
let you add preprocessing directives to the code, and add the request
library to make HTTP requests.
Then add a gulp task to preprocess the code before it is bundled:
function preprocessFilter() {
return preprocess({
context: {
scripts: {
adtech: ['http://aka-cdn.adtechus.com/dt/common/DAC.js'],
yieldbot: 'http://cdn.yldbt.com/js/yieldbot.intent.js'
},
script: function (args) {
// it's an array, so get
// the first argument
var src = args[0],
exported = args[1],
callback = args[2];
if (toString.call(src) !== '[object Array]') {
src = [src];
}
var scripts = src.map(function (scriptSrc) {
var res = request('GET', scriptSrc, {
headers: {
'User-Agent': CHROME_UA
}
}),
script = res.getBody();
if (!script.length) {
throw scriptSrc + ' returned error';
}
return script;
}).join(';');
/**
* Since this will be evaluated in an IIFE, we want to avoid situations where the script sets a global variable
* implicitly (e.g., via `var globalVar = value`), by attaching that variable to the `window` object:
*/
var exportCheck = 'try{ if (' + exported + ' && !window.' + exported + '){window.' + exported + '='+ exported + ';} }catch(e){};';
/**
* this wraps the script's contents in an IIFE that
* is deferred, which should reduce the impact on frame-rate
* from the browser evaluating such a large/continuous chunk of JS.
* because it's asynchronous, we let the user provide a callback
* which gets called when the script & its global variable are available.
*/
return [
"/** ",
src,
" @",
(new Date),
" */;setTimeout(function(){",
"(1,eval)('",
jsStringEscape(scripts.toString()),
"');",
exportCheck,
callback + "();",
"}, 1);"
].join('');
}
}
});
}
gulp.task('preprocess', function () {
return gulp.src(['src/*.js', 'src/**/*.js', 'src/*.json'])
.pipe(preprocessFilter())
.pipe(gulp.dest(buildDir));
});
Change the build-dev
task to first preprocess:
gulp.task('build-dev', ['preprocess', 'clean-dist'], function () {
return gulp.src([buildDir + '/prebid.js'])
.pipe(browserify({
debug: false,
standalone: 'pbjs'
}))
.pipe(derequire())
.pipe(header(banner, {
pkg: pkg
}))
.pipe(gulp.dest(releaseDir));
});
The @exec script()
directive works by downloading the script, putting
its code into a string that will get eval
'd, and then calling the
specified function when the script is evaluated/available.
For aol, we add a _onScript
function to the adapter scope:
var _scriptLoadQueue = [];
/**
* When the script is available, call any waiting handlers
* and then make further calls (pushes) immediate
*/
function _onScript() {
function _call(fn){ fn(); }
utils._each(_scriptLoadQueue, _call);
_scriptLoadQueue.prototype.push = _call;
}
function _callBids(params) {
bids = params.bids;
if (!bids || !bids.length) return;
_scriptLoadQueue.push(_reqBids);
}
/**
* Inline the specified script (provided in the gulpfile)
* @param {string} url
* @param {string} varName - ensure that this variable is available on
* the global scope.
* @param {string} callbackName - call this function (in this scope)
* when the script is ready/evaluated
* @return {Function} inline eval IIFE
*/
/* @exec script(scripts.adtech, 'ADTECH', '_onScript') */