headLoader.js
headLoader.js借助indexedDB数据库实现网站快速加载。原理是在恰当时间将网站数据缓存到浏览器的数据库indexedDB,例如JS、CSS、JSON、图片等,使网站页面下次加载或刷新时,迅速的从indexedDB数据库获取有效资源,从而缩短响应时间。
- 主要功能:资源加载、预加载、热加载、跨页缓存管理、代码保护及隐藏等。
- 网站加速:由于使用纯前端的数据库缓存技术,基本上用"0成本"就能使普通的网站得到明显的加速及优化。
- 使用场景:其"按需加载"和"跨页缓存共享"等特点,可胜任大型网站对服务器高频资源海量请求的痛点。
快速上手
<script type="text/javascript" data-dir="/" data-js="a,b,c,d" data-css="a,b,c" src="/headLoader.min.js"></script>
以上代码为采用了headLoader.js技术,它可实现如下常规页面加载代码的功能
<script type="text/javascript" src="/a.js"></script>
<script type="text/javascript" src="/b.js"></script>
<script type="text/javascript" src="/c.js"></script>
<script type="text/javascript" src="/d.js"></script>
<link rel="stylesheet" href="/a.css" type="text/css">
<link rel="stylesheet" href="/b.css" type="text/css">
<link rel="stylesheet" href="/c.css" type="text/css">
headerLoader.js不仅能实现上述功能,还会自动将资源缓存到indexedDB,当刷新页面或者其它页面再次请求该资源时,直接从缓存读取而不会有额外的网络请求。
插件介绍
- 可实现一个script标签加载页面所需的全部css及js文档;
- 支持"js","css","svg","text","xml","json","html","htm"等文本文件的读取;
- 支持图片、视频、字体、图标库等二进制文件类型的读取;
- 当页面刷新或者重新载入时,会从缓存中优先读取,缩短响应时间,减少页面载入时的闪烁;
- 可实现资源预加载功能,即加载完成后存入缓存,当其它页面需要时从缓存快速读取;
- 资源热加载,即加载完成后自动应用,而不需要刷新页面;
- 缓存生命周期可自定义,即能复用本地缓存又能保证代码的时效性;
- 缓存生命周期结束后,如果服务器文件未修改,继续使用本地缓存,进一步减少网络流量提高效率;
- 生产环境自动加载(js、css)文档的.min版本,开发环境加载正常版本,开发及上线一气呵成,减少维护成本;
- 生产环境并行加载文档加快速度,开发环境串行加载利于观察程序运行;
- 生产环境程序在内存中运行便于保护及隐藏代码,开发环境正常在页面中运行方便调试与定位;
- 从v2.0.0版本后使用现代浏览器的indexedDB数据库存储缓存数据,容量不再受限制,尽可用它构建更大规模的项目。
使用方法
headLoader的使用方法有标签属性定义法和命令行语句法,可以任选其一,而对于更复杂的逻辑要求,也可以混合使用,比如用标签属性定义法加载调度和配置文件,在调度和配置文件中用命令行语句法来实现更灵活的加载.下面介绍一下两种方法的具体使用:
1.标签属性定义法
<script type="text/javascript" data-dir="/media/" data-css="public/css1,css2,_css" data-js="libs/jquery-3.1.0,libs/jquery.elfAlert,_js" data-font="font/a.woff,font/b.woff" data-file="html/a.html,svg/a.svg" src="/media/js.min/libs/headLoader.min.js"></script><!--引入js-->
2.使用代码程序的方法
也可以先让headLoader.js先加载一个调度文件,然后在调度文件中使用代码程序加载更多的文件,当然还是要首先引入headLoader.js
<script type="text/javascript" src="/media/js.min/libs/headLoader.min.js"></script><!--引入js-->
然后使用javascript定义
// 首先创建一个headLoader实例
let loader=new headLoader();
// String | js、css资源路径,默认"./" | 可选项
loader.dataDir="/media/";
// Array | CSS资源,默认为空数组"[]" | 可选项
loader.dataCss=['public/css1','public/css2','_css'];
// Array | JS资源,默认为空数组"[]" | 可选项
loader.dataJs=['libs/jquery-3.1.0','libs/jquery.elfAlert','_js'];
// Array | 字体资源,默认为空数组"[]" | 可选项
loader.dataFont=['font/a.woff','font/b.woff'];
// Array | 支持更多类型的资源,例如html、svg等,默认为空数组"[]" | 可选项
loader.dataFile=['html/a.html','svg/a.svg'];
// Number | 缓存生命周期参数lifeCycle,如果为整数时,,单位小时,默认24,是从2000/1/1 00:00:00开始每24个小时检查更新一次 | 可选项
// Array | 缓存生命周期参数lifeCycle,如果为数组时,例如[168,48],表示生命周期为168小时,延迟时间参数cycleDelay为48小时 | 可选项
loader.lifeCycle=24; //[注意]当数据类型为Array时,第一个值为lifeCycle,第二个值为cycleDelay
// Number | 每次缓存生命周期延迟时间,单位小时,默认0,建议最大值24 | 可选项
loader.cycleDelay=0;
// Boolean | 线下与线上路径是否自动切换,例如"/js/"转为"/js.min/","/a.js"转为"a.min.js",默认False | 可选项
loader.dataActive=false;
// Boolean | 是否显示加载统计,默认false | 可选项
loader.showLog=false;
// Number, 0或1 | 预加载开关 1:预加载打开(不应用于当前页面),0:预加载关闭(加载后立即应用于当前页面)。 默认0 | 可选项
loader.preload=0;
// Function | 外部命令法可以定义回调函数,参数为请求的数据结果 | 仅用于命令行模式 | 可选项
loader.callback=function(_data){console.log(_data)};
await loader.run()
// run()结果只有一个值时返回:{"/media/css/public/css.css":value, ...}
// run()结果有多个值时返回:[{"/media/css/public/css.css":value, ...},...]
// run()结果没有值时返回:false
// let result=await loader.run();
-或者在js中这样写:
let loader=new headLoader({
dataDir:"/media/",
dataCss:['public/css1','public/css2','_css'],
dataJs:['libs/jquery-3.1.0','libs/jquery.elfAlert','_js'],
dataFont:['font/a.woff','font/b.woff'],
dataFile:['html/a.html','svg/a.svg'],
callback:function(_data){console.log(_data)}
});
await loader.run()
标签属性定义法、命令行语句法在定义时需要特别注意命名方法和大小写的区别,特列下表:
标签属性定义法 | 命令行语句法 | |
---|---|---|
资源路径 | data-dir | dataDir |
CSS资源 | data-css | dataCss |
JS资源 | data-js | dataJs |
字体资源 | data-font | dataFont |
更多类型的资源 | data-file | dataFile |
缓存生命周期 | data-lifecycle | lifeCycle |
每次缓存生命周期延迟 | data-cycledelay | cycleDelay |
线下与线上路径是否自动切换 | data-active | dataActive |
是否显示加载统计 | data-showlog | showLog |
预加载开关 | (未提供) | preload |
生命周期计算方法
在实例化headLoader时有一个生命周期参数lifeCycle,用来定义从2000/1/1零点以来每lifeCycle小时更新一次数据。
获取更新时间点有如下作用:
对于网站管理者,要选择合适的时间点关闭网站,直到网站更新完毕。
对于用户,在更新时间点后打开网页将会使所需数据与服务器保持同步。
由于存储在客户端的数据都有一个有效期限,为了避免用户端加载的数据模块不能兼容,网站版本更新一定要算好生命周期时间点再进行操作。
例如:网站有a、b两个模块,本来应该零点更新网站,然而8点才更新,而且8点之前也没有关闭网站服务,那么用户在8点前加载了a模块,根据规则为旧版本的a模块做了延期,而8点后操作的过程中,需要b模块,那么程序会根据规则下载新版本的b模块,此时旧版本的a与新版本的b混用,如果不兼容,就会造成意外的风险。
网站新版本上线一定严格按照数据的生命周期来完成。
1.为了方便获取下次更新时间,掌握网站更新时机,可以用下面的方法计算:
let loader=new headLoader();
//如果每36小时更新一次,获取下次更新时间
loader.nextLife(36);
//返回一个日期格式的字符串,例如 '2022/5/21 12:00:00'
//如果每24小时更新一次,每次更新延迟3小时,获取下次更新时间
loader.nextLife(24,3);
//返回一个日期格式的字符串,例如 '2022/5/21 3:00:00'
2.您也可以试试这个工具:
强制清除缓存方法
1.脚本时间参数定义法
通过给headLoader.js路径修改不同的时间参数,可以强制清除生成的缓存数据。
例如:原来页面插入headLoader.js脚本如下:
<script type="text/javascript" src="/media/js.min/libs/headLoader.min.js?v=2022-05-25"></script><!--引入js-->
现在,将路径时间参数“v=2022-05-25”修改为“v=2025-07-07”,即可强制清除缓存数据,并重新加载最新的数据,示例如下:
<script type="text/javascript" src="/media/js.min/libs/headLoader.min.js?v=2025-07-07"></script><!--引入js-->
友情提示
- 参数必须采用时间格式或时间戳,例如"2025-07-07",使用其它形式的参数不会触发强制清除缓存的功能。
- 强制清除缓存后会记录时间戳参数,后续其它的页面有不同的参数时,如果时间小于记录的时间将不会再次执行清除缓存功能。
2.代码程序清除法
let loader=new headLoader();
//强制清除缓存,执行后需要刷新页面,否则可能造成模块缺失。`
loader.db.delete();
友情提示
- 强制清除缓存后重新刷新页面会有一定的时间重新加载,时间长短依据网络质量和数据包大小,请知晰;
- 此方法用于异常情况下的急救措施,正常情况下程序会根据默认的生命周期自动差异化更新数据。
JS/CSS预加载
在用户操作的空闲期间,将必要的资源预加载并保存到indexedDB,在用户切换到其它页面时,headLoader直接从indexedDB中获取,减少网络请求给页面渲染造成的卡顿。
一个预加载实例(您可以根据需要增加其它参数):
let loader=new headLoader();
loader.dataDir="/media/";
loader.dataCss=['css1','css2'];
loader.dataJs=['js1','js2'];
loader.preload=1;
await loader.run();
以上代码实现将以下资源预加载:
/media/css/css1.css
/media/css/css2.css
/media/js/js1.js
/media/js/js2.js
预加载利用闲时加载资源,当前页面或者其它页面需要时,将会直接从缓存中无延迟的加载,以达到快速响应的目的。
JS/CSS热加载
当程序运行过程中,headLoader可实现热加载,实现对模块随需随加载。
例如程序在运行过程中,需要xxx.js模块,而页面之前又未加载该资源,那么可以用如下方法实现热加载:
let hotLoad=function(_module){
return new Promise((_resolve,_reject)=>{
let loader=new headLoader();
loader.dataDir="/media/";
loader.dataJs=[_module];
await loader.run().then(_=>_resolve(_));
});
}
//假设模块路径为“/media/libs/xxx.js”
hotLoad("libs/xxx").then(_=>console.log(_));
热加载适用于代码较大但是又不确定是否会调用的脚本资源。
加载并缓存字体
headLoader可以将一些操作系统没有的字体缓存下来,使整个网站所有页面再次使用时可以快速载入。
例如网站需要使用elf_round字体,而操作系统又没有安装这个字体,如果使用通常的CSS链接方式,会使页面每次载入时都需要重新从服务器请求,导致效率很低,那么可以通过headLoader.js加载字体,使字体包加载一次后便缓存下来,后续使用字体可以快速从缓存中获取:
let loader=new headLoader();
//字体包路径为“/font/elf_round.woff”
//如果需要更多字体在数组中追加正确路径即可
loader.dataFont=["/font/elf_round.woff"];
//加载后立即应用当前页面
loader.preload=0;
//缓存生命周期为7*24小时
loader.lifeCycle=7*24;
await loader.run();
//在页面中直接插入样式标签,也可以在CSS中定义,字体名称就是字体包文件名
let fontStyle=document.createElement("style");
fontStyle.setAttribute("type","text/css");
fontStyle.innerHTML=`*{font-family:elf_round, "微软雅黑", sans-serif;}`;
document.head.appendChild(fontStyle);
同站所有页面(重复)使用该方法加载该字体时仅从缓存中读取,而不会有额外的网络请求。
加载并缓存图片
headLoader可以加载过的图片缓存下来,使整个网站所有页面再次使用时可以快速载入。
1、html中定义
<img data-file="/images/1.png" title="图1" class="middle" style="width:90%;max-width:751px;">
<img data-file="/images/2.png" title="图2" class="middle" style="width:500px;">
2、javascript代码
let loadFiles=async function(_e){
//实例化一个headLoader
let thisLoader=new headLoader();
//缓存生命周期为7*24小时
thisLoader.lifeCycle=7*24;
let thisSrc=_e.getAttribute("data-file");
//thisLoader.loadFile(_src)方法可用于加载图片数据,当数据库中数据有效时,不会重复加载。
await thisLoader.loadFile(thisSrc);
//thisLoader.db.getBase64(_src)方法可返回图片的base64编码
_e.src= await thisLoader.db.getBase64(thisSrc);
};
let images=document.querySelectorAll("img[data-file]");
images.forEach(_e=>loadFiles(_e));
同站所有页面(重复)使用该方法加载该图片时仅从缓存中读取,而不会有额外的网络请求。
加载并缓存其它
headLoader不仅可以加载缓存JS、CSS文档,还支持更多的类型的文件,例如HTML、SVG等。
支持的文本文件类型有:"js","css","svg","text","xml","json","html","htm"等。
支持二进制文件类型但不限于这些类型:"jpg","png","woff"等。
let thisLoader=new headLoader();
let files;
//读取一个文件,第一次从网络地址读取,第二次从indexedDB中读取,如果数据过期,会自动更新。
files=await thisLoader.loadFile("/button.svg");
console.log(files.value);
//同理缓存JS并返回所有文件代码
files=await thisLoader.loadJs(["/default.min.js","/button.js"]);
//同理缓存CSS并返回所有文件代码
files=await thisLoader.loadCss(["/index.css","/button.css"]);
//缓存并读取其它类型文件
files=await thisLoader.loadFile(["/index.html","/button.svg","/a.jpg"]);
//从files结果中检索文件代码
console.log(files.find(_v=>_v.key==="/index.html").value);
//或者thisLoader.db.getValue(_src)方法可返回文件代码
console.log(thisLoader.db.getValue("/button.svg"));
//thisLoader.db.getBase64(_src)方法可返回图片的base64编码
console.log(thisLoader.db.getBase64("/a.jpg"));
同站所有页面(重复)使用该方法加载该资源时仅从缓存中读取,而不会有额外的网络请求。
跨页缓存存取
headLoader在实例化后生成一个缓存方法的API,可实现多页面缓存共享,同一站点下不同页面可以随意存取。
例如两个不同页面,a.html和b.html,那么通过如下方法可以使两个页面像同一个页面一样的使用缓存:
//a.html和b.html的JS程序需要先各自实例化一个headLoader
let loader=new headLoader();
//定义数据存储的生命周期
loader.lifeCycle=[24,6];//数据有效期为24小时,每次延迟6小时。默认为【24,0],简写为24。
//初始化
await loader.run();
//在任意一个页面缓存写入使用命令:loader.db.setValue(key,value)
await loader.db.setValue("names",["张三","李四","王五"]);
//在任意任一个页面缓存读取:loader.db.getValue(key)
let names=await loader.db.getValue("names");
console.log(names);//["张三","李四","王五"] 或 false
由于缓存仓库使用indexedDB,因此容量不受限制,您可用它应用大数据的缓存共享及管理。
进阶
让headLoader.js只加载一个调度文件,仅通过修改调度文件实现整个站点所有页面的资源调配。
在所有的HTML页面插入相同的javascript标签,使其加载dispatch.js
<script type="text/javascript" data-dir="/" data-js="dispatch" src="/headLoader.min.js"></script>
dispatch.js程序,您可以根据需要修改使用:
(function(_g){
/**
*此部分为配置项代码,可根据项目需要修改
*/
//基础CSS,所有页面都用
_g.baseCss=['elficons','dracula','animate'];
//基础JS,所有页面都用
_g.baseJs=['libs/jquery','libs/jquery.cookie','libs/highlight'];
//基础字体文件,所有页面都用
_g.dataFont=["/font/elfRound/elf_round.woff"]
//明确URL路径所用的资源
_g.pathSource={
/**
*页面的URL路径包含以下字符串,加载指定的库,
*_css代表与当前页面同名的css文件,例如/a.html时加载/media/css/a.css文件,_js同理
*/
//此项匹配"/map/index.html"
"/map/":{
dataCss:[..._g.baseCss,'_css'],
dataJs:[..._g.baseJs,'_js'],
dataFont:[..._g.dataFont]
},
//此项匹配"/icon/index.html"
"/icon/":{
dataCss:[..._g.baseCss,'_css'],
dataJs:[..._g.baseJs,'libs/clipboard','_js','utils/pageInit'],
dataFont:[..._g.dataFont]
},
//可以使用通配符"*"代表所有字符,此项匹配类似"/news/a.html"或者"/news/all/a.html"等
"/news/*":{
dataCss:[..._g.baseCss,'_css'],
dataJs:[..._g.baseJs,'libs/fuse','_js'],
dataFont:[..._g.dataFont]
},
//前面未定义的页面,加载如下资源,这行一定要放到最后
"*":{
dataCss:[..._g.baseCss,'_css'],
dataJs:[..._g.baseJs,'_js','utils/pageInit'],
dataFont:[..._g.dataFont]
}
};
/**
*以下部分为固定的业务执行代码,不必修改
*/
let getOption=function(_path){
let path=_path.replace(/(\/.*)((\.html)|(index\.html))(.*)$/,"$1$5");
let option={};
let paths=Object.keys(_global.pathSource);
let thisRegexp;
//去除路径的第一个“/”,如果路径最后为“/”则替换为“/index”
let selfJsCss=_index=>_index.replace(/^\//,"").replace(/\/$/,'/index');
for(let i=0; i<paths.length; i++){
thisRegexp=new RegExp(`^${paths[i].replace('*','.*')}$`);
if(thisRegexp.test(path)){
option.dataJs=_global.pathSource[paths[i]].dataJs.map(_v=>_v==="_js" ? `${selfJsCss(path)}` : _v);
option.dataCss=_global.pathSource[paths[i]].dataCss.map(_v=>_v==="_css" ? `${selfJsCss(path)}` : _v);
option.dataFont=_global.pathSource[paths[i]].dataFont
break;
}
}
option.dataDir="/media/";//页面所需数据(例如js,css)的基础路径;
option.callback=false;
return option;
};
let thisOption=getOption(window===window.top ? window.location.pathname : window.frameElement.getAttribute("src") || window.frameElement["pathname"] || "");
let loader=new headLoader(thisOption);
await loader.run();}
)((window.location.origin==="null" || window.location.origin===window.top.location.origin) ? window.top : window);
注意事项
- 由于本插件需要浏览器的IndexedDB功能,所以必须使用现代浏览器,例如:Chrome、Edge等;
- 网站新版本上线一定严格按照数据的生命周期来完成,具体查看生命周期的计算方法;
- 使用javascript语句数定义生命周期时,如果lifeCycle为数组,例如[168,48],则生命周期为168小时,延迟时间为48小时;
- javascript标签的"data-css"属性定义css模块,多个css以逗号分割,headLoader会依次加载,不需要指定文件的扩展名称,"data-js"同理;
- 标签属性定义法与命令行语句法中的属性名称分别如下一一对应: data-dir 相当于 dataDir data-css 相当于 dataCss data-js 相当于 dataJs;
- css模块在"dataDir"路径下的"css"目录中,js模块在"dataDir"路径下的"js"目录中;
- 命令行语句法可以定义回调函数,标签属性定义法不支持定义回调函数;
- 当data-css包含"_css"模块时,代表headLoader自动加载页面css,例如当页面为127.0.0.1/index/index.html,则对应的css为127.0.0.1/css/index/index.css;
- 当data-js包含"_js"模块时,代表headLoader自动加载页面js,例如当页面为127.0.0.1/index/index.html,则对应的js为127.0.0.1/js/index/default.min.js;
- 当需要给加载的css或js标签添加属性时,可以用"_js|属性名=属性值|属性名=属性值|..."的形式实现,例如loader.dataJs=['_js|id=abc'];
- 如果是公有IP或者非localhost,所加载的文档会直接从浏览器数据库中并发读取,版本默认每24个小时更新一次;
- 如果是私有IP或者localhost开发环境,每次刷新页面都会以队列形式重新加载资源,这样做是为了方便开发调试,由此加载速度较慢,请知晓;
- 当在本地开发环境下运行时,在某些情况下会有提示,如果不希望出现提示请使用压缩文件headLoader.min.js;
- 当模块为完整的http地址时,例如loader.dataJs=['http://dwz.cn/headLoader'],将直接创建标签并加载,不进行任何处理;
- 如果请求的URL发生404等异常错误时仍然返回数据包,目的是不会中断后续操作,数据包中的exception不为false时表示异常。例如:
{ "key": "/a/b.html",//这是一个不存在的网址 "value": "", "exception": { "code": 404, "detail": "Error-404 ! 该资源找不到了。" }, "version": 0, "etag": "" }
- 在引入headLoader.js时如果添加“data-active=true”属性参数时,或者命令行语法在给实例添加dataActive并置为true,此时开发环境正常加载,但是线上环境会将“/js/”目录切换为"/js.min/"目录,并将文件扩展名".js"改为".min.js"(CSS同理)。例如“/media/js/a.js”在线上环境会自动切换为“/media/js.min/a.min.js”。
代码下载
https://github.com/baiyukey/headLoader
*代码仅供学习参考,并不定期更新版本,对于不同版本造成的任何问题,作者不承担任何法律责任,特此声明!
责任声明
- 个人用户免费使用,捐赠请扫页面左栏下方的二维码,您的支持是对知识产权最大的尊重;
- 营利机构,政府部门或者商业用户,您需要联系作者(baiyukey@qq.com)申请一个授权(*推广期酌情免费授权);
- 作者保留一切法律权力,并对于侵权或者非法使用行为,有权追究一切法律责任;
- UI精灵目前的域名仅有uielf.cn,而且暂未和任何第三方合作,对于冒名合作开发的机构作者保留追究其法律责任的权利。