
Web 1.5 or partial replacement of Ajax
Hello% Habrauser%.
I want to share with you the idea / implementation of an analog of ajax technology which, unlike the first, is devoid of its main shortcomings. From the ajax wiki, we know that:
The invented idea of Template client cache (hereinafter TCC) has the same advantages as ajax, but is completely devoid of its shortcomings.
In the case of ajax, the main "trick" is to load individual blocks, which significantly saves traffic. But the big problem with ajax is the strong incompatibility with both old browsers and search engines, as well as complex integration into existing projects. Solving the puzzle by rewriting the next site on blocks with ajax, I thought, is it really not possible to achieve a similar result with modern technologies but with less implementation and compatibility costs? So ... let's finish pouring water ... the idea is insanely simple: load only what we need to update (well, really, it looks a lot like the ajax idea). The whole issue is implementation. The basis was modern standards for supporting some kind of storage for JS needs (such as localStorage) and the idea of caching templates on the server side. Crossing them we got TCC. Those., as in the case of templates on the server, the entire page is usually divided into pieces that are processed and formed by separate files (pieces of code), are combined and discarded by the user. Often, some pieces are cached and not re-formed for some time. The idea is to transfer this cache of some pieces to the client side and create an infrastructure for transferring information from the client to the server and back to maintain the cache in a liquid state. Those. It turns out that when loading the page, we will, as usual, form it on the server side, skipping some blocks, but not taking them from the server cache, but simply leaving them empty, and in this form we will transfer it to the client, where it will combine them with the existing cache and display the resulting page to the user. From the description it is clear that:

