Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIS源码-fis release概览 #31

Open
chyingp opened this issue May 11, 2015 · 0 comments
Open

FIS源码-fis release概览 #31

chyingp opened this issue May 11, 2015 · 0 comments

Comments

@chyingp
Copy link
Owner

chyingp commented May 11, 2015

开篇

前面已经已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);   // 将项目编译到默认的目录下

下面简单对上面几个步骤进行一一讲解。

findFisConf() + setProjectRoot()

由于这两步之间存在比较紧密的联系,所以这里就放一起讲。在没有任何运行参数的情况下,比较简单

  1. 从命令运行时所在的工作目录开始,向上逐级查找fis-conf.js,直到找到位置
  2. 如果找到fis-conf.js,则以它为项目配置文件。同时,将项目的根路径设置为fis-conf.js所在的目录。
  3. 如果没有找到fis-conf.js,则采用默认项目配置。同时,将项目的根路径,设置为当前命令运行时所在的工作目录。

fis release的支持的配置参数可以知道,可以分别通过:

  1. --file:指定fis-conf.js的路径(比如多个项目公用编译配置)
  2. --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 所在的目录
    }
}

mergeFisConf()

合并项目配置文件。从源码可以清楚的看到,包含两个步骤:

  1. fis-conf.js创建缓存。除了配置文件,FIS还会为项目的所有源文件建立缓存,实现增量编译,加快编译速度。缓存的细节后面再讲,这里知道有这么回事就行。
  2. 合并项目自定义配置
// 如果找到了 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 + ']');
}

readSourcesAndReleaseToDest()

通过这个死长的伪函数名,就知道这个步骤的作用了,非常关键。根据当前项目配置,读取项目的源文件,编译后输出到目标目录。

编译过程的细节,下一节会讲到。

项目编译大致流程

项目编译发布的细节,主要是在release这个方法里完成。细节非常的多,主要在fis.release()这个调用里完成,基本上用到了fis-kernel里所有的模块,如releasecompilecache等。

  1. 读取项目源文件,并将每个源文件抽象为一个File实例。
  2. 读取项目配置,并根据项目配置,初始化File实例。
  3. 为File实例建立编译缓存,提高编译速度。
  4. 根据文件类型、配置等编译源文件。(File实例各种属性的修改)
  5. 项目部署:将编译结果实际写到本地磁盘。

伪代码流程如下: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()

前面说了,细节非常多,后续文章继续展开。。。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant