forked from addyosmani/basket.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
251 lines (172 loc) · 13 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>basket.js - a simple script loader that caches scripts with localStorage</title>
</head>
<link rel="stylesheet" type="text/css" href="resources/base.css">
<body>
<div id="container">
<h1>basket.js (0.2)</h1>
<h2>A simple (proof-of-concept) script loader that caches scripts with localStorage </h2>
<p>
<a href="dist/basket.js">basket.js</a> (3.3KB)
<a href="dist/basket.min.js">basket.min.js</a> (0.6KB gzipped)
<a href="https://twitter.com/share" class="twitter-share-button" data-text="basket.js - a simple script loader that caches scripts with localStorage" data-via="addyosmani">Tweet</a>
</p>
<div id="message" class="content"><p><strong>If you're seeing this message, something has gone wrong. Please ensure you are testing this page using a HTTP server.</strong></p></div>
<div class="content">
<h3>Introduction</h3>
<p>basket.js is a small JavaScript library supporting localStorage caching of scripts. If script(s) have previously been saved locally, they will simply be loaded and injected into the current document. If not, they will be XHR'd in for use right away, but cached so that no additional loads are required in the future.</p>
<h3>Why localStorage?</h3>
<p>Tests by Bing and Google have shown that there are performance benefits to caching assets in localStorage (especially on mobile) when compared to simply reading and writing from the standard browser cache. This project is currently working on replicating these tests in the public so that we have definitive statistics on whether this is true. </p>
<p>Developers have also been wondering why we opted for localStorage as opposed to alternatives such as IndexedDB. Jens Arps has shown that IDB is at present <a href="http://jsperf.com/indexeddb-vs-localstorage/2">significantly slower</a> than localStorage for reading and writing assets. Other developers exploring this space have also shown that localStorage works just fine for caching data (it's actually significantly <a href="http://www.webdirections.org/blog/localstorage-perhaps-not-so-harmful/">faster</a> in Safari at the moment than any other browser). </p>
<p>We believe that in time, once implementers have optimized localStorage, it will become more feasible to use cross-browser. In the mean time, we are also exploring solutions such as the <a href="http://www.html5rocks.com/en/tutorials/file/filesystem/">FileSystem API</a> as a storage mechanism for caching scripts locally.</p>
<h3>Project History</h3>
<p>This project was created as a response to a <a href="https://twitter.com/#!/souders/statuses/166928191649357824">tweet</a> by Steve Souders asking that the jQuery project consider caching jQuery in localStorage for performance reasons. Intrigued by the idea, we began exploring a minimalistic solution to it (just for the sake of experimentation).</p>
<p>You may remember Souders previously did research and a <a href="http://www.stevesouders.com/blog/2011/03/28/storager-case-study-bing-google/">write-up</a> on search engines making use of localStorage caching back in 2011. His conclusions were:</p>
<p><strong>Bing and Google Search make extensive use of localStorage for stashing SCRIPT blocks that are used on subsequent page views. None of the other top sites from my previous post use localStorage in this way. Are Bing and Google Search onto something? Yes, definitely.</strong></p>
<p>To help provide a more general solution for this problem, we put together a script loader that treats localStorage as a cache.</p>
<h3>Compatibility</h3>
<p>basket.js supports locally caching scripts in any browser with <a href="http://caniuse.com/#search=localstorage">localStorage capabilities</a>.</p>
<h3>API And Examples</h3>
<p>Let's briefly review the current basket.js API, which differs from the API supported in 0.1.</p>
<ul>
<li><strong>basket.require()</strong></li>
<li><strong>basket.add()</strong></li>
<li><strong>basket.remove()</strong></li>
<li><strong>basket.wait()</strong></li>
</ul>
<p><strong>basket.require( uri )</strong></p>
<p><em>uri:</em> A relative URI to a local script to load.</p>
<p><code>.require()</code> allows us to construct loader chains, each of which instruct basket.js to fetch (either externally or from a localStorage cached-copy) a script defined by a supplied uri. The basic loading of a single uri could be achieved as follows:</p>
<pre><code>basket.require('jquery.js');</code></pre>
<p>This will check to see if an item with the key <code>basket-jquery.js</code> exists in localStorage (i.e a cached copy of the script). If present, the script contents are read from storage and injected into the current page. If not present, the script will be fetched, cached in storage and then similarly injected into the document for use.</p>
<p>basket.js also supports chaining and so requesting multiple scripts is also straight-forward:</p>
<pre><code>basket
.require('jquery.js')
.require('underscore.js')
.require('backbone.js');
</code></pre>
<p><strong>basket.require( key )</strong></p>
<p>If a script has been saved to a specific key in localStorage (e.g <code>jquery</code>), it can be accessed using <code>require()</code> as follows:</p>
<pre><code>basket.require('jquery');</code></pre>
<p>Note that at present, JSONP/server-side middleware is necessary for the support of remote scripts. This is down to access of script content being limited under cross-origin policies.</p>
<p><strong>basket.add( uri, [,options], [,callback] )</strong></p>
<p><em>uri:</em> A relative URI to a local script to be saved to localStorage (but not executed)</p>
<p><em>options:</em> An object allowing you to configure the add operation. It supports two optional keys: overwrite - A boolean defining whether an entry being added can overwrite an existing entry with the same path/filename.</p>
<p><em>key:</em> A key-name under which to save a specific script
callback: An optional function to be executed once a script has been added to localStorage</p>
<p><code>add()</code> allows you to add scripts to localStorage without actually injecting them into the current document. It accepts a URI, which it then fetches the contents of and caches. If the overwrite argument is supplied (and is <code>true</code>), basket.js will overwrite an existing entry for the script being added if one is found.</p>
<pre><code>basket
.add('jquery.js')
.add('handlebars.js')
.add('jquery.plugin1.js')
.add('jquery.plugin2.js');
</code></pre>
<p>An example of how to use the callback is as follows:</p>
<pre><code>basket
.add('jquery.js')
.add('handlebars.js', {}, function(){
console.log('jQuery and Handlebars exist in localStorage now');
});
</code></pre>
<p>If you wish to store a script under a specific key name (e.g if you have a build process outputting a script of a name such as <code>012345.js</code> and would like to store it under say, <code>main</code>), this could be done as follows:</p>
<pre><code>basket.add('012345.js', { key: 'main' });</code></pre>
<p>Similarly, if you wish to store <code>jquery-1.7.1.min.js</code> as <code>jquery</code>, this can be done as well:</p>
<pre><code>basket.add('jquery-1.7.1.min.js', { key: 'jquery' });</code></pre>
<p>Finally if you would like to use options it is fairly trivial to add a script with an option to overwrite it if it already exists, store it under a specific key and add a callback to it as follows:</p>
<pre><code>basket.add('jquery-1.7.1', { key: 'foo', overwrite: false }, function() {
});
</code></pre>
<p><strong>basket.remove( uri )</strong></p>
<p><em>uri:</em> A relative URI to a local script to be removed from localStorage</p>
<p>remove() will simply remove a previously cached script from localStorage. An example of how to use it can be seen below:</p>
<pre><code>basket
.remove('jquery.js')
.remove('modernizr.js');
</code></pre>
<p><strong>basket.remove( key )</strong></p>
<p>If a script has been saved to a specific key in localStorage (e.g <code>jquery</code>), it can be removed using remove() as follows:</p>
<pre><code>basket.remove('jquery');</code></pre>
<p><strong>basket.wait( [,callback] )</strong></p>
<p><em>callback:</em> An optional function to be executed once a script has loaded</p>
<p>When <code>wait()</code> is inserted into a chain, it informs the chain to ensure previous scripts inserted into the chain complete their loading prior to allowing the next item to load and execute. At it’s most basic, <code>wait()</code> can be used as follows:</p>
<pre><code>basket
.require('jquery.js').wait()
.require('jquery.plugin1.js')
.require('jquery.plugin2.js');
</code></pre>
<p>You can also specify an (optional) callback (typically an anonymous function) that will be executed in order following the previous scripts executing and before subsequent scripts in the chain are executed.</p>
<pre><code>basket
.require('jquery.js')
.require('underscore.js')
.require('app.js').wait(function(){
var numbers = [10, 5, 100, 2, 1000];
$('#message')
.html('Live long and prosper, bro.
Want the minimum value in our array? Boom:' + _.min(numbers));
});
</code></pre>
<p>Finally, in case you're interested, here's the basket.js example that was used to power the intro to this page:</p>
<pre><code>var message, str;
basket
.require('test/jquery-1.7.1.min.js').wait()
.require('test/underscore-min.js')
.require('test/backbone-min.js').wait(function() {</p>
// Backbone is now available, so let's define
// a test model with a sample attribute
var testModel = Backbone.Model.extend({
idAttribute: '_id'
});
// Create an instance of the model
var myModel = new testModel({ _id: 4, name: 'Nyan Cat' });
// Cache a reference to our message holder with jQuery
message = $('#message');
// Where #intro contains a template, get our templating on:
var templateContent = $('#intro').html();
var compiled = _.template(templateContent);
message.html( compiled({name: myModel.get('name')}));
});
</code></pre>
<p> </p>
<h3>The Future</h3>
<p>We are currently investigating a number of different features we would like to bring to the project. Some of these include:</p>
<ul>
<li><a href="https://github.com/addyosmani/basket.js/issues/27">Support for TTL</a></li>
<li><a href="https://github.com/addyosmani/basket.js/issues/26">Intelligent file-versioning</a></li>
<li><a href="https://github.com/addyosmani/basket.js/issues/25">Adding support for multiple files to <code>.require()</code></a></li>
<li><a href="https://github.com/addyosmani/basket.js/issues/24">Performance benchmarks (compared to IndexedDB, Browser cache and more)</a></li>
</ul>
<p> </p>
<h3>Team, License & Contribution Guide</h3>
<p>basket.js is released under an MIT/GPL license and is currently maintained by <a href="https://github.com/addyosmani">Addy Osmani</a>, <a href="https://github.com/sindresorhus">Sindre Sorhus</a> and <a href="https://github.com/peol">Andrée Hansson</a>. We would also like to extend our thanks to <a href="https://github.com/rwldrn">Rick Waldron</a> for the optimizations he suggested for improving the project.</p>
<p>For more information on our style-guide and how to get involved with basket.js, please see the README in our project <a href="http://github.com/addyosmani/basket.js">repo</a>.</p>
</div>
</div>
<script type="text/template" id="intro">
<p><em>This section was rendered using the micro-templating in Underscore.js. It's just one of a number of scripts that were added to this document using basket.js when you loaded it and should now be <strong>cached</strong> in localStorage (if supported). If you visit this page again, the scripts will be loaded from there rather than being refetched. Backbone.js was also loaded and we created a new model with the name: <%= name %> (just for funsies).</em></p>
</script>
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
<script src="dist/basket.min.js"></script>
<script>
var message, str;
basket
.require("test/jquery-1.7.1.min.js").wait()
.require("test/underscore-min.js")
.require("test/backbone-min.js").wait(function() {
var testModel = Backbone.Model.extend({
idAttribute: "_id"
});
var myModel = new testModel({ _id: 4, name: "Nyan Cat" });
//basket.remove('test/backbone-min.js');
//basket.require('test/modernizr.js');
message = $('#message');
var templateContent = $('#intro').html();
var compiled = _.template(templateContent);
message.html( compiled({name: myModel.get('name')}));
});
</script>
<a href="http://github.com/addyosmani/basket.js"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://a248.e.akamai.net/assets.github.com/img/e6bef7a091f5f3138b8cd40bc3e114258dd68ddf/687474703a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub"></a>
</body>
</html>