我正在编写一个RequireJS加载器插件。该插件通过EasyXDM获取跨域的html片段。它是使用加载器语法调用的,如下所示:
'html!someTemplate,#fragmentSelector'
由于可能会发生许多重复请求,例如从同一HTML文档请求不同的片段,我想要缓存整个HTML文档和片段。但到目前为止我无法进行任何缓存,因为我对RequireJS加载器插件的理解显然有一个漏洞。我认为在通过调用提供的 onLoad()函数发出完成信号之前不会再次调用它。但这种情况并非如此。使用控制台语句进行调试显示,在我调用 onLoad()之前,已快速连续进行62次调用(此应用程序中共有62个资产请求)。我尝试在传递给异步部分之前检查这些缓存,但缓存中从来没有任何东西,因为所有62个调用都已经传递到异步部分。这62个异步调用确实返回了良好的数据,因此最终插件工作正常。但我的缓存没有,我不能为我的生活想象我们如何解决这个问题。任何帮助将不胜感激。
答案 0 :(得分:0)
好的,终于想出了如何做到这一点。在口头上,您要做的是将所有调用排队到加载程序,然后一次处理一个。在以下代码点,从队列中拉出一个调用进程:
以下是可能需要示例的任何人的代码
/**
* RequireJS plugin for loading templates cross domain via easyXDM
* Author: Larry Gerndt
* Version: 0.0.1 (2013/5/1)
* Usage: html!url,fragment-selector
* url: omit the .html extension
* fragment-selector: css selector of the fragment to extract
*/
define(['assetLoader','jquery'], function(AssetLoader, $) {
/**
* Caches documents and fragments of documents
* The hash is derived from everything following the bang (!)
* For example, given this: html!assets/templates/IntroductionTooltip/introduction-tooltip,#mint-itt, we just
* strip out all illegal characters using the _hash() function and that's our hash for fragments. But we also
* want to cache the document from which the fragment came, in case a request is made for a different fragment from
* the same document. The hash for the document cache is made the same way as for fragments, except first omitting
* everything from the comma to the end of the line. In other words, omitting the fragment selector.
*/
function Cache(name) {
this.name = name;
this.cache = {};
this.size = 0;
}
Cache.prototype = {
get: function(name) {
return this.cache[name];
},
has: function(name) {
return this.cache.hasOwnProperty(name);
},
add: function(name, html) {
this.cache[name] = html;
this.size += 1;
}
};
//-----------------------------------------------------------------------------------------------------------
// a FIFO queue that stores calls to this module
//-----------------------------------------------------------------------------------------------------------
function CallQueue() {
this.store = [];
}
CallQueue.prototype = {
push: function(name, req, onLoad, config) {
this.store.push({
name : name,
req : req,
onLoad: onLoad,
config: config
});
},
pop: function() {
return this.store.length ? this.store.splice(this.store.length - 1, 1)[0] : null;
},
isEmpty: function() {
return this.store.length === 0;
}
};
var documentCache = new Cache('document'),
fragmentCache = new Cache('fragment'),
callQueue = new CallQueue(),
processedFirstCall = false;
//-----------------------------------------------------------------------------------------------------------
// helpers
//-----------------------------------------------------------------------------------------------------------
function getFragment(html, fragmentSelector) {
var $container, fragmentHtml;
if (!document.getElementById('mint-html-container')) {
$('body').append('<div id="mint-html-container" style="display:none;"></div>');
}
$('#mint-html-container').html(html);
fragmentHtml = $(fragmentSelector).get(0).outerHTML;
return fragmentHtml;
}
function hash(string) {
return string.replace(/[\/,#\.\s\-]/g, '');
}
function loadRemoteAsset(url, fragmentSelector, onLoad) {
var documentHash = hash(url),
fragmentHash = hash(url+fragmentSelector);
AssetLoader.loadHtmlFragment(url + '.html', fragmentSelector).then(function(fragmentHtml, allHtml) {
documentCache.add(documentHash, allHtml);
fragmentCache.add(fragmentHash, fragmentHtml);
onLoad(fragmentHtml);
processOneCall();
}, function() {
onLoad.error('AssetLoader: failed for unknown reason');
});
}
function processOneCall() {
if (!callQueue.isEmpty()) {
var item = callQueue.pop(),
split = item.name.split(','),
url = split[0],
fragmentSelector = split[1];
if (url.indexOf('/') === 0) {
url = item.config.baseUrl + url;
}
if (!url || !fragmentSelector) {
item.onLoad.error('AssetLoader: invalid argument: ' + item.name + '\n Example Usage: html!assets/templates/IntroductionTooltip/introduction-tooltip,#mint-itt');
}
else {
var documentHash = hash(url),
fragmentHash = hash(url+fragmentSelector);
if (fragmentCache.has(fragmentHash)) {
item.onLoad(fragmentCache.get(fragmentHash));
//console.log('using cached fragment for url: ', url, ', fragment: ', fragmentSelector);
processOneCall();
}
else if (documentCache.has(documentHash)) {
var fragmentHtml = getFragment(documentCache.get(documentHash), fragmentSelector);
fragmentCache.add(fragmentHash, fragmentHtml);
item.onLoad(fragmentHtml);
//console.log('using cached document for url: ',url, ', fragment: ', fragmentSelector);
processOneCall();
}
else {
loadRemoteAsset(url, fragmentSelector, item.onLoad);
}
}
}
}
//-----------------------------------------------------------------------------------------------------------
// public API
//-----------------------------------------------------------------------------------------------------------
return {
load : function(name, req, onload, config){
callQueue.push(name, req, onload, config);
if (!processedFirstCall) {
processedFirstCall = true;
processOneCall();
}
},
pluginBuilder: 'html-builder' // uses this no-op module during Optimizer build to avoid error "window is not defined"
};
});