FIS 插件机制
author: @TiffanysBearphp
当咱们使用 FIS 插件的时候,有没有想过本身也开发一个基于 FIS 的插件,参与 FIS 打包编译的整个流程;那么问题就来了:css
基于如下的问题,从原理再进行慢慢分析,了解 FIS 编译的基本流程和原理,以及如何本身自定义一个 FIS 插件。html
fis的编译过程能够分为两个阶段: 单文件编译 和 打包。处理流程以下图,图片来自 FIS 官网:前端
[图片上传失败...(image-e08187-1566795911500)]node
从图上能够看出,单文件编译过程都是经过pipe管道进行的,而且在最初都创建有缓存,以提高编译效率,在单文件的处理过程当中,又主要分为了如下的几个步骤:git
- parser(编译器):将其余语言编译为标准js、css,好比将前端模板、coffee-> script编译为js,将less、sass编译为css。
- preprocessor(标准预处理器):在fis进行标准化处理以前进行某些修改,好比 支持image-set语法的预处理插件
- standard(标准化处理):前面两项处理会将文件处理为标准的js、css、html语法,fis内核的标准化处理过程对这些语言进行 三种语言能力 扩展处理。这也就意味着,使用less、coffee等语法在fis系统中同样具有 资源定位、内容嵌入,依赖声明 的能力。该过程 不可扩展。
- postprocessor(标准后处理器):对文件进行标准化以后的处理,好比利用依赖声明能力实现的 js包装器插件,能够获取js文件的依赖关系,并添加define包装。
- lint(可选)(校验器):代码校验阶段,使用 fis release命令的 --lint 参数会调用该过程。
- test(可选)(测试器):自动测试阶段,使用 fis release命令的 --test 参数会调用该过程。
- optimize(可选)(优化器):代码优化阶段,使用 fis release命令的 --optimize 参数会调用该过程。fis内置的fis-optimizer-uglify-js插件和fis-optimizer-clean-css插件都是这类扩展。
若是是文件的简单合并,可使用 __inline 进行简单的内容嵌入,若是嵌入的内容中须要实时嵌入动态变量,能够考虑使用 bdtmpl 进行前端模块的编译和转换。github
打包的原理是经过 FIS 的pack 配置,对文件资源进行合并等操做,最后产出关于文件打包信息到 map.json 文件中,并产生相应的打包文件。因此 FIS 的打包结果并 不会再嵌入到某个文件内,而是利用map.json中的数据进行运行时打包信息查询。npm
1、 在fis-conf.js中配置:json
fis.config.merge({ pack : { 'aio.js' : ['a.js', 'b.js', 'c.js'] } });
2、 执行命令 fis release --pack --dest ./output后端
3、 进入output目录,查看map.json文件,获得内容:
{ "res" : { "a.js" : { "uri" : "/a.js", "type" : "js", "pkg" : "p0" }, "b.js" : { "uri" : "/b.js", "type" : "js", "pkg" : "p0" }, "c.js" : { "uri" : "/c.js", "type" : "js", "pkg" : "p0" } }, "pkg" : { "p0" : { "uri" : "/aio.js", "type" : "js", "has" : ["a.js", "b.js", "c.js"] } } }
4、 将map.json交给某个前端或后端框架,当运行时须要“a.js”资源的时候,该框架应该读取map.json的信息,并根据必定的策略决定是否应该返回“a.js”资源所标记的“p0”包的uri。
所以也能够看出,FIS 团队强调的一点,打包只是资源的备份。
fis系统的打包过程提供了4个可扩展的处理过程,它们是:
fis的插件也是一个npm包,利用fis.require函数来加载。当咱们在fis系统中加载一个插件的时候,会利用 nodejs的require向上查找机制 从 fis-kernel 模块出发,向上查找所需模块。
fis插件系统巧妙的利用了nodejs的require机制来实现其扩展机制。这意味着,要想扩展fis能够有 三种途径 :
一、使用fis的用户,本身须要某种插件,能够在fis安装目录的 同级,安装本身扩展的插件。好比: npm install -g fis
npm install -g fis-parser-coffee-script
二、使用 FIS 内置的插件,目前已经内置的插件包括:
三、开发一个依赖于fis模块的npm包,并在这个包里定制所须要的插件。这种方式与上一条相似,也是将插件安装在fis的同级目录下。
在整个编译流程能够扩展的点有如下,也就说说咱们本身自定义的插件能够在下列的时机进行本身需求的定制,经过回调获取该阶段编译的结果,进行自定义配置。
编译阶段:
打包阶段:
自定义插件须要的,须要封装一个npm包,结合上面的可扩展时机,命名规则通常为:fis-[须要插入的时机名称]-[自定义插件名],例如:fis-parse-my-css;
一、在自定义插件的index.js中:
/* * fis * http://fis.baidu.com/ */ 'use strict'; var sass = require('node-sass'); module.exports = function(content, file, settings) { // content: 内容 // file: 文件 // settings: 如今的配置 var opts = fis.util.clone(settings); opts.data = content; return sass.renderSync(opts); };
二、插件配置调用
在fis-config.js中调用格式以下:
// vi fis-conf.js // 文件后缀 .scss 的调用插件 my-sass 进行解析 fis.config.set('modules.parser.scss', 'my-sass'); fis.config.set('settings.parser.my-sass', { // my-sass 的配置 }); fis.config.set('roadmap.ext.scss', 'css'); // 因为 scss 文件最终会编译成 css,设置最终产出文件后缀为 css
三、发布npm包,这个能够参考我以前写过的一个文档,从0到1发布一个npm包
一、插件接口如此:
/** * 打包阶段插件接口 * @param {Object} ret 一个包含处理后源码的结构 * @param {Object} conf 通常不须要关心,自动打包配置文件 * @param {Object} settings 插件配置属性 * @param {Object} opt 命令行参数 * @return {undefined} */ module.exports = function (ret, conf, settings, opt) { // ret.src 全部的源码,结构是 {'<subpath>': <File 对象>} // ret.ids 全部源码列表,结构是 {'<id>': <File 对象>} // ret.map 若是是 spriter、postpackager 这时候已经能获得打包结果了, // 能够修改静态资源列表或者其余 }
以prepackager插件为例。prepackager即打包前须要对文件作某些处理,好比想在全部的html注释里面插入编译时间。
二、插件开发
咱们为这个插件取名叫 append-build-time
<npm/global/path>/fis-prepackager-append-build-time <npm/global/path>/fis-prepackager-append-build-time/index.js
在其 index.js 中:
module.exports = function(ret, conf, settings, opt) { fis.util.map(ret.src, function(subpath, file) { if (file.isHtmlLike) { var content = file.getContent(); content += '<!-- build '+ (new Date())+'-->'; file.setContent(content); } }); };
三、配置使用
// vi fis-conf.js fis.config.set('modules.prepackager', 'append-build-time'); // packager阶段插件处理全部文件,因此不须要给某一类后缀的文件设置。 fis.config.set('settings.prepackager.append-build-time', { // settings })
四、发布npm包,这个能够参考我以前写过的一个文档,从0到1发布一个npm包
固然为了更快速的搞定一些小需求,能够把插件功能直接写到配置文件 fis-conf.js 中;
// vi fis-conf.js fis.config.set('modules.postprocessor.js', function (content) { return content += '\n// build time: ' + Date.now(); });
注意:配置使用插件时,同一个扩展点能够配置多个插件,好比;
// 调用 fis-prepackager-a, fis-prepackager-b ...插件 fis.config.set('modules.prepackager', 'a,b,c,d'); // or fis.config.set('modules.prepackager', ['a', 'b', 'c', 'd']); // or fis.config.set('modules.prepackager', [function () {}, function () {}])
具体的原理和封装步骤就讲到这么多,具体封装解决方案能够见 FIS 的官网 —— 封装解决方案
本文参考文档:
一、插件扩展点列表 http://fex-team.github.io/fis-site/docs/more/extension-point.html
二、插件调用机制 http://fex-team.github.io/fis-site/docs/more/how-plugin-works.html
三、编译过程运行原理 http://fex-team.github.io/fis-site/docs/more/fis-base.html