[jQ plugin] Source Caching - Hệ thống tải tệp nguồn hỗ trợ nhớ tạm

  Bài viết hay nhất1
Code:
name: source caching
created: 17:10 - 01/22/2016
updated: 17:10 - 01/22/2016

Giới thiệu:
Khi bạn sử dụng hệ thống lưu tải tệp này, trang của bạn chỉ tải dữ liệu lần đầu, những lần gọi sau điều được lấy từ bộ nhớ đệm, tiết kiệm băng thông, thời gian tải trang và dữ liệu tối đa.
Sử dụng mã javascript tuần nên không cần chạy nền jQuery.
Chỉ hỗ trợ trình duyệt có HTML5.

Lưu ý:
- Tùy vào việc cập nhật tệp nguồn, hãy tùy chỉnh thời gian nhớ đúng với lịch cập nhật của bạn.
- Đối với việc sử dụng trong diễn đàn, bạn các quản trị nên tạo mới (refresh) mỗi lần thành viên đăng nhập hoặc lần truy cập đầu tiên.
- Hạn chế việc sử dụng cho tệp có tính thay đổi cao, có hệ khắc phục bằng việc giảm thời gian nhớ.

Kịch bản:
Code:
/*
name: source caching - lamhieu (@lh)
created: 17:10 - 01/22/2016
updated: 17:10 - 01/22/2016
*/