In total, the sequence of work of TCC can be represented as follows:
It’s clear that I will collect the demo example in a short line, and therefore I will turn a blind eye to many things (such as using jQuery for parsing html, as well as some lightness to get a quick result).
A list of what can be improved and as I give below, so do not be critical of the demo example (in terms of application to production).
First we need to decide how we will mark the blocks for caching. For simplicity, I took the standard html span element, since it has little effect on the blocks contained in it (i.e., it works normally with floating nested blocks and with an inline context). To mark that this is a marker, a special class was selected called by the following principle:
tcc__[_];
Where:
- Unique block number;
- Caching mode (about it below);
- cache lifetime;
The caching mode is designed to indicate to the client which block it is dealing with. Three cache modes were distinguished:
r (realtime) - a block not subject to caching (updated every time the page is reloaded);
s (server) - a block formed on the server and subject to caching (created if it is necessary to form or replace a block in the cache);
c (client) - a block formed on the server empty in which the client must place the cached data;
Let's move on to implementation (the server code will be in PHP, but I think it will not be difficult to rewrite it to anything). To begin with, we will write a simple function of forming the beginning of the block (in fact, this function, based on some input data, should form a standard span with a special class). As you can see, the function uses two external variables, $ curTime (current time in the required format), as well as $ tcc (list of client’s caches, and we’ll talk below about receiving it on the server from the client). The rest of the logic of the function is very simple and straightforward. Next, we need to write on the client side (i.e., on the JS side) a certain set of service functions for working with storage (in this case, localStorage).
Everything here is also simple ... in storSet we save data without forgetting to specify id and save ttl to check the validity of blocks. In storGet we retrieve the block by its id, check the validity of ttl and, if successful, return the cached block.
Then we need a megaparser, which will parse everything received from the server. There is also nothing much to talk about here, since the code is quite transparent. Found a block -> realized that it was -> produced the necessary action. As a result, the only thing left for us to do on the client side is to let the server know what cache we have, so that the server can correctly form subsequent pages. In the form of transport, a simple and elementary cookie was chosen.
Cookies are formed quite transparently, we go through the storehouse looking for our blocks and fill out the list in cookies indicating the ID and TTL of each block. In the given piece of code, another service function storExist is used, which is almost the same as storGet with the exception that it returns the TTL of the block if this block is valid. Well, on the client side, we basically did everything, now back to the server. At the very beginning, I mentioned a certain $ tcc array (for the function of forming marker blocks), which we need to form. Everything is elementary.
I must say that I am quite pleased with the result of the experiment. In the final script, I got a simple system that automatically processes the cache, merges and transmits as little traffic as possible. The resulting JS script (which should always be transmitted from the client to the server) takes 2k of compressed YUV but uncompressed GZip traffic, which, to me, is quite acceptable. It is also worth noting that in the case of old browsers or search engines, the client side will not support JS, localStorage or Cookie, which means the server will always think that there is no cache on the client side and will always form a full page (i.e. we got an automatic system of compatibility with old people, which for me is so very cool). It is also worth noting a fairly simple server implementation, which can easily be combined with some kind of template engine and mark blocks and cache them both on the client side and on the server without too much hemorrhoids for the programmer. Also, optionally, you can cache in this way JS and CSS placed directly in the body of the document (in the body of the document, by the way, in this case, you can include all the necessary scripts and styles). In this case, separating and separating each JS or CSS into a separate block, we get a convenient and elementary system for loading only the necessary JS and CSS code, minimizing server requests (even if this request is just a cache validation). it is possible to cache in this way JS and CSS placed directly in the body of the document (in the body of the document, by the way, in this case, you can include all the necessary scripts and styles). In this case, separating and separating each JS or CSS into a separate block, we get a convenient and elementary system for loading only the necessary JS and CSS code, minimizing server requests (even if this request is just a cache validation). it is possible to cache in this way JS and CSS placed directly in the body of the document (in the body of the document, by the way, in this case, you can include all the necessary scripts and styles). In this case, separating and separating each JS or CSS into a separate block, we get a convenient and elementary system for loading only the necessary JS and CSS code, minimizing server requests (even if this request is just a cache validation).
Obviously, you need to get rid of jQuery (well, this is understandable).
Change the marking of the blocks to something like this _[_] -->, in this case we can mark the blocks without worrying about compatibility with the standards and the integrity of the shared html (however, with span blocks we would have to cut out only the finished html blocks otherwise, if there is no support from TCC browser, we would have collapsed).
The main disadvantages of the idea itself are:
Well, actually that's all ...
I would like to hear reasonable criticism and ideas of optimization. I will be happy to answer all questions.
PS Unfortunately, I can not lay out an example of a working script, if someone tells me a place for 1 php file, I will be grateful.
UPD PS Link to a working example: catsmile.heliohost.org/Mear/tcc.php (thanks to catsmile for the place)
UPD.2PPS To eliminate the ambiguity of understanding the objectives of this topic I will clarify. I do not propose replacing ajax in the literal sense of the word. I propose only to consider the option of client caching in a particular implementation. Also, the demonstrated code does not pretend to be complete and was created only for the purpose of demonstrating the approach. The main goals of this topic are to collect ideas and comments about the idea of caching some blocks on the client side and methods of working with these blocks.
I want to share with you the idea / implementation of an analog of ajax technology which, unlike the first, is devoid of its main shortcomings. From the ajax wiki, we know that:
Benefits
- Traffic saving;
- Reducing the load on the server;
- Acceleration of the reaction of the interface;
disadvantages
- Lack of integration with standard browser tools;
- Dynamically loaded content is not available to search engines;
- Old methods of accounting for site statistics become irrelevant;
- The complication of the project;
The invented idea of Template client cache (hereinafter TCC) has the same advantages as ajax, but is completely devoid of its shortcomings.
Idea
In the case of ajax, the main "trick" is to load individual blocks, which significantly saves traffic. But the big problem with ajax is the strong incompatibility with both old browsers and search engines, as well as complex integration into existing projects. Solving the puzzle by rewriting the next site on blocks with ajax, I thought, is it really not possible to achieve a similar result with modern technologies but with less implementation and compatibility costs? So ... let's finish pouring water ... the idea is insanely simple: load only what we need to update (well, really, it looks a lot like the ajax idea). The whole issue is implementation. The basis was modern standards for supporting some kind of storage for JS needs (such as localStorage) and the idea of caching templates on the server side. Crossing them we got TCC. Those., as in the case of templates on the server, the entire page is usually divided into pieces that are processed and formed by separate files (pieces of code), are combined and discarded by the user. Often, some pieces are cached and not re-formed for some time. The idea is to transfer this cache of some pieces to the client side and create an infrastructure for transferring information from the client to the server and back to maintain the cache in a liquid state. Those. It turns out that when loading the page, we will, as usual, form it on the server side, skipping some blocks, but not taking them from the server cache, but simply leaving them empty, and in this form we will transfer it to the client, where it will combine them with the existing cache and display the resulting page to the user. From the description it is clear that:
- + We will not form unnecessary blocks and will not spend time on them on the server side (plus in performance, including the cost of caching these blocks);
- + We will not transmit unnecessary (duplicate) information from the server to the client (plus in download speed and reduced traffic);
- + We essentially do not change the structure and method of forming the document (plus the ease of integration into existing engines / sites, as well as the absence of problems with search engines and old browsers, because if they do not have TCC support, they will always receive a full version of the page with all blocks);

