一种JavaScript文件构建方法及装置
技术领域
本申请涉及计算机应用技术领域,尤其涉及一种JavaScript文件构建方法及装置。
背景技术
JavaScript一种直译式脚本语言,目前广泛用于Web应用开发。早期的JavaScript并不支持模块化编程,随着Web应用功能的日益复杂,模块化编程成为迫切的需求,随之也产生许多能在现有的JavaScript环境中实现模块化效果的方案。
开发人员在后端编写的脚本文件,如果需要作为前端文件被页面引用,那么在页面上线之前,需要基于该文件的后端版本构建(build)相应的前端版本,使得该文件能够在前端正确运行。在现有的模块化开发方案中,如果一个后端文件中依赖了其他的模块,那么在构建过程中,需要把被依赖模块的内容也包含进来。
针对上述需求,现有的构建方案是引入一个加载器,通过加载器把依赖模块和被依赖模块的内容封装起来。然而采用这种构建方案所得到的前端文件,在运行时需要先执行模块定义操作、再执行模块加载操作,导致前端文件相对于后端文件的运行效率降低,而且涉及依赖的模块数量越多,影响就越明显。另外,由于引入加载器需要加入很多的辅助代码,导致前端文件的体积增加、用户需要消耗更多的代价(时间、网络带宽等)来下载文件,这对用户体验也造成了影响。
发明内容
针对上述技术问题,本申请提供一种JavaScript文件构建方法及装置,技术方案如下:
根据本申请的第一方面,提供一种JavaScript文件构建方法,该方法包括:
获得待处理文件;
根据所述待处理文件代码中的依赖关键字,确定所述待处理文件的依赖链;
针对所述依赖链中包含的各个文件,分别进行代码转换处理;
根据所述依赖链的顺序对转换得到的各段代码进行合并,使被依赖文件的代码转换结果位于依赖文件的代码转换结果之前,得到构建后的文件;
其中,依赖链中包含的各个文件分别具有预先约定的对应变量名,对任一文件进行代码转换处理的操作包括:
将文件代码中用于表示依赖的代码文本替换为被依赖文件对应的变量名;
将文件代码中的模块输出关键字替换为返回关键字;
将文件内容封装为自执行函数写入转换结果,函数的返回值赋给该文件对应的变量。
根据本申请的第二方面,提供一种JavaScript文件构建装置,该装置包括:
文件获得模块,用于获得待处理文件;
依赖链确定模块,用于根据所述待处理文件代码中的依赖关键字,确定所述待处理文件的依赖链;
转换模块,用于针对所述依赖链中包含的各个文件,分别进行代码转换处理;
合并模块,用于根据所述依赖链的顺序对转换得到的各段代码进行合并,使被依赖文件的代码转换结果位于依赖文件的代码转换结果之前,得到构建后的文件;
其中,依赖链中包含的各个文件分别具有预先约定的对应变量名,所述转换模块具体用于:
将文件代码中用于表示依赖的代码文本替换为被依赖文件对应的变量名;
将文件代码中的模块输出关键字替换为返回关键字;
将文件内容封装为自执行函数写入转换结果,函数的返回值赋给该文件对应的变量。
本申请所提供的技术方案,将构建过程中涉及的各个独立的文件的内容代码重新封装为自执行函数的形式,然后根据文件依赖链的顺序,对各个自执行函数进行排序合并,函数之间采用约定的变量名传递数值以解决依赖关系。从而避免使用加载器对构建后文件造成的性能损坏,同时也能够有效减小构建后的文件体积。
应当理解的是,以上的一般描述和后文的细节描述仅是示例性和解释性的,并不能限制本申请。
附图说明
为了更清楚地说明本申请实施例或现有技术中的技术方案,下面将对实施例或现有技术描述中所需要使用的附图作简单地介绍,显而易见地,下面描述中的附图仅仅是本申请中记载的一些实施例,对于本领域普通技术人员来讲,还可以根据这些附图获得其他的附图。
图1是本申请的JavaScript文件构建方法的流程示意图;
图2是本申请的JavaScript文件构建装置的结构示意图。
具体实施方式
为了使本领域技术人员更好地理解本申请中的技术方案,下面将结合本申请实施例中的附图,对本申请实施例中的技术方案进行详细地描述,显然,所描述的实施例仅仅是本申请一部分实施例,而不是全部的实施例。基于本申请中的实施例,本领域普通技术人员所获得的所有其他实施例,都应当属于本申请保护的范围。
首先对现有技术中引入加载器构建前端文件的方法进行简单说明,假设开发人员在后端编写了两个脚本文件a.js和b.js,其文件代码内容分别如下:
a.js内容:
var b=require('./b.js');
console.log('a'+b);
b.js内容:
module.exports='b';
根据a.js中的依赖函数require()可以看出,脚本文件a.js依赖了脚本本件b.js,假如a.js需要作为前端页面的入口文件被页面引用,也就是说在前端仅存在一个a.js文件,无法直接调用被依赖的b.js文件,因此在页面上线之前,需要构建一个新的a.js,把原a.js中所依赖的b.js的内容也包含进来,以确保构建后的a.js能够在前端正常运行。
根据现有的文件构建方法,在文件构建过程中,为了处理依赖关系,需要引入一个加载器,通过加载器把依赖文件和被依赖文件的内容封装起来。构建后得到的a.js代码内容如下:
可见,利用上述方案构建后得到的前端文件,在运行时需要先执行模块定义操作、再执行模块加载操作,导致前端文件相对于后端文件的运行效率降低,而且涉及依赖的模块数量越多,影响就会越明显。另外可以看出,相对于原有后端文件a.js和b.js的代码内容总和,构建得到的新a.js文件内容明显增加,在一定程度上也会影响用户的使用体验。
针对上述问题,本申请提供一种JavaScript文件构建方法,参见图1所示,该方法可以包括以下步骤:
S101,获得待处理文件;
对于后端开发开发过程中所使用的页面入口文件,为了保证其在前端能够正常运行,需要在上线之前进行构建,在本申请中,将这个构建的对象称为待处理文件,例如前例中的a.js。
S102,根据待处理文件代码中的依赖关键字,确定待处理文件的依赖链;
根据目前通用的JavaScript规范,使用require函数实现模块之间的依赖调用,例如require('./b.js'),表示依赖./路径下b.js文件,也就是说,可以通过查找文件代码中的“require('*')”文本(*为通配符),获知当前待处理文件的依赖文件。考虑到依赖关系可能存在多级,因此还需要进一步对被待处理文件的依赖文件进行同样的查找处理,以确定是否存在更高一级的依赖关系,依次递归,直到文件代码中不存在“require('*')”文本,结束递归,确定整条依赖链。例如:
通过查找发现在a.js中存在require('./b.js'),得知a.js依赖b.js;
进一步对b.js进行查找,发现存在require('./c.js'),得知b.js依赖c.js;
进一步对c.js进行查找,没有发现“require('*')”文本,则最终确定a.js的完整依赖链为:a.js—b.js—c.js。
可以理解的是,上述举例是针对目前的JavaScript规范,使用require作为依赖关键字确定依赖链,如果编程语言中使用其他函数或语法实现依赖调用,则需要根据实际情况调整用于在代码中查找依赖关系的关键字匹配规则。
S103,针对依赖链中包含的各个文件,分别进行代码转换处理;
本申请方案是通过将依赖链中的每个文件重新封装成自执行函数、然后按照依赖链顺序依次执行各个函数的方式来解决依赖关系,为了在各个函数之间传递数值,需要为每个自执行函数分别创建一个变量以保存运行结果。而为了方便在构建时做统一处理,需要预先约定一套命名规则,使得每个文件都具有一个确定的对应变量名,例如,规定./a.js对应变量名_a、./b.js对应变量名_b,等等,这样在文件构建过程中,遇到代码中需要处理的文件名时,就可以按照上述规则直接将文件名替换为对应的变量名。
可以理解的是,上述文件名-变量名的命名规则仅用于示意性说明,并不构成对本申请方案的限定。
其中,对任意一个文件进行代码转换具体包括以下操作:
1)将文件代码中用于表示依赖的代码文本替换为被依赖文件对应的变量名;
根据目前通用的JavaScript规范,即是将“require(‘被依赖文件标识’)”文本替换为被依赖的文件对应的变量名,其中“被依赖文件标识”可以仅包含文件名,也可以是“路径+文件名”的形式。
例如,根据预设的命名规则,./b.js对应变量名_b,则代码require(‘./b.js’)将被替换为_b。
如果编程语言中使用其他函数或语法实现依赖调用,则需要在替换时根据实际情况调整用于在代码中表示依赖的代码文本。
2)将文件代码中的模块输出关键字替换为返回关键字;
根据目前通用的JavaScript规范,即是将代码中的“module.exports”替换为“return”。如果编程语言采用了使用其他函数或语法实现“模块输出”功能或“返回”功能,则在替换时相应调整关键字即可。
3)将文件内容封装为自执行函数,函数的返回值赋给该文件对应的变量。
根据目前通用的JavaScript规范,即是在转换结果中写入以下代码:
var文件对应变量名=(function(){文件内容})()
可以理解的是,上述1)、2)、3)三个步骤对应的三个是独立的转换操作,因此在实际执行顺序上并没有特别限定,只要保证在一个文件的最终代码转换结果中,其中的“文件内容”部分是经过1)、2)对应的两次替换操作得到即可。
S104,根据依赖链的顺序对转换得到的各段代码进行合并,使被依赖文件的代码转换结果位于依赖文件的代码转换结果之前,得到构建后的文件;
在一组“依赖-被依赖”关系中,被依赖的文件对应的函数需要先得到执行结果,才能被依赖它的文件所对应的函数所使用,因此在本步骤中,根据S102得到的依赖链进行递归分析,将被依赖文件对应的自执行函数代码排在前面,然后根据排序结果对各文件对应的自执行函数代码进行合并,得到构建后的文件。也就是说,在最终得到的构建后文件代码中,对于任意一组“依赖文件-被依赖文件”关系,都能满足以下关系:“被依赖文件的代码转换结果”位于“依赖文件的代码转换结果”之前。
下面结合一个具体实例对本申请的JavaScript文件构建方法进行说明:
假设a.js和b.js为开发人员在后端编写的两个脚本文件,并且a.js依赖b.js,两个文件代码内容分别如下:
a.js内容:
var b=require('./b.js');
console.log('a'+b);
b.js内容:
module.exports='b';
某个页面采用a.js作为入口文件,则为了保证其在前端能够正常运行,需要在上线之前进行构建。
根据S101,获得待处理文件,即上述后端版本的a.js;
根据S102,通过查找发现在a.js中存在require('./b.js'),得知a.js依赖b.js;进一步对b.js进行查找,没有发现require函数,则最终确定a.js的完整依赖链为:a.js—b.js。
根据S103,分别针对依赖链中涉及的文件a.js和b.js进行代码转换处理;根据预设的命名规则,两个文件对应的变量名分别为_a和_b:
针对后端文件a.js,转换结果为:
针对后端文件b.js,转换结果为:
var_b=(function(){
return'b';
})();
根据S104,按照a.js—b.js的依赖顺序,将被依赖文件b.js的代码转换结果排在前面,最终得到的构建后的文件代码如下:
可见,构建后的得到文件直接采用代码顺序执行的方式,与利用加载器构建得到的文件相比,避免了执行时的性能损失,并且代码数量明显减少。与上述用于示意性说明的具体实例相比,在实际应用中,一个入口文件的依赖链往往更为复杂,应用本申请方案所带来的优势也将更为明显。
相应于上述方法实施例,本申请还提供一种JavaScript文件构建装置,参见图2所示,该装置可以包括:
文件获得模块110,用于获得待处理文件;
依赖链确定模块120,用于根据待处理文件代码中的依赖关键字,确定待处理文件的依赖链;
转换模块130,用于针对依赖链中包含的各个文件,分别进行代码转换处理;
其中,依赖链中包含的各个文件分别具有预先约定的对应变量名,转换模块130可以具体用于:
1)将文件代码中用于表示依赖的代码文本替换为被依赖文件对应的变量名;
2)将文件代码中的模块输出关键字替换为返回关键字;
3)将文件内容封装为自执行函数写入转换结果,函数的返回值赋给该文件对应的变量。
上述1)、2)、3)对应的三个是独立的转换操作,因此在实际执行顺序上并没有特别限定,只要保证在一个文件的最终代码转换结果中,其中的“文件内容”部分是经过1)、2)对应的两次替换操作得到即可。
合并模块140,用于根据依赖链的顺序对转换得到的各段代码进行合并,使被依赖文件的代码转换结果位于依赖文件的代码转换结果之前,得到构建后的文件;
上述装置中各个模块的功能和作用的实现过程具体详见上述方法中对应步骤的实现过程,在此不再赘述。
通过以上的实施方式的描述可知,本领域的技术人员可以清楚地了解到本申请可借助软件加必需的通用硬件平台的方式来实现。基于这样的理解,本申请的技术方案本质上或者说对现有技术做出贡献的部分可以以软件产品的形式体现出来,该计算机软件产品可以存储在存储介质中,如ROM/RAM、磁碟、光盘等,包括若干指令用以使得一台计算机设备(可以是个人计算机,服务器,或者网络设备等)执行本申请各个实施例或者实施例的某些部分所述的方法。
本说明书中的各个实施例均采用递进的方式描述,各个实施例之间相同相似的部分互相参见即可,每个实施例重点说明的都是与其他实施例的不同之处。尤其,对于装置实施例而言,由于其基本相似于方法实施例,所以描述得比较简单,相关之处参见方法实施例的部分说明即可。以上所描述的装置实施例仅仅是示意性的,其中所述作为分离部件说明的模块可以是或者也可以不是物理上分开的,在实施本申请方案时可以把各模块的功能在同一个或多个软件和/或硬件中实现。也可以根据实际的需要选择其中的部分或者全部模块来实现本实施例方案的目的。本领域普通技术人员在不付出创造性劳动的情况下,即可以理解并实施。
以上所述仅是本申请的具体实施方式,应当指出,对于本技术领域的普通技术人员来说,在不脱离本申请原理的前提下,还可以做出若干改进和润饰,这些改进和润饰也应视为本申请的保护范围。