var _lh = {};
_lh.sourcecache = {
 key: 'lamhieu',
 request: {
 create: function () {
 var objectXHR = false;
 if (window.XMLHttpRequest) {
 var objectXHR = new XMLHttpRequest();
 }else if (window.ActiveXObject) {
 try {
 var objectXHR = new ActiveXObject("Msxml2.XMLHTTP.6.0");
 } catch (e) {
 try {
 var objectXHR = new ActiveXObject("Msxml2.XMLHTTP.3.0");
 } catch (e) {
 //.
 }
 }
 }
 return objectXHR;
 },
 get: function (url, callback) {
 var xhr = _lh.sourcecache.request.create();
 xhr.onreadystatechange = function() {
   if (xhr.readyState == 4) {
   callback({'return': true, 'status': xhr.status, 'response': (xhr.responseText ? xhr.responseText : null)});
   }
   };
 xhr.open("GET", url, true);
 xhr.send();
 return true;
 }
 },
 exec: function (type, content, callback) {
 var type = typeof type == 'string' ? type : null,
 content = typeof content == 'string' ? content : null,
 callback = typeof callback == 'function' ? callback : function () {};
 if (type == 'css' || type == 'stylesheet') {
 var cssTag = document.createElement('style');
 cssTag.type = 'text/css';
 if (cssTag.styleSheet) {
 cssTag.styleSheet.cssText = content;
 }else {
 cssTag.appendChild(document.createTextNode(content));
 }
 document.getElementsByTagName("head")[0].appendChild(csscssTag);
 callback(true);
 return true;
 }else if (type == 'js' || type == 'javascript') {
 eval(content);
 callback(true);
 return true;
 }
 },
 load: function (type, url, timeout, callback) {
 var type = typeof type == 'string' ? type : nullm
 url = typeof url == 'string' ? url : null,
 timeout = typeof timeout == 'number' ? timeout : null,
 callback = typeof callback == 'function' ? callback : function () {};
 if (type == null || url == null || timeout == null) {
 callback(false);
 return false;
 }
 var timeNow = Math.floor(Date.now() / 1000);
 var cachedListObj = _lh.sourcecache.cache.each();
 cachedListObj['sourcecache'] = typeof cachedListObj['sourcecache'] == 'object' ? cachedListObj['sourcecache'] : {};
 if (typeof cachedListObj['sourcecache'][url] == 'object') {
 var sourceObj = cachedListObj['sourcecache'][url];
 var sourceType = typeof sourceObj['type'] == 'string' ? sourceObj['type'] : null,
 sourceContent = typeof sourceObj['content'] == 'string' ? sourceObj['content'] : null,
 sourceExpired = typeof sourceObj['added'] == 'number' && typeof sourceObj['timeout'] == 'number' ? sourceObj['added'] + sourceObj['timeout'] : 0;
 if (sourceType == type) {
 if (sourceExpired >= timeNow) {
 var isExce = _lh.sourcecache.exec(type, sourceContent);
 callback({'return': true, 'loaded': isExce, 'content': sourceContent, 'added': false, 'cached': true, 'timeleft': ((timeNow + timeout) - sourceExpired)});
 return true;
 }
 }
 }
 return _lh.sourcecache.request.get(url, function (returnRequest) {
 if (typeof returnRequest == 'object') {
 if (typeof returnRequest['return'] == 'boolean' && returnRequest['return'] == true && typeof returnRequest['response'] == 'string') {
 var sourceContent = returnRequest['response'];
 var isExce = _lh.sourcecache.exec(type, sourceContent);
 var isAdded = _lh.sourcecache.cache.push({'url': url, 'type': type, 'content': sourceContent, 'timeout': timeout});
 callback({'return': true, 'loaded': isExce, 'content': sourceContent, 'added': isAdded, 'cached': false, 'timeleft': timeout});
 }
 }
 });
 },
 checkif: {
 json: function (str) {
 try {
        JSON.parse(str);
    } catch (e) {
        return false;
    }
    return true;
 },
 supported: function () {
 if (typeof localStorage == 'object') {
 return true;
 }else {
 return false;
 }
 },
 },
 storage: {
 write: function (data, callback) {
 var data = typeof data == 'object' ? data : {},
 callback = typeof callback == 'function' ? callback : function () {};
 data['sourcecache'] = typeof data['sourcecache'] == 'object' ? data['sourcecache'] : {};
 if (typeof localStorage != 'object') {
 callback(false);
 return false;
 }
 localStorage[_lh.sourcecache.key] = JSON.stringify(data);
 callback(true);
 return true;
 },
 get: function (callback) {
 callback = typeof callback == 'function' ? callback : function () {};
 var contentStorage = null;
 if (typeof localStorage === 'object' && typeof localStorage[_lh.sourcecache.key] === 'string') {
 if (_lh.sourcecache.checkif.json(localStorage[_lh.sourcecache.key])) {
 contentStorage = JSON.parse(localStorage[_lh.sourcecache.key]);
 }
 }
 callback(contentStorage);
 return contentStorage;
 }
 },
 cache: {
 push: function (object, callback) {
 var object = typeof object == 'object' ? object : {},
 callback = typeof callback == 'function' ? callback : function () {};
 var sourceUrl = typeof object['url'] == 'string' ? object['url'] : null,
 sourceType = typeof object['type'] == 'string' ? object['type'] : null,
 sourceContent = typeof object['content'] == 'string' ? object['content'] : null,
 sourceTimeout = typeof object['timeout'] == 'number' ? object['timeout'] : null;
 if (sourceType == null || sourceType == null || sourceType == null) {
 callback(false);
 return false;
 }
 var timeNow = Math.floor(Date.now() / 1000);
 var cachedListObj = _lh.sourcecache.cache.each();
 cachedListObj['sourcecache'] = typeof cachedListObj['sourcecache'] == 'object' ? cachedListObj['sourcecache'] : {};
 cachedListObj['sourcecache'][sourceUrl] = {'type': sourceType, 'content': sourceContent, 'added': timeNow, 'timeout': sourceTimeout};
 return _lh.sourcecache.storage.write(cachedListObj, function (isPushed) {
 callback(isPushed);
 return isPushed;
 });
 },
 each: function (callback) {
 var callback = typeof callback == 'function' ? callback : function () {};
 var cachedObj = _lh.sourcecache.storage.get();
 if (cachedObj == null) {
 cachedObj = {};
 }
 callback(cachedObj);
 return cachedObj;
 },
 clean: function (callback) {
 var callback = typeof callback == 'function' ? callback : function () {};
 var timeNow = Math.floor(Date.now() / 1000);
 var cachedListObj = _lh.sourcecache.cache.each();
 cachedListObj['sourcecache'] = typeof cachedListObj['sourcecache'] == 'object' ? cachedListObj['sourcecache'] : {};
 for (var sourceUrl in cachedListObj['sourcecache']) {
 var sourceObj = cachedListObj['sourcecache'][sourceUrl];
 if (sourceObj['added'] + sourceObj['timeout'] < timeNow) {
 delete cachedListObj['sourcecache'][sourceUrl];
 }
 }
 return _lh.sourcecache.storage.write(cachedListObj, function (isPushed) {
 callback(isPushed);
 return isPushed;
 });
 },
 refresh: function (callback) {
 var callback = typeof callback == 'function' ? callback : function () {};
 var timeNow = Math.floor(Date.now() / 1000);
 var cachedListObj = _lh.sourcecache.cache.each();
 cachedListObj['sourcecache'] = typeof cachedListObj['sourcecache'] == 'object' ? cachedListObj['sourcecache'] : {};
 return _lh.sourcecache.storage.write(cachedListObj, function (isPushed) {
 callback(isPushed);
 return isPushed;
 });
 }
 }
};
Thêm vào bất kỳ nơi vào bạn muốn, chèn trong thẻ hoặc trích dẫn ngoài đường dẫn riêng.
(Mẹo: thêm vào thẻ <head> để được tải)

