用JavaScript实现js文件的版本管理和加载

《大公司怎样开发和部署前端代码?》这篇文章的启发,结合自己的项目实践,创建了一套JavaScript文件的版本管理和加载的机制,虽然比较粗糙,但是解决了不少实际的问题。

使用到的主要工具:

  1. Node.js
  2. NPM
  3. grunt和相关插件(grunt-hashmap,grunt-contrib-uglify,自定义的插件)
  4. LAB.js

功能能点:

  1. 利用grunt插件hashmap根据JavaScript的文件的内容生成hash码,可做为JavaScript文件名的一部分,有效防止在更改JavaScript文件内容后浏览器缓存的问题。
  2. JSP页面中不再直接引用JavaScript文件,所有的JavaScript文件通过JSON格式的映射文件,由LAB.js根据映射关系负责加载,这样每次修改JavaScript文件后,再次发布时无需修改JSP页面。
  3. 开发环境和生成环境的切换。
  4. 对JavaScript文件压缩

前端的项目结构:

后端的主要文件:static_file.jsp

<script type="text/javascript" src="${staticroot}/static/js/lib/LAB.min.js"></script>
<script type="text/javascript">
	(function() {
		$LAB
		.script("${staticroot}/static/js/lib/jquery.min.js")
		.wait()
		.script("${staticroot}/static/js/config/JspJsMapping.js")
		.script("${staticroot}/static/js/config/VersionMapping.js").wait()
		.script("${staticroot}/static/js/config/AppConfig.js")
		.wait(function() {
			 	AppConfig.getScripts(‘<%=request.getServletPath()%>‘, ‘${staticroot}‘);
		})
	})();
</script>

项目中的JSP文件中如果存在JavaScript文件,则只需引用static_file.jsp文件即可,其余的工作都交给static_file.jsp文件中的JavaScript代码即可。

通过观察static_file.jsp文件可以发现,首先要引用LAB.min.js类库,这是加载JavaScript文件的核心。另外由于项目中每个JSP文件都有对Jquery的引用,所以在此处Jquery文件也有LABjs默认加载。

另外,JspJsMapping.js、VersionMapping.js和AppConfig.js便是整个前端的配置文件,在某个JSP中加载哪些JavaScript文件,加载哪个版本的JavaScript文件,使用生成环境还是测试环境都由这三个配置文件进行管理。

下面分别介绍这三个配置文件:

JspJsMapping.js文件由JspJsMapping.tpl模板文件使用grunt自定义插件生成的,内容如下(部分):

/**
 * Created by wanglu on 2014/12/16.
 * 生成jsp中使用到的js文件的映射信息(模板)
 */
;(function(window) {
    window.JspJsMapping = {
		"public1": {
			"scripts":[
				"lib/spin.min.js",
				"lib/iosOverlay.js",
				"lib/fastclick.js",
				"Utils.js"
			]
		},
		"/WEB-INF/views/acc/about.jsp": {
			"scripts":[
				"include:public1"
			]
		},
		"/WEB-INF/views/acc/applylist.jsp": {
			"scripts":[
				"lib/jquery.mobile.custom.min.js",
				"lib/AjaxUtil.js",
				"lib/iscroll.js",
				"lib/iscrollAssist.js",
				"PageController.js",
				"lib/SessionStorageUtil.js",
				"lib/LocalStorageUtil.js",
				"include:public1",
				{"name": "applylist.js", "wait": true}
			]
		},
		"/WEB-INF/views/acc/favbranches.jsp": {
			"scripts":[
				"lib/jquery.mobile.custom.min.js",
				"lib/AjaxUtil.js",
				"lib/iscroll.js",
				"lib/iscrollAssist.js",
				"PageController.js",
				"lib/SessionStorageUtil.js",
				"lib/LocalStorageUtil.js",
				"include:public1",
				{"name": "favbranches.js", "wait": true}
			]
		},
		"/WEB-INF/views/acc/favjobs.jsp": {
			"scripts":[
				"lib/jquery.mobile.custom.min.js",
				"lib/AjaxUtil.js",
				"lib/iscroll.js",
				"lib/iscrollAssist.js",
				"PageController.js",
				"lib/SessionStorageUtil.js",
				"lib/LocalStorageUtil.js",
				"include:public1",
				{"name": "favjobs.js", "wait": true}
			]
		},
		"/WEB-INF/views/acc/index.jsp": {
			"scripts":[
			]
		}
	}

})(window);

这个文件配置的JSP页面与JavaScript的引用关系。

VersionMapping.js文件由Versionmapping.tpl模板文件使用grunt自定义插件生成,内容如下(部分):

/**
 * Created by wanglu on 2014/12/16.
 * 生成版本映射信息(模板)
 */
