Open fouber opened 10 years ago
满足一切前端开发需求,诚不欺我也。
4天解决如此多的开发需求,给力
为何没有上后端静态资源管理以及后端模板的组件化能力呢?是时间问题还是有的新的思考?
真的很长,晚上回去慢慢看。
@hefangshi
不同的公司要采用不同的技术选型,以松鼠团队现状来看,这样的选型是比较合适的,后面会做更深入的探索。
mark 再看
@fouber 我们有一套基于 Ruby 的集成方案 Linner 在团队实践一年了, 思路一致.
在使用上追求简洁, 易用. 尤其体现在配置上.
@SaitoWu
不错,赞一个,每个团队都会实践自己的解决方案,相信思路上也会一致,因为这个过程是普适的。
工程化还是很复杂的一件事情
赞
牛逼~
写得很好,赞一个
好专业啊
赞~
写的真好 赞一个 战斗力真强
mark一下。
NB
给力!!学习了!!!
So, sexy !
= =。。。好厉害,好多新东西都没办法用到。。。所以也不知道怎么学。。。学的东西,是应为在使用,学不到,是因为,没在用。
@yyman001
早晚会用到,到时再去了解也不迟
seajs开发体系,支付宝团队前端开发体系,以 spm 为构建和包管理工具 fis-plus,百度绝大多数前端团队使用的开发体系,以fis为构建工具内核,以lights为包管理工具 edp,百度ecomfe前端开发体系,以 edp 为构建和包管理工具 modjs,腾讯AlloyTeam团队出品的开发体系 yeoman,google出品的解决方案,以grunt为构建工具,bower为包管理工具
总结的很好
工程化的思考除了需要过硬的编程能力,更多需要的是对人和工程本身的深刻洞察。大赞!
太牛逼~
赞 Fis 关于按需加载有个问题,请教下。 我理解,按需加载其实是按照 Page 的粒度进行的,比 Module 更高一层。理论上讲,Page 会比 Module 少很多。 基于这种想法的按需加载方案: 1、增加两个(js + css)列表(排序防止不用顺序不命中缓存)记录已加载的 Page(直接 require.async 的 Module)。 2、require 新的 Page 时将 1 中的列表拼接发送给服务器。 3、服务器解析依赖,返回对应的 Module 以及依赖的内容。
这样可以: 1、不再需要将所有依赖关系发送给客户端,减小文件大小。 2、请求链接更短(期望值),更清晰。
稍微增加服务器处理,有 Cache 情况下可以无视。
这种方案是不是更好呢?
不愧是国内工业化前端第一人!!服!
当前端项目达到一定规模后,工程问题将成为主要瓶颈,原来孤立的技术要素开始彼此产生影响,需要有人从比较高的角度去梳理、寻找适合自己团队的集成解决方案。
說得好!
@ZoomZhao
了解你的意思了,这也是一种不错的机制,需要把依赖关系表产出给静态资源服务器,然后去diff差异。并不一定是page,只要require.async把自己被调用过的参数记录下来,然后再发起加载请求的时候带上就好了,在后端做diff,非常机智!
大局观啊!我用grunt做前端构建工具时,就把项目文件按照css,html,js,image来划分的。后来才发现,特码的,我写的组件,css,js,图片太分散了。外人怎么引用啊?
@litson 不会看了半天还是只看到fis吧。。。工具不重要,如果用grunt能实现也挺好。问题是工具之外的过程、步骤
写的好详细,点赞
佩服云龙,用fis基本上不用去关注构建的任何问题了,只需关注业务本身,开发效率提升不少!继续推广,普及fis,不同公司在fis的基础上都会很快搭建出一个符合个性化的前端集成解决方案!
正好在梳理工程上的一些问题,lz此文真是雪中送炭那,给了不少启示,十分感谢~
长见识了
是啊,我也是这样想的,看了这篇文章受益不少
发自我的 iPad
在 2014年4月30日,10:39,xiaomingming notifications@github.com 写道:
大局观啊!我用grunt做前端构建工具时,就把项目文件按照css,html,js,image来划分的。后来才发现,特码的,我写的组件,css,js,图片太分散了。外人怎么引用啊?
— Reply to this email directly or view it on GitHub.
component_modules目录存放外部模块资源的意思是这里存放一些公共的模块,例如公共组件等公共资源吗? 如果是这样的话,每新建一个项目都要将所有的公共资源拉到本地来?那这些公共资源又是怎样维护的?
通过compoment或bower。
发自我的 iPad Mini
在 2014年5月2日,16:31,Dongming Ji notifications@github.com 写道:
component_modules目录存放外部模块资源的意思是这里存放一些公共的模块,例如公共组件等公共资源吗? 如果是这样的话,每新建一个项目都要将所有的公共资源拉到本地来?那这些公共资源又是怎样维护的?
— Reply to this email directly or view it on GitHub.
@xiaoji121
我提供了一个命令行工具,叫
scrat install name@version
新建项目,用户可以创建一个 component.json
的文件,内容为:
{
"dependencies" : {
"xxxx" : "1.0.0"
}
}
然后在项目下执行scrat install就能自动在项目中拉取过来了,而真正的代码是维护在其他仓库中的。
赞!
是不是每个项目的工程里面都会有一份公共模块的拷贝,发布时这些公共模块也会作为项目的一部分随项目代码一同发布到线上?
在 2014年5月2日 下午5:16,张云龙 notifications@github.com写道:
@xiaoji121 https://github.com/xiaoji121
我提供了一个命令行工具,叫
scrat install name@version
新建项目,用户可以创建一个 component.json 的文件,内容为:
{ "dependencies" : { "xxxx" : "1.0.0" }}
然后在项目下执行scrat install就能自动在项目中拉取过来了,而真正的代码是维护在其他仓库中的。
— Reply to this email directly or view it on GitHubhttps://github.com/fouber/blog/issues/2#issuecomment-42007425 .
@xiaoji121
是的
不明觉厉,我是来点赞的
涨姿势。
松鼠浏览器的项目来了:这工期还是太长了,能不能再压一下
其实我是来围观的。顺便发一下,这个前端知识收集: https://github.com/jikeytang/front-end-collect
建立alias那段fis-conf.js代码有一行错误:map.alias[match[1]] = id; 应改为 map.alias[match[1]] = file.id; 不然就报错了。很少见到这么完整的讲清楚前端流程的文章,写的超棒。
@cospring 多谢提醒,马上修正
好文!翻来覆去看了将近十遍,终于理清一些东西!赞龙兄!哈哈!
@zhaowx
写的太长了。。。所以要反复阅读么。。。
按照这个开发日记,自己也做下来了。现在剩下合并请求(combo )这块还有些迷茫。 在这个blog中只是提到了在得到依赖关系表之后可以将请求合并,但并木有什么实现办法啊。 我看了下seajs的,有一个seajs-combo插件,也需要配合服务器开启combo功能。 咱这边能详细一些么,可以实施一下! 谢龙兄!
@zhaowx
我在文档中提到过seajs-combo的问题,这个你可以再看看那部分的说明。
... 第一个问题还好,尤其是在gzip下差不多多少字节,但是要配置js压缩器保留require函数不压缩。第二个问题就比较麻烦了,虽然seajs有seajs-combo插件可以一定程度上减少请求,但仍然不能很好的解决这个问题。举个例子,有如下seajs模块依赖关系树: ...
文章中上面那段文字后面我比较详细的叙述了关于模块化框架的设计思路。值得注意的是,业内目前没有设计的非常好的,兼顾了性能的模块化框架,因此我们通常要自己动手实践,其依据就是我在文章中说到的原理了。
这里有一个框架: https://github.com/scrat-team/scrat.js/blob/master/scrat.js
就是这篇文档中提到的一个模块化框架的具体实现,你可以参考一下。其核心实现是借助工具分析生成的依赖关系表来设计模块化框架的接口,你可以看看下面的代码,这样的接口改怎么实现:
require.config({
"deps": {
"proj/1.0.4/bar/bar.js": [
"proj/1.0.4/bar/bar.css"
],
"proj/1.0.4/foo/foo.js": [
"proj/1.0.4/bar/bar.js",
"proj/1.0.4/foo/foo.css"
]
}
});
require.async('proj/1.0.4/foo/foo.js', function(foo){
//todo
});
后面我会再单独写文章详细介绍模块化框架的设计原则。这部分是前端集成解决方案的关键地方
前端开发体系建设日记
上周写了一篇 文章 介绍前端集成解决方案的基本理论,很多同学看过之后大呼不过瘾。
本打算继续完善理论链,形成前端工程的知识结构。但鉴于如今的快餐文化,po主决定还是先写一篇实战介绍,让大家看到前端工程体系能为团队带来哪些好处,调起大家的胃口再说。
新到松鼠团队的第二天,小伙伴 @nino 找到我说
po主不禁暗喜,好机会,这是我专业啊,蓝翔技校-前端集成解决方案学院-自动化系-打包学专业的文凭不是白给的,于是自信满满的对nino说,有什么需求尽管提!
我倒吸一口凉气,但表面故作镇定的说:恩,确实不多,让我们先来看看第一个需求。。。
还没等我说完,nino打断我说
总的来说,还要追加两个部署需求:
我凝望着会议室白板上的这些需求,正打算争辩什么,一扭头发现nino已经不见了。。。正在沮丧之际,小伙伴 @hinc 过来找我,跟他大概讲了一下nino的需求,正想跟他抱怨工期问题时,hinc却说
3天时间,13项前端技术元素,靠谱么。。。
2014年02月13日 - 多云
一觉醒来,轻松了许多,但还有任务在身,不敢有半点怠慢。整理一下昨天的需求,我们来做一个简单的划分。
这样一套规范、框架、工具和仓库的开发体系,服从我之前介绍的 前端集成解决方案 的描述。前端界每天都团队在设计和实现这类系统,它们其实是有规律可循的。百度出品的 fis 就是一个能帮助快速搭建前端集成解决方案的工具。使用fis我应该可以在3天之内完成这些任务。
不幸的是,现在fis官网所介绍的 并不是 fis,而是一个叫 fis-plus 的项目,该项目并不像字面理解的那样是fis的加强版,而是在fis的基础上定制的一套面向百度前端团队的解决方案,以php为后端语言,跟smarty有较强的绑定关系,有着
19项
技术要素,密切配合百度现行技术选型。绝大多数非百度前端团队都很难完整接受这19项技术选型,尤其是其中的部署、框架规范,跟百度前端团队相关开发规范、部署规范、以及php、smarty等有着较深的绑定关系。因此如果你的团队用的不是
php后端
&&smarty模板
&&modjs模块化框架
&&bingo框架
的话,请查看 fis的文档,或许不会有那么多困惑。言归正传,让我们基于 fis 开始实践这套开发体系吧!
0. 开发概念定义
前端开发体系设计第一步要定义开发概念。开发概念是指针对开发资源的分类概念。开发概念的确立,直接影响到规范的定制。比如,传统的开发概念一般是按照文件类型划分的,所以传统前端项目会有这样的目录结构:
这样确实很直接,任何智力健全的人都知道每个文件该放在哪里。但是这样的开发概念划分将给项目带来较高的维护成本,并为项目臃肿埋下了工程隐患,理由是:
以我个人的经验,更倾向于具有一定语义的开发概念。综合前面的需求,我为这个开发体系确定了3个开发资源概念:
基于开发概念的确立,接下来就要确定目录规范了。我通常会给每种开发资源的目录取一个有语义的名字,三种资源我们可以按照概念直接定义目录结构为:
这样划分目录确实直观,但结合前面hinc说过的,希望能使用component仓库资源,因此我决定将模块化资源目录命名为
components
,得到:而nino又提到过模块资源分为项目模块和公共模块,以及hinc提到过希望能从component安装一些公共组件到项目中,因此,一个components目录还不够,想到nodejs用node_modules作为模块安装目录,因此我在规范中又追加了一个
component_modules
目录,得到:nino说过今后大多数项目采用nodejs作为后端,express是比较常用的nodejs的server框架,express项目通常会把后端模板放到
views
目录下,把静态资源放到public
下。为了迎合这样的需求,我将page、static两个目录调整为views
和public
,规范又修改为:考虑到页面也是一种静态资源,而
public
这个名字不具有语义性,与其他目录都有概念冲突,不如将其与views
目录合并,views目录负责存放页面和非模块化资源比较合适,因此最终得到的开发目录结构为:2. 部署目录设计
托nino的福,咱们的部署策略将会非常复杂,根据要求,一个完整的部署结果应该是这样的目录结构:
由于还要部署一些可以被第三方使用的模块,public下只有项目名的部署还不够,应改把模块化文件单独发布出来,得到这样的部署结构:
由于
component_modules
这个名字太长了,如果部署到这样的路径下,url会很长,这也是一个优化点,因此最终决定部署结构为:插一句,并不是所有团队都会有这么复杂的部署要求,这和松鼠团队的业务需求有关,但我相信这个例子也不会是最复杂的。每个团队都会有自己的运维需求,前端资源部署经常牵连到公司技术架构,因此很多前端项目的开发目录结构会和部署要求保持一致。这也为项目间模块的复用带来了成本,因为代码中写的url通常是部署后的路径,迁移之后就可能失效了。
好了,去吃个午饭,下午继续。。。
3. 配置fis连接开发规范和部署规范
我准备了一个样例项目:
fis-conf.js
是fis工具的配置文件,接下来我们就要在这里进行构建配置了。虽然开发规范和部署规范十分复杂,但好在fis有一个非常强大的 roadmap.path 功能,专门用于分类文件、调整发布结构、指定文件的各种属性等功能实现。闲话少说,我们先来看一下基本的配置,在
fis-conf.js
中添加代码:在fis中,roadmap.pah是一个数组数据,数组每个元素是一个对象,必须定义
reg
属性,用以匹配项目文件路径从而进行分类划分,reg属性的取值可以是路径通配字符串或者正则表达式。fis有一个内部的文件系统,会给每个源码文件创建一个 fis.File 对象,创建File对象时,按照roadmap.path的配置逐个匹配文件路径,匹配成功则把除reg之外的其他属性赋给File对象,fis中各种处理环节及插件都会读取所需的文件对象的属性值,而不会自己定义规范。有关roadmap.path的工作原理可以看这里 以及 这里。ok,让md文件不发布很简单,那么views目录下的按版本发布要求怎么实现呢?其实也是非常简单的配置:
roadmap.path数组的第二元素据采用正则作为匹配规则,正则可以帮我们捕获到分组信息,在release属性值中引用分组是非常方便的。正则匹配 + 捕获分组,成为目录规范配置的强有力工具:
在上面的配置中,版本号被写到了匹配规则里,这样非常不方便工程师在迭代的过程中升级项目版本。我们应该将版本号、项目名称等配置独立出来管理。好在roadmap.path还有读取其他配置的能力,修改上面的配置,我们得到:
fis的配置系统非常灵活,除了 文档 中提到的配置节点,其他配置用户可以随便定义使用。比如配置的roadmap是系统保留的,而name、version都是用户自己随便指定的。fis系统保留的配置节点只有6个,包括:
完成第一份配置之后,我们来看一下效果。
进入到项目目录,然后使用fis release命令,对项目进行构建,用
--dest <path>
参数指定编译结果的产出路径,可以看到部署后的结果:fis系统的强大之处在于当你调整了部署规范之后,fis会识别所有资源定位标记,将他们修改为对应的部署路径。
fis的文件系统设计决定了配置开发规范的成本非常低。fis构建核心有三个超级正则,用于识别资源定位标记,把用户的开发规范和部署规范通过配置完整连接起来,具体实现可以看这里。
接下来,我们修改一下项目版本配置,再发布一下看看效果:
再次执行:
得到:
至此,我们已经基本解决了开发和部署直接的目录规范问题,这里我需要加快一些步伐,把其他目录的部署规范也配置好,得到一个相对比较完整的结果:
我构造了一个相对完整的目录结构,然后进行了一次构建,效果还不错:
不管部署规则多么复杂都不用担心,有fis强大的资源定位系统帮你在开发规范和部署规范之间建立联系,设计开发体系不在受制于工具的实现能力。
从前面的例子可以看出,开发使用相对路径即可,fis会在构建时会根据fis-conf.js中的配置完成开发路径到部署路径的转换工作。这意味着在fis体系下开发的模块将具有天然的可移植性,既能满足不同项目的不同部署需求,又能允许开发中使用相对路径进行资源定位,工程师再不用把部署路径写到代码中了。
愉快的一天就这么过去了,睡觉!
2014年02月14日 - 阴转多云
每到周五总是非常惬意的感觉,不管这一周多么辛苦,周五就是一个解脱,更何况今天还是个特别的日子——情人节!
昨天主要解决了开发概念、开发目录规范、部署目录规范以及初步的fis-conf.js配置。今天要进行前端开发体系设计的关键任务——模块化框架。
模块化框架肩负着模块管理、资源加载、性能优化(按需,请求合并)等多种重要职责,同时它也是组件开发的基础框架,因此模块化框架设计的好坏直接影响到开发体系的设计质量。
很遗憾的说,现在市面上已有的模块化框架都没能很好的处理模块管理、资源加载和性能优化三者之间的关系。这倒不是框架设计的问题,而是由前端领域语言特殊性决定的。框架设计者一般在思考模块化框架时,通常站在纯前端运行环境角度考虑,基本功能都是用原生js实现的,因此一个模块化开发的关键问题不能被很好的解决。这个关键问题就是
依赖声明
。以 seajs 为例(无意冒犯),seajs采用运行时分析的方式实现依赖声明识别,并根据依赖关系做进一步的模块加载。比如如下代码:
当seajs要执行一个模块的factory函数之前,会先分析函数体中的
require
书写,具体代码在这里和这里,大概的代码逻辑如下:由于框架设计是在“纯前端实现”的约束条件下,使得模块化框架对于依赖的分析必须在模块资源加载完成之后才能做出识别。这将引起两个性能相关的问题:
第一个问题还好,尤其是在gzip下差不多多少字节,但是要配置js压缩器保留require函数不压缩。第二个问题就比较麻烦了,虽然seajs有seajs-combo插件可以一定程度上减少请求,但仍然不能很好的解决这个问题。举个例子,有如下seajs模块依赖关系树:
采用seajs-combo插件之后,静态资源请求的效果是这样的:
工作过程是
虽然combo可以在依赖层级上进行合并,但完成page.js的请求仍需要4个。很多团队在使用seajs的时候,为了避免这样的串行依赖请求问题,会自己实现打包方案,将所有文件直接打包在一起,放弃了模块化的按需加载能力,也是一种无奈之举。
原因很简单
归根结底,这样的结论是由前端领域语言的特点决定的。前端语言缺少三种编译能力,前面讲目录规范和部署规范时其实已经提到了一种能力,就是“资源定位的能力”,让工程师使用开发路径定位资源,编译后可转换为部署路径。其他语言编写的程序几乎都没有web这种物理上分离的资源部署策略,而且大多具都有类似'getResource(path)'这样的函数,用于在运行环境下定位当初的开发资源,这样不管项目怎么部署,只要getResource函数运行正常就行了。可惜前端语言没有这样的资源定位接口,只有url这样的资源定位符,它指向的其实并不是开发路径,而是部署路径。
这里可以简单列举出前端语言缺少三种的语言能力:
以后我会在完善前端开发体系理论的时候在详细介绍这三种语言能力的必要性和原子性,这里就暂时不展开说明了。
要兼顾性能的同时解决模块化依赖管理和加载问题,其关键点在于
了解了原因,我们就要自己动手设计模块化框架了。不要害怕,模块化框架其实很简单,思想、规范都是经过很多前辈总结的结果,我们只要遵从他们的设计思想去实现就好了。
参照已有规范,我定义了三个模块化框架接口:
define(id, factory);
require.async(ids, callback);
require.config(options);
利用构建工具建立模块依赖关系表,再将关系表注入到代码中,调用
require.config
接口让框架知道完整的依赖树,从而实现require.async
在异步加载模块时能提前预知所有依赖的资源,一次性请求回来。以上面的page.js依赖树为例,构建工具会生成如下代码:
当执行
require.async('page.js', fn);
语句时,框架查询config.deps表,就能知道要发起一个这样的combo请求:从而实现
按需加载
和请求合并
两项性能优化需求。根据这样的设计思路,我请 @hinc 帮忙实现了这个框架,我告诉他,deps里不但会有js,还会有css,所以也要兼容一下。hinc果然是执行能力非常强的小伙伴,仅一个下午的时间就搞定了框架的实现,我们给这个框架取名为 scrat.js,仅有393行。
前面提到fis具有资源依赖声明的编译能力。因此只要工程师按照fis规定的书写方式在代码中声明依赖关系,就能在构建的最后阶段自动获得fis系统整理好的依赖树,然后对依赖的数据结构进行调整、输出,满足框架要求就搞定了!fis规定的资源依赖声明方式为:在html中声明依赖,在js中声明依赖,在css中声明依赖。
接下来,我要写一个配置,将依赖关系表注入到代码中。fis构建是分流程的,具体构建流程可以看这里。fis会在
postpackager
阶段之前创建好完整的依赖树表,我就在这个时候写一个插件来处理即可。编辑
fis-conf.js
我们准备一下项目代码,看看构建的时候发生了什么:
执行fis release查看命令行输出,可以看到consolog.log的内容为:
可以看到js和同名的css自动建立了依赖关系,这是fis默认进行的依赖声明。有了这个表,我们就可以把它注入到代码中了。我们为页面准备一个替换用的钩子,比如约定为
__FRAMEWORK_CONFIG__
,这样用户就可以根据需要在合适的地方获取并使用这些数据。模块化框架的配置一般都是写在非模块化文件中的,比如html页面里,所以我们应该只针对views目录下的文件做这样的替换就可以。所以我们需要给views下的文件进行一个标记,只有views下的html或js文件才需要进行依赖树数据注入,具体的配置为:我在
views/index.html
中写了这样的代码:执行
fis release -d ../release
之后,得到构建后的内容为:在调用
require.async('components/foo/foo.js')
之际,模块化框架已经知道了这个foo.js依赖于bar.js、bar.css以及foo.css,因此可以发起两个combo请求去加载所有依赖的js、css文件,完成后再执行回调。现在模块的id有一些问题,因为模块发布会有版本号信息,因此模块id也应该携带版本信息,从前面的依赖树生成配置代码中我们可以看到模块id其实也是文件的一个属性,因此我们可以在roadmap.path中重新为文件赋予id属性,使其携带版本信息:
重新构建项目,我们得到了新的结果:
you see?所有id都会被修改为我们指定的模式,这就是以文件为中心的编译系统的威力。
接下来还有一个问题,就是模块名太长,开发中写这么长的模块名非常麻烦。我们可以借鉴流行的模块化框架中常用的缩短模块名手段——别名(alias)——来降低开发中模块引用的成本。此外,目前的配置其实会针对所有文件生成依赖关系表,我们的开发概念定义只有components和component_modules目录下的文件才是模块化的,因此我们可以进一步的对文件进行分类,得到这样配置规范:
然后我们为一些模块id建立别名:
再次构建,在注入的代码中就能看到alias字段了:
这样,代码中的
require('foo');
就等价于require('proj/1.0.5/foo/foo.js');
了。还剩最后一个小小的需求,就是希望能像写nodejs一样开发js模块,也就是要求实现define的自动包裹功能,这个可以通过文件编译的 postprocessor 插件完成。配置为:
所有在components目录和component_modules目录下的js文件都会被包裹define,并自动根据roadmap.path中的id配置进行模块定义了。
最煎熬的一天终于过去了,睡一觉,拥抱一下周末。
2014年02月15日 - 超晴
周末的天气非常好哇,一觉睡到中午才起,这么好的天气写码岂不是很loser?!
2014年02月16日 - 小雨
居然浪费了一天,剩下的时间不多了,今天要抓紧啊!!!
让我们来回顾一下已经完成了哪些工作:
模块化开发,js模块化,css模块化,像nodejs一样的模块化开发组件化开发,js、css、handlebars维护在一起采用nodejs后端,基本部署规范应该参考 express 项目部署按版本号做非覆盖式发布公共模块可发布给第三方共享js模块化框架,支持请求合并,按需加载等性能优化点剩下的几个需求中有些是fis默认支持的,比如base64内嵌功能,图片会先经过编译流程,得到压缩后的内容fis再对其进行base64化的内嵌处理。由于fis的内嵌功能支持任意文件的内嵌,所以,这个语言能力扩展可以同时解决前端模板和图片base64内嵌需求,比如我们有这样的代码:
无需配置,既可以在js中嵌入资源,比如 foo.js 中可以这样写:
编译后得到:
支持stylus也非常简单,fis在 [parser]() 阶段处理非标准语言,这个阶段可以把非标准的js(coffee/前端模板)、css(less/sass/stylus)、html(markdown)语言转换为标准的js、css或html。处理之后那些文件还能和标准语言一起经历预处理、语言能力扩展、后处理、校验、测试、压缩等阶段。
所以,要支持stylus的编译,只要在fis-conf.js中添加这样的配置即可:
这样我们项目中的*.styl后缀的文件都会被编译为css内容,并且会在后面的流程中被当做css内容处理,比如压缩、csssprite等。
文件监听、自动刷新都是fis内置的功能,fis的release命令集合了所有编译所需的参数,
这些参数是可以随意组合的,比如我们想文件监听、自动刷新,则使用:
压缩、打包、文件监听、自动刷新、发布到output目录,则使用:
另外,fis是命令行工具,各种内置的插件也是完全独立无环境依赖的,可以与ci平台直接对接,并在各个主流操作系统下运行正常。
利用fis的内置的各种编译功能,我们离目标又近了许多:
模块化开发,js模块化,css模块化,像nodejs一样的模块化开发组件化开发,js、css、handlebars维护在一起采用nodejs后端,基本部署规范应该参考express项目部署按版本号做非覆盖式发布公共模块可发布给第三方共享js模块化框架,支持请求合并,按需加载等性能优化点可以编译stylus为css支持js、css、图片压缩允许图片压缩后以base64编码形式嵌入到css、js或html中与ci平台集成文件监听、浏览器自动刷新剩下两个,我们可以通过扩展fis的命令行插件来实现。fis有11个编译流程扩展点,还有一个命令行扩展点。要扩展命令行插件很简单,只要我们将插件安装到与fis同级的node_modules目录下即可。比如:
那么执行
fis say
这个命令,就能调用到那个fis-command-say插件了。剩下的这个component模块安装,我就利用了这个扩展点,结合component开源的 component-installer 包,我就可以把component整合当前开发体系中,这里我们需要创建一个npm包来提供扩展,而不能直接在fis-conf.js中扩展命令行,插件代码我就不贴了,可以看 这里。眼前我们有了一个差不多100行的fis-conf.js文件,还有几个插件,如果我把这样一个零散的系统交付团队使用,那么大家使用的步骤差不多是这样的:
npm install -g fis
npm insatll -g fis-command-component
npm install -g fis-parser-stylus
这种情况让团队用起来会有很多问题。首先,安装过程太过麻烦,其次如果项目多,那么fis-conf.js不能同步升级,这是非常严重的问题。grunt的gruntfile.js也是如此。如果说有一个项目用了某套grunt配置感觉很爽,那么下个项目也想用这套方案,复制gruntfile.js是必须的操作,项目用的多了,同步gruntfile的成本就变得非常高了。
因此,fis提供了一种“包装”的能力,它允许你将fis作为内核,包装出一个新的命令行工具,这个工具内置了一些fis的配置,并且把所有命令行调用的参数传递给fis内核去处理。
我准备把这套系统打包为一个新的工具,给它取名为
scrat
,也是一只松鼠。这个新工具的目录结构是这样的:其中,index.js的内容为:
将这个npm包发布出来,我们就有了一个全新的开发工具,这个工具可以解决前面说的13项技术问题,并提供一套完整的集成解决方案,而你的团队使用的时候,只有两个步骤:
使用新工具的命令、参数几乎和fis完全一样:
而scrat这个工具所内置的配置将变成规范文档描述给团队同学,这套系统要比grunt那种松散的构建系统组成方式更容易被多个团队、多个项目同时共享。
终于到了周一,交付了一个新的开发工具——scrat,及其使用 文档。
然而,过去的三天,为了构造这套前端开发体系,都写了哪些代码呢?
一共
960行
代码,用了4人/天。总结
如果说只是实现一个简单的编译+压缩+文件监+听自动刷新的常规构建系统,基于fis应该不超过1小时就能完成一个,但要实践完整的前端集成解决方案,确实需要点时间。
如之前一篇 文章 所讲,前端集成解决方案有8项技术要素,除了组件仓库,其他7项对于企业级前端团队来说,应该都需要完整实现的。即便暂时不想实现,也会随着业务发展而被迫慢慢完善,这个完善过程是普适的。
对于前端集成解决方案的实践,可以总结出这些设计步骤:
我们可以看看业界已有团队提出的各种解决方案,无不以这种思路来设计和发展的:
纵观这些公司出品的前端集成解决方案,深入剖析其中的框架、规范、工具和流程,都可以发现一些共通的影子,设计思想殊途同归,不约而同的朝着一种方向前进,那就是前端集成解决方案。尝试将前端工程孤立的技术要素整合起来,解决常见的领域问题。
在这里我不能给出肯定或者否定的答复。
因为单纯从语言的角度来说,html、js、css(甚至有人认为css是数据结构,而非语言)确实是最简单最容易上手的开发语言,不用模块化、不用工具、不用压缩,任何人都可以快速上手,完成一两个功能简单的页面。所以说,在一般情况下,前端开发非常简单。
但正是由于前端语言这种灵活松散的特点,使得前端项目规模在达到一定规模后,工程问题凸显,成为发展瓶颈,各种技术要素彼此之间开始出现关联,要用模块化开发,就必须对应某个模块化框架,用这个框架就必须对应某个构建工具,要用这个工具,就必须对应某个包管理工具……这个时候,完整实践前端集成解决方案就成了不二选择。
所以会出现一些框架或工具在小项目中使用的好好的,一旦放到团队里使用就非常困难的情况。
前端入门虽易工程不易,且行写珍惜!