Sử dụng:
Code:
var fileType = 'js', // kiểu tệp nguồn: css, js,..
 fileUrl = '...', // đường dẫn đến tệp nguồn
 fileTimeout = 12 * 60 * 60; // thời gian lưu trữ tạm, tín bằng giây. ví dụ: 12 * 60 * 60 là 12 giờ
 fileCallback = function (fileLoadedObj) {
      console.log(fileLoadedObj); // giá tri trả về
}; // hàm gọi lại sau khi hoàn tất
_lh.sourcecache.load(fileType, fileUrl, (12 * 60 * 60), fileTimeout);

Ví dụ:
Code:
_lh.sourcecache.load('js', 'https:\/\/ajax.googleapis.com\/ajax\/libs\/jquery\/2.1.4\/jquery.min.js', (24* 60 * 60));
đoạn mã trên sử dụng để tải jQuery từ Google libs, lưu trữ trong 1 ngày. Vì chưa đủ ngày nên mình thêm dấu \ vào mỗi dấu / để không bị chặn.

Kiểm tra khả dụng:
Code:
if (_lh.sourcecache.checkif.supported()) {
 // trình duyệt hỗ trợ.
}else {
 // trình duyệt không hỗ trợ.
}

Tags: [You must be registered and logged in to see this link.] [You must be registered and logged in to see this link.]
  Bài viết hay nhất2
Hướng dẫn thêm

Làm mới bộ nhớ tạm:
Code:
_lh.sourcecache.cache.refresh();
Nội dung đã lưu trữ sẽ được xóa hoàn toàn.

Dọn dẹp bộ nhớ tạm:
Code:
_lh.sourcecache.cache.clean();
Các nguồn đã hết hạn sẽ được xóa khỏi không gian lưu trữ.

Thông tin nguồn đanglưu trữ:
Code:
_lh.sourcecache.cache.each();
Giá trị trả về sẽ là thông tin các nguồn đang lưu trữ.
  Bài viết hay nhất3
Đây đâu phải là cache, nó chỉ là web storage. Mặc dù cách dùng nó có vài điểm giống cache nhưng nó không phải.
Về ý tưởng thêm giới hạn thời gian vào storage thì lúc trước cũng có vài người làm rồi nhưng nó không phù hợp với mục đích sử dụng storage nên không được phổ biến và... chìm vào quên lãng. Có một library khá bá đạo mảng này là [You must be registered and logged in to see this link.] hỗ trợ cả IE 6, 7.

Về code của bạn thì khá ổn, nhưng cách dùng thì có vài vấn đề:

  1. Hàm supported bạn kiểm tra hỗ trợ localStorage nhưng không kiểm tra hỗ trợ JSON.
  2. Không kiểm tra dung lượng lưu trữ, vì web storage thông thường chỉ cho phép tối đa 5Mb.
  3. Code của bạn chạy nó sẽ tạo thêm 1 request để tải lại file đó, và khi bạn dùng lại nó thì cũng không có cách nào chặn trình duyệt tải file trước. Như vậy bạn chạy cùng 1 code 2-3 lần sẽ dễ tạo nên lỗi, đặc biệt với js. Trường hợp bạn không nhúng css và js vào trang thì bỏ qua vấn đề này.
  4. Khi dùng lại code từ storage, css thì bạn nhúng internal toàn bộ, nó sẽ ảnh hưởng rất lớn đến css specificity, có thể làm lỗi giao diện. Còn với js, bạn dùng eval để chạy, nó chắn chắn sẽ chậm hơn nhiều, nhất là với những file lớn.
  5. Chỉ dùng được với css và js, trong khi 2 loại file này trình duyệt sẽ tự lưu vào cache, thậm chí nó sẽ lấy từ bộ cache nếu file bạn dùng giống với file cache của web khác (ví dụ dùng chung google cdn jquery).

Vấn đề phụ nữa là bạn đăng bài sai chuyên mục, mà dù sao Devs cũng chưa có chuyên mục cho việc này nên bỏ qua. Sau này bạn còn tiếp tục phát triển thêm các library thuần js mình sẽ tạo thêm mục mới (nếu nhiều) hoặc bổ sung vào mục này cho tiện. ^_^
  Bài viết hay nhất4
[You must be registered and logged in to see this link.]
[You must be registered and logged in to see this link.] wrote:...
Vấn đề phụ nữa là bạn đăng bài sai chuyên mục, mà dù sao Devs cũng chưa có chuyên mục cho việc này nên bỏ qua. Sau này bạn còn tiếp tục phát triển thêm các library thuần js mình sẽ tạo thêm mục mới (nếu nhiều) hoặc bổ sung vào mục này cho tiện. ^_^
cảm ơn lời góp ý của bạn.
khi nào có thời gian mình sẽ góp thêm vài kịch bản nữa, vì đã số các kịch bản điều được tối ưu cho mã nguồn của mình nên khi chuyển thể sang các kiểu khác khơi lằng nhằm :D, khi vọng được góp vui cùng mọi người.
  Bài viết hay nhất5
You cannot reply to topics in this forum