;(function(window) {
    window.VersionMapping = {
        version: ‘20150403174521‘,
        mappings: {
			‘CityHelper.js‘: ‘5e4cda‘,
			‘DictionaryCache.js‘: ‘96ecdf‘,
			‘FoodHelper.js‘: ‘ec65b2‘,
			‘FunctionHelper.js‘: ‘350065‘,
			‘Gruntfile.js‘: ‘f916ad‘,
			‘PageController.js‘: ‘b5ed9d‘,
			‘SocialHelper.js‘: ‘821373‘,
			‘Utils.js‘: ‘cb4ade‘,
			‘WorkFuncHelper.js‘: ‘3013b8‘,
			‘app.js‘: ‘aecc0b‘,
			‘apply.js‘: ‘baa38d‘,
			‘applylist.js‘: ‘78be19‘,
			‘branch.js‘: ‘388c5e‘,
			‘branchjobs.js‘: ‘1fe1ec‘,
			‘branchlist.js‘: ‘86d21a‘,
			‘favbranches.js‘: ‘d2331a‘,
			‘favjobs.js‘: ‘4970e4‘,
			‘iscroll_kt.js‘: ‘0dc411‘
		}
    }
})(window);

这个文件配置的JavaScript文件和其当前的hashcode的映射关系,比如/WEB-INF/views/acc/favjobs.jsp文件引用了PageController.js文件,那么最终在JSP文件中将加载PageController_b5ed9d.js文件,对于一些公用的JavaScript文件,可将其配置如下的格式:

"public1": {
			"scripts":[
				"lib/spin.min.js",
				"lib/iosOverlay.js",
				"lib/fastclick.js",
				"Utils.js"
			]
		}

然后在应用JavaScript文件的使用,改变为如下样式:

"/WEB-INF/views/acc/applylist.jsp": {
			"scripts":[
				"lib/jquery.mobile.custom.min.js",
				"lib/AjaxUtil.js",
				"lib/iscroll.js",
				"lib/iscrollAssist.js",
				"PageController.js",
				"lib/SessionStorageUtil.js",
				"lib/LocalStorageUtil.js",
				"include:public1",
				{"name": "applylist.js", "wait": true}
			]
		}

AppConfig.js文件是加载功能的实现,在此文件中使用LABjs,通过JspJsMapping.js和VersionMapping.js两个配置文件为JSP页面加载所需要的JavaScript的文件。内容如下(全部):

;(function($, window){
    window.AppConfig = {
//	    mode: ‘debug‘,
        min: true,
        log: true,
        baseUrl: ‘/static/js/build/‘,
        debugUrl: ‘/static/js/‘,
        getScripts: function(key, baseUrl) {
            var scripts = JspJsMapping[key][‘scripts‘] || [];
            var labJsScript = this.getLABScript(baseUrl, scripts);
            if (labJsScript !== ‘$LAB‘) {
                try {
                    window.eval(labJsScript + ‘;‘);
                } catch(e) {
                    console && console.error(e);
                }
            }
        },

        getLABScript: function(baseUrl, scripts, labJsScript) {
            labJsScript = labJsScript || ‘$LAB‘;
            for (var sc; scripts && (scripts instanceof Array) && (sc = scripts.shift()); ) {
                if (typeof sc === ‘string‘) {
                    if(sc.indexOf(‘include:‘) === 0) {
                        var key = sc.substring(sc.indexOf(‘:‘) + 1);
                        labJsScript = this.getLABScript(baseUrl, JspJsMapping[key][‘scripts‘] || [], labJsScript);
                    }
                    else {
                        var url = AppConfig.getFileName(sc);
                        labJsScript += ‘.script("‘+ baseUrl + url + ‘")‘;

                        this.log && console && console.log(‘loadding : ‘ + baseUrl + url);
                    }
                }
                else if (typeof sc === ‘object‘) {
                    var url = AppConfig.getFileName(sc[‘name‘]);
                    labJsScript += ‘.script("‘+ baseUrl + url + ‘")‘;
                    if (sc[‘wait‘]) {
                        labJsScript += ‘.wait()‘;

                        url = ‘wait ‘ + baseUrl + url;
                    }

                    this.log && console && console.log(‘loadding : ‘ +  url);
                }
            }

            return labJsScript;
        },
        getFileName : function(fileName) {
            if (!fileName) {
                return ‘‘;
            }
            if (this.mode === ‘debug‘) {
                return  ( AppConfig.debugUrl || AppConfig.baseUrl) + fileName;
            }
            else {
                return  (AppConfig.baseUrl || ‘‘)
                    + (this.min ? ‘min/‘ : ‘‘)
                    + fileName.substring(0, fileName.lastIndexOf(‘.‘))
                    + ‘_‘ + VersionMapping.mappings[fileName] + ‘.js‘;
            }
        }
    };

    return window.AppConfig;
})($, window);

