本文作者:IMWeb 黎清龙 原文出处:IMWeb社区 未经同意,禁止转载
fis3相比fis2,核心思路并没有改变
还是围绕3个核心做处理:
而整体构建流程也没有做过多的改动。
fis3相对于fis2来说,用户更容易使用,给插件开发者提供更多的能力
这主要体现于以下两点:
这篇文章主要介绍的就是第2点,讲解fis3提供的特殊事件,及其作用。
fis3 在查找文件资源的时候,通过统一的接口【fis.project.lookup】查找
在该接口里面,会抛出lookup:file事件,看如下注释:
/**
* 当查找文件时派送, 可以扩展 fis 默认的查找文件功能。如:支持无后缀文件查找,支持 components 文件查找。
* @event lookup:file
* @property {Object} info 包含查找路径信息。
* @property {File} file 文件对象。
* @memberOf fis
*/
fis.emit('lookup:file', info, file);
info参数是一个对象,因此,我们通过监听lookup:file事件来对文件资源的id进行额外处理
fis3-hook-lego是一个fis3 版本的 lego组件系统 包管理的模块查找插件
它的作用是让用户使用lego组件如同使用node组件一样,让系统能够智能的识别lego组件,感兴趣的读者可以深入了解该插件
实际上,fis3-hook-lego的核心原理就是利用了lookup:file事件,看如下源代码:
module.exports = function(fis, opts) {
fis.on('lookup:file', function(info, file) {
// 不处理相对路径的文件和已经处理了的文件
if (file.isJsLike && info.rest && info.rest[0] !== '.' && !info.id) {
var ret = lookup(info.rest, opts); // 尝试lego模块查找
if (ret && ret.file) { // 如果找到,则修改id,定位到相应文件
info.id = ret.file.getId();
info.file = ret.file;
}
}
});
};
这4个事件的用意非常明显,fis3的构建流程主要分为2步:每个文件的compile和所有文件的package
以上4个事件就是这两个过程的开始以及结束锚点
这4个关键节点的用处不言而喻,下面笔者举两个例子
var timeMap = {};
fis.on('proccess:start', function(file) {
timeMap[file.id] = +new Date();
});
fis.on('proccess:end', function(file) {
console.log(file.id, 'compile use:', +new Date() - timeMap[file.id], 'ms');
});
fis.on('release:start', function(file) {
timeMap['release:'] = +new Date();
});
fis.on('release:end', function(file) {
console.log('release use:', +new Date() - timeMap['release:'], 'ms');
});
结果如下:
在打包过程中,通常我们需要许多复杂的处理,为了提升构建效率,可能我们需要做一些缓存机制
但是,我们需要在构建结束的时候,把这些缓存清掉,否则就会影响下一次构建(在watch的情况下)
这个时候,release:end事件就可以很好的为我们工作:
fis.on('release:end', function() {
// 每次处理完都需要重置cache
readCache = {};
});
以上5个事件是文件compile阶段中5个关键节点的锚点
在每个阶段中,对file进行相应阶段的处理之前,fis3都会先抛出一个事件
这些节点的意义也非常深远,甚至可以改变fis3的compile流程,同样笔者也举两个例子
因为fis3的构建流程固化了,有时候我们在进行某些处理的时候发现,我们需要在某个操作的前后分别进行处理,比如我要在资源inline替换的前后做某些操作,我们知道inline替换是standard步骤进行的,那么我们可能需要写两个插件,分别是:
fis3-preprocessor-xxx 和 fis3-postprocessor-xxx
我们真的需要吗?
No,通过事件,我们可以只需要一个插件!看如下代码:
// file: fis3-preprocessor-xxx
var conf;
fis.on('compile:postprocessor', function(file) {
// some process after inline
});
module.exports = function(content, file, settings) {
// some process before inline
conf = settings; // save the settings which can be used on postprocessor
};
需要注意以下两点:
有5个事件,所以我们可以把5个插件何在一起!!!
compile是单文件处理,在这个过程中是拿不到其他文件的,它不像package阶段插件那样,有一个ret参数可以使用
但是,有时候,我们在compile阶段真的需要其他一些文件资源的时候怎么办?
在fis2或者不知道有额外能力的情况下,往往我们会把我们的处理放到package阶段,在这里我们才能找到其他文件,然后做相应处理,然后,可能,你需要蛋疼的重新compile一次该文件(笔者是做过这种事情的)
解决方案是,你可以在compile阶段自己存储所有的文件资源!
请看如下代码:
// file: fis3-parser-xxx
// parse是最早的事件点,所以要定义这个点的插件,详见4.1
var fileMap = {};
fis.on('compile:postprocessor', function(file) {
var deps = findDepsOfFile(file);
if (deps.length) { // 需要依赖依赖其他文件
if (hasAllDepsFile(deps)) {
// 所有依赖的资源已经compile,现在可以进行处理了!
process(file);
// 然后需要看下其他文件是否依赖这个文件
processIfOtherFileNeedThisFile(file);
} else {
// 资源还没到齐,先缓存起来
fileMap[file.id].deps = deps;
}
} else {
processIfOtherFileNeedThisFile(file);
}
});
module.exports = function(content, file, settings) {
// 收集file对象
fileMap[file.id] = {
file: file
};
};
看起来很绕,也确实很绕,在这里只是想让读者知道,可以这样子处理
这样处理已经违背了fis3的核心理念,慎用!
可以看到,fis3最重大的改进就是提供了更多的想象空间给插件开发者
合理利用这些特性,我们可以在fis3的环境下做许多事情
但就只有这些吗?
No!还有另外一些更加有趣更加开放的特性还没介绍。
敬请期待下一期!