In my previous post I’ve described a solution that allows you to inject scripts and styles in specific placeholders in a layout. Scripts are registered in views, but they will be rendered by global filters only when all output of a page is ready. However, when it comes to caching Sitecore will be able to record HTML generated by your component just after it is rendered. So none of the actions that will be required in other parts of the site will be triggered with cache enabled.
Approach
We need to simulate similar behavior that Sitecore has in the pipeline, but instead of HTML output, we need to save and restore the required actions. We will need 3 processors:
- Enter Context
- Save Actions
- Restore From Cache
Context & Cache Key
When we are talking about MVC components they all have their execution context, as you would need to provide different rendering items to each component. Sitecore will create it and push context to a stack when you enter it, and remove it from the stack and return to parent context, when a lifetime of args object for the current pipeline will come to an end. This would ensure that during the whole execution of the pipeline you will be able to get current rendering info.
Also if Sitecore finds out that the current component configured to be cached it would generate CacheKey. The Key will be used to identify the HTML output of a component in a site cache.
In the current solution, context will store information about the current caching scope and the key will be used to identify scripts in the cache.
public class EnterScriptCachingContext : RenderRenderingProcessor
{
public override void Process(RenderRenderingArgs args)
{
Assert.ArgumentNotNull(args, "args");
if (args.Rendered || string.IsNullOrEmpty(args.CacheKey)
|| RequireHelper.GetInstance().IsScriptCachingContextEntered) // prevent saving in caching context of nested controls
{
return;
}
this.EnterContext(args);
}
protected virtual void EnterContext(RenderRenderingArgs args)
{
var disposable = RequireHelper.GetInstance().EnterScriptCachingContext(args.CacheKey);
args.Disposables.Add(disposable);
}
}
In Line 7, we checking that if the caching context was entered already. We should be able to get all scripts for current and child controls when current control will be retrieved from the cache.
Line 16, create a context with current CacheKey (It is important to add it to an args.Disposables to exit context properly). Context creation will use Sitecore ContextService to push the ScriptCachingService object there.
public IDisposable EnterScriptCachingContext(string cacheKey)
{
return ContextService.Get().Push(new ScriptCachingContext(cacheKey));
}
Writing to Cache
Writing to cache would be similar to renderings cache implementation, we only need to generate peace of content that we would like to save.
|
|
In the case of scripts registration code will iterate over registrars and find scripts snippets marked with current **CacheKey **with method FindByCacheKey, which will return rendered script section. Before saving key would be enriched with the registrar id and index of this script so that we will be able to put scripts in the correct place in layout and remove duplication.
Restore Scripts
The last piece of this puzzle is to restore elements from the cache. If a component has cache options enabled and the site already has something in the cache then the processor will look for an element with the current key, registrar id, and index. When script snippets are retrieved they are registered similar to registration from view described in the initial post.
|
|
Usage
Finally, we need to register our pipelines in mvc.renderRendering pipeline in the order you find below.
|
|
That’s it. We could cache components that inject scripts and styles via global filters.
Share if you like the post. Follow me on Twitter @true_shoorik