此文件中的代码最后生成是类似于$LAB.script(...).script(..)格式的代码字符串,然后用window.eval方式执行,实现JavaScript的代码加载。

在此文件中还可以配置是否开启debug(debug属性)模式,是否加载压缩过(min属性)的JavaScript文件等。由于在浏览器调试JavaScript的时候,压缩过的JavaScript的文件无法阅读,所以才使用min属性控制是否加载压缩过的JavaScript文件。

未压缩的文件放在build文件夹中,压缩过的文件放在build/min文件夹中。

整个机制就是这样,目前写的还不是太详细,后续将继续完善。另外,本方法不是对所有的项目都适用,比如由模块化开发的JavaScript项目,而且代码比较粗糙,只是写出来给有用的朋友一些帮助。

时间: 2024-10-07 20:03:04

用JavaScript实现js文件的版本管理和加载的相关文章

如何使用 require.js ,实现js文件的异步加载,避免网页失去响应,管理模块之间的依赖性,便于代码的编写和维护。

一.为什么要用require.js? 最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了.后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载.下面的网页代码,相信很多人都见过. 1 <script src="1.js"></script> 2 <script src="2.js"></script> 3 <script src="3.js">

Android 文件的存储和加载

Android 文件的存储和加载,主要用于请求网络中json文件的缓存,引入了一个简单的过期时间,供大家参考学习! 文件存储 1 private void saveLocal(String json, int index) { 2 3 BufferedWriter bw = null; 4 try { 5 File dir=FileUtils.getCacheDir(); 6 //在第一行写一个过期时间 7 File file = new File(dir, getKey()+"_"

js文件代码未加载或者没有js效果

问题:在页面中js文件中的代码未加载或者没有任何效果. 原因: 成功引用了js文件,但无效果或者提示未加载该文档中的代码. 可能页面引用js文件的路径存在问题 解决: 重新检查你引用的js文件的路径是否正确: 一般是js代码存在语法问题,仔细检查标点符号.单双引号(本人源于这个问题),以及页面中是否有未结束的<!--的符号. js文件代码未加载或者没有js效果

javascript 在js文件中获取路径

如果在*.js文件中获取当自己当前的路径是很重要的. 举个例子,如果一个css文件中引用图片,如background-img: url('./Images/bg.png').那么图片的路径,是相对于css文件而言的. 但是,如果我们再js文件中引用图片,如img.src = './images/bg.png';  但是,图片却不是根据js的相对路径的.而是根据(引用该js)的html的相对路径来决定的.这显然不合理. 所以如果我们能获取js文件的绝对路径的话,就会好很多.譬如 img.src =

js文件如何最后加载

总结一下,大概有三种方式 1.最简单粗暴的:将js文件放在最后写 2.window.onload 当一个文档完全下载到浏览器中时,才会触发window.onload事件.这意味着页面上的全部元素对js而言都是可以操作的,也就是说页面上的所有元素加载完毕才会执行.这种情况对编写功能性代码非常有利,因为无需考虑加载的次序. 3.$(document).ready{ }: 会在DOM完全就绪并可以使用时调用.虽然这也意味着所有元素对脚本而言都是可以访问的,但是,并不意味着所有关联的文件都已经下载完毕.

js文件先后顺序加载的问题

<script type="text/javascript"> function loadScript(url, callback) { var _script = document.createElement("script"); _script.type="text/javascript"; if (_script.readyState) {//IE _script.onreadystatechange = function ()

ELF文件的格式和加载过程

http://blog.csdn.net/lingfong_cool/article/details/7832896 (一) ELF 文件的格式       ELF 文件类型 (1) 可重定位文件( .o 目标文件) : 用于链接创建可执行文件或 so 文件 (2) 可执行文件                     : 用于执行 (3)so( 共享对象 ) 文件            : 用于链接 注 :   一个 Program Header 对应一个 Segment 一个 Section

MFC中数据文件的存储和加载

1. 传统的数据存储,将数据定义到一个结构体中,用CFile的方式循环读写数据结构体 struct SData { UINT nNumb; TCHAR sName[20]; COleDateTime date; }; void CfffDlg::OnBnClickedSave() { //另存为对话框 CFileDialog fd(FALSE, _T("*.fff"), _T("example"), OFN_OVERWRITEPROMPT, _T("信息文

JavaScript的8行代码搞定js文件引入问题

单页面的操作,免不了会有各种jsp的嵌套问题,一个操作页面里面可能涉及到几十甚至上百个jsp页面. 平常我们对用到的js文件的引入,都会放到index的header里面.如图: 但是,让我们思考三个问题: 1.当业务需要,用到iframe的时候,那么iframe页面将不能再直接调用index引入的js文件 2.假如我这个项目有100个jsp页面,有一个js文件只有其中一个jsp要使用,我将这个js放到index的header区,每次一加载项目都会加载它,是不是延长了项目加载时间. 3.针对问题二