前面已经已fis server open
为例,讲解了FIS的整体架构设计,以及命令解析&执行的过程。下面就进入FIS最核心的部分,看看执行fis release
这个命令时,FIS内部的代码逻辑。
这一看不打紧,基本把fis-kernel
的核心模块翻了个遍,虽然大部分细节已经在脑海里里,但是要完整清晰的写出来不容易。于是决定放弃大而全的篇幅,先来个概要的分析,后续文章再针对涉及的各个环节的细节进行展开。
fis-command-release
老规矩,献上精简版的 release.js
,从函数名就大致知道干嘛的。release(options)
是我们重点关注的对象。
'use strict'; exports.register = function(commander){ // fis relase --watch 时,就会执行这个方法 function watch(opt){ // ... } // 打点计时用,控制台里看到的一堆小点点就是这个方法输出的 function time(fn){ // ... } // fis release --live 时,会进入这个方法,对浏览器进行实时刷新 function reload(){ //... } // 高能预警!非常重要的方法,fis release 就靠这个方法走江湖了 function release(opt){ // ... } // 可以看到有很多配置参数,每个参数的作用可参考对应的描述,或者看官方文档 commander .option('-d, --dest <names>', 'release output destination', String, 'preview') .option('-m, --md5 [level]', 'md5 release option', Number) .option('-D, --domains', 'add domain name', Boolean, false) .option('-l, --lint', 'with lint', Boolean, false) .option('-t, --test', 'with unit testing', Boolean, false) .option('-o, --optimize', 'with optimizing', Boolean, false) .option('-p, --pack', 'with package', Boolean, true) .option('-w, --watch', 'monitor the changes of project') .option('-L, --live', 'automatically reload your browser') .option('-c, --clean', 'clean compile cache', Boolean, false) .option('-r, --root <path>', 'set project root') .option('-f, --file <filename>', 'set fis-conf file') .option('-u, --unique', 'use unique compile caching', Boolean, false) .option('--verbose', 'enable verbose output', Boolean, false) .action(function(){ // 省略一大堆代码 // fis release 的两个核心分支,根据是否有加入 --watch 进行区分 if(options.watch){ watch(options); // 有 --watch 参数 } else { release(options); // 这里这里!重点关注!没有 --watch 参数 } }); };
release(options);
做了些什么用伪代码将逻辑抽象下,主要分为四个步骤。虽然最后一步才是本片文章想要重点讲述的,不过前三步是第四步的基础,所以这里还是花点篇幅介绍下。
findFisConf(); // 找到当前项目的fis-conf.js setProjectRoot(); // 设置项目根路径,需要编译的源文件就在这个根路径下 mergeFisConf(); // 导入项目自定义配置 readSourcesAndReleaseToDest(options); // 将项目编译到默认的目录下
下面简单对上面几个步骤进行一一讲解。
由于这两步之间存在比较紧密的联系,所以这里就放一起讲。在没有任何运行参数的情况下,比较简单
fis-conf.js
,直到找到位置fis-conf.js
,则以它为项目配置文件。同时,将项目的根路径设置为fis-conf.js
所在的目录。fis-conf.js
,则采用默认项目配置。同时,将项目的根路径,设置为当前命令运行时所在的工作目录。从fis release
的支持的配置参数可以知道,可以分别通过:
--file
:指定fis-conf.js
的路径(比如多个项目公用编译配置)--root
:指定项目根路径(在A工作目录,编译B工作目录)由本小节前面的介绍得知,--file
、--root
两个配置参数之间是存在联系的,有可能同时存在。下面用伪代码来说明下
if(options.root){ if(options.file){ // 项目根路径,为 options.root 指定的路径 // fis-conf.js路径,为 options.file 指定的路径 }else{ // 项目根路径,为 options.root 指定的路径 // fis-conf.js路径,为 options.root/fis-conf.js } }else{ if(options.file){ // fis-conf.js路径,为 options.file 指定的路径 // 项目根路径,为 fis-conf.js 所在的目录 }else{ // fis-conf.js路径,为 逐层向上遍历后,找到的 fis-conf.js 路径 // 项目根路径,为 fis-conf.js 所在的目录 } }
合并项目配置文件。从源码可以清楚的看到,包含两个步骤:
fis-conf.js
创建缓存。除了配置文件,FIS还会为项目的所有源文件建立缓存,实现增量编译,加快编译速度。缓存的细节后面再讲,这里知道有这么回事就行。// 如果找到了 fis-conf.js if(conf){ var cache = fis.cache(conf, 'conf'); if(!cache.revert()){ options.clean = true; cache.save(); } require(conf); // 加载 fis-conf.js,其实就是合并配置 } else { // 还是没有找到 fis-conf.js fis.log.warning('missing config file [' + filename + ']'); }
通过这个死长的伪函数名,就知道这个步骤的作用了,非常关键。根据当前项目配置,读取项目的源文件,编译后输出到目标目录。
编译过程的细节,下一节会讲到。
项目编译发布的细节,主要是在release
这个方法里完成。细节非常的多,主要在fis.release()
这个调用里完成,基本上用到了fis-kernel
里所有的模块,如release
、compile
、cache
等。
伪代码流程如下:fis-command-release/release.js
var collection = {}; // 跟total一样,key=>value 为 “编译的源文件路径”=》"对应的file对象" var total = {}; var deploy = require('./lib/deploy.js'); // 文件部署模块,完成从 src -> dest 的最后一棒 function release(opt){ opt.beforeEach = function(file){ // 用compile模块编译源文件前调用,往 total 上挂 key=>value total[file.subpath] = file; }; opt.afterEach = function(file){ // 用compile模块编译源文件后调用,往 collection 上挂 key=>value collection[file.subpath] = file; }; opt.beforeCompile = function(file){ // 在compile内部,对源文件进行编译前调用(好绕。。。) collection[file.subpath] = file; }; try { //release // 在fis-kernel里,fis.release = require('./lib/release.js'); // 在fis.release里完成除了最终部署之外的文件编译操作,比如文件标准化等 fis.release(opt, function(ret){ deploy(opt, collection, total); // 项目部署(本例子里特指将编译后的文件写到某个特定的路径下) }); } catch(e) { // 异常处理,暂时忽略 } }
fis.release()
前面说了,细节非常多,后续文章继续展开。。。
文章: casperchen