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.您也可以试试这个工具:

  • 生命周期:

    -+

    小时 //对应参数 lifeCycle
  • 每次延迟:

    -+

    小时 //对应参数 cycleDelay
  • 计算未来:
    更新时间点
  • 开始计算恢复默认
强制清除缓存方法

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,而且暂未和任何第三方合作,对于冒名合作开发的机构作者保留追究其法律责任的权利。
关于我
我是一名网站前端工程师,江湖道义称一声宇哥,追求前端技术和艺术20余载,自由职业。
UI精灵网站及技术成果全部由我研发,如果想拥有和我一样的网站,或者需要我的技术支持,欢迎与我洽谈合作。点此发送邮件
如果我的成果能够帮您节省宝贵的时间。不妨点击我的头像请我喝杯咖啡。并诚邀来github关注我的开源项目。