In total, the sequence of work of TCC can be represented as follows:
- The client requests the page;
- The server generates html dividing it into logical blocks with special block markers;
- Having received this page, the client divides it by markers and puts it in the cache;
- The client follows the link to another page of the same site;
- Server. knowing which blocks are cached, it forms a page consisting of empty block markers and filled block markers only for those blocks that are not relevant in the cache (that is, it essentially forms a diff between the old and new pages);
- The client, having received such a diff, extracts the necessary blocks from the cache and fills the page;
- Profit
Implementation
It’s clear that I will collect the demo example in a short line, and therefore I will turn a blind eye to many things (such as using jQuery for parsing html, as well as some lightness to get a quick result).
A list of what can be improved and as I give below, so do not be critical of the demo example (in terms of application to production).
First we need to decide how we will mark the blocks for caching. For simplicity, I took the standard html span element, since it has little effect on the blocks contained in it (i.e., it works normally with floating nested blocks and with an inline context). To mark that this is a marker, a special class was selected called by the following principle:
tcc_
Where:
The caching mode is designed to indicate to the client which block it is dealing with. Three cache modes were distinguished:
r (realtime) - a block not subject to caching (updated every time the page is reloaded);
s (server) - a block formed on the server and subject to caching (created if it is necessary to form or replace a block in the cache);
c (client) - a block formed on the server empty in which the client must place the cached data;
Let's move on to implementation (the server code will be in PHP, but I think it will not be difficult to rewrite it to anything). To begin with, we will write a simple function of forming the beginning of the block (in fact, this function, based on some input data, should form a standard span with a special class). As you can see, the function uses two external variables, $ curTime (current time in the required format), as well as $ tcc (list of client’s caches, and we’ll talk below about receiving it on the server from the client). The rest of the logic of the function is very simple and straightforward. Next, we need to write on the client side (i.e., on the JS side) a certain set of service functions for working with storage (in this case, localStorage).
function blockStart($id, $ttl = false)
{
global $curTime;
global $tcc;
if (!$ttl)
{
echo '';
return true;
}
else if (isset($tcc[$id]) && ($tcc[$id] > $curTime))
{
echo '';
return false;
}
else
{
echo '';
return true;
}
}
function storSet(id, ttl, value)
{
localStorage.setItem('tcc_' + id, ttl + ':' + value);
}
function storGet(id, ttl)
{
var data = localStorage.getItem('tcc_' + id);
if (data == null) return null;
var data_ttl = data.substr(0, 12).split(':', 1)[0];
if ((data_ttl != ttl) || (data_ttl <= curTime)) return null;
return data.substr(data_ttl.length + 1);
}
Everything here is also simple ... in storSet we save data without forgetting to specify id and save ttl to check the validity of blocks. In storGet we retrieve the block by its id, check the validity of ttl and, if successful, return the cached block.
Then we need a megaparser, which will parse everything received from the server. There is also nothing much to talk about here, since the code is quite transparent. Found a block -> realized that it was -> produced the necessary action. As a result, the only thing left for us to do on the client side is to let the server know what cache we have, so that the server can correctly form subsequent pages. In the form of transport, a simple and elementary cookie was chosen.
function tcc()
{
var item;
while ((item = $('[class^=tcc_]:first')) && (item.length != 0))
{
var cn = item.attr('class').split('_');
if (cn[2] == 's')
{
var nItem = item.clone();
var cItem;
while ((cItem = nItem.find('[class^=tcc_]:first')) && (cItem.length != 0))
{
cItem.replaceWith('');
}
storSet(cn[1], cn[3], nItem.html());
}
else if (cn[2] == 'c')
{
var data = storGet(cn[1], cn[3])
if (data == null)
{
alert('critical error');
}
else
{
var nItem = $('' + data + '');
var cItem;
while ((cItem = nItem.find('[class^=cache_tcc_]:first')) && (cItem.length != 0))
{
var ncn = cItem.attr('class').split('_');
var rItem = item.find('[class^=tcc_' + ncn[2] + '_]');
if (rItem.length != 0)
{
cItem.replaceWith('' + rItem.html() + '');
}
else
{
cItem.remove();
}
}
item.replaceWith(nItem.html());
}
}
if (cn[2] != 'c')
{
item.replaceWith(item.html());
}
}
...
}
function tcc()
{
...
var storage = '';
for (var i = localStorage.length - 1; i >= 0; i--)
{
var key = localStorage.key(i);
var prefix = key.split('_', 1)[0];
var key = key.substr(prefix.length + 1);
if (prefix != 'tcc') continue;
var ttl = storExist(key);
if (ttl != null)
{
if (storage.length != 0) storage += ',';
storage += key + '_' + ttl;
}
else
{
localStorage.removeItem('tcc_' + key);
}
}
var cookie = 'tcc=' + escape(storage) + ';path=/';
if (storage == '') cookie += ';expires=Thu, 01-Jan-1970 00:00:01 GMT';
document.cookie = cookie;
}
Cookies are formed quite transparently, we go through the storehouse looking for our blocks and fill out the list in cookies indicating the ID and TTL of each block. In the given piece of code, another service function storExist is used, which is almost the same as storGet with the exception that it returns the TTL of the block if this block is valid. Well, on the client side, we basically did everything, now back to the server. At the very beginning, I mentioned a certain $ tcc array (for the function of forming marker blocks), which we need to form. Everything is elementary.
function storExist(key)
{
var data = localStorage.getItem('tcc_' + key);
if (data == null) return false;
var data_ttl = data.substr(0, 12).split(':', 1)[0];
if (data_ttl <= curTime) return null;
return data_ttl;
}
if (isset($_COOKIE['tcc']))
{
$list = explode(',', $_COOKIE['tcc']);
for ($i = 0; $i < count($list); $i++)
{
$item = explode('_', $list[$i]);
if (count($item) == 2)
{
$tcc[$item[0]] = $item[1];
}
}
}
Summarizing
I must say that I am quite pleased with the result of the experiment. In the final script, I got a simple system that automatically processes the cache, merges and transmits as little traffic as possible. The resulting JS script (which should always be transmitted from the client to the server) takes 2k of compressed YUV but uncompressed GZip traffic, which, to me, is quite acceptable. It is also worth noting that in the case of old browsers or search engines, the client side will not support JS, localStorage or Cookie, which means the server will always think that there is no cache on the client side and will always form a full page (i.e. we got an automatic system of compatibility with old people, which for me is so very cool). It is also worth noting a fairly simple server implementation, which can easily be combined with some kind of template engine and mark blocks and cache them both on the client side and on the server without too much hemorrhoids for the programmer. Also, optionally, you can cache in this way JS and CSS placed directly in the body of the document (in the body of the document, by the way, in this case, you can include all the necessary scripts and styles). In this case, separating and separating each JS or CSS into a separate block, we get a convenient and elementary system for loading only the necessary JS and CSS code, minimizing server requests (even if this request is just a cache validation). it is possible to cache in this way JS and CSS placed directly in the body of the document (in the body of the document, by the way, in this case, you can include all the necessary scripts and styles). In this case, separating and separating each JS or CSS into a separate block, we get a convenient and elementary system for loading only the necessary JS and CSS code, minimizing server requests (even if this request is just a cache validation). it is possible to cache in this way JS and CSS placed directly in the body of the document (in the body of the document, by the way, in this case, you can include all the necessary scripts and styles). In this case, separating and separating each JS or CSS into a separate block, we get a convenient and elementary system for loading only the necessary JS and CSS code, minimizing server requests (even if this request is just a cache validation).
Cons (or what can be done differently)
Obviously, you need to get rid of jQuery (well, this is understandable).
Change the marking of the blocks to something like this _
The main disadvantages of the idea itself are:
- The need on the server side to monitor the relevance of the cache for each client (for example, in the case of a basket of goods in an online store, should also change when the basket is changed and be unique to the client, i.e. we must somehow understand that the client’s cache is outdated);
- Potential growth of cookies, which leads to an increase in traffic from client to server (but this is precisely a potential problem, since in the end it all depends on the selected TTL blocks and their number;
Well, actually that's all ...
I would like to hear reasonable criticism and ideas of optimization. I will be happy to answer all questions.
UPD PS Link to a working example: catsmile.heliohost.org/Mear/tcc.php (thanks to catsmile for the place)
UPD.2PPS To eliminate the ambiguity of understanding the objectives of this topic I will clarify. I do not propose replacing ajax in the literal sense of the word. I propose only to consider the option of client caching in a particular implementation. Also, the demonstrated code does not pretend to be complete and was created only for the purpose of demonstrating the approach. The main goals of this topic are to collect ideas and comments about the idea of caching some blocks on the client side and methods of working with these blocks.