CSSLoader实现思路以及与JSLoader的异同

1 篇文章 0 订阅
童鞋们新年好,2012年只发了两篇水文,就这么过去了,实在汗颜。话说大家都转移阵地去微博和github讨论问题了,谁让iteye还不支持markdown呢 :lol: 。

今天说说我不熟悉的领域CSS,因为手里的项目实在需要,所以最近花了不少精力在我有限的CSS知识范围内寻找解决方案,也经过和很多同事讨论碰撞,不如记录一下。

[b][size=large]CSS也需要模块化[/size][/b]
CSS也需要模块化,这个其实不需要说太多,大家也都是这么做的。在开发阶段css伴随这个各个模块存在。隐约记得还有不少关于面向对象CSS的讨论,也有Less和SASS这种加强CSS结构化的工具。

而在上线前往往需要将各处散落的CSS文件统一合并,或更智能的实时combo。所以今天讨论的CSSLoader是开发时使用。

[b][size=large]CSS模块间存在依赖关系,但用JS模块管理方式一并管理不够恰当[/size][/b]
CSS模块间存在依赖关系,常见的情况是所有css都依赖框架的reset.css,然后可能各个css也都依赖项目通用css如project.css。

但是用js模块管理方式来管理css模块是不够合适的,有以下一些问题:
1. 当前的JSLoader,往往只支持,一个js模块依赖若干css模块,并非真正的支持css间的依赖。
2. css文件内,不支持js语法,所以无法描述它的依赖。内置的@import有它的问题,稍后讲讲。

在讨论中,我们发现还是从CSS的特点出发,为它量身定制Loader解决方案为佳。


[b][size=large]CSS特点是有层次,那么理想的项目CSS规划怎样[/size][/b]
CSS的全称是层叠样式表,这其实说明了CSS的特点是有层次的,而这一点在JS中表现的并不明显,特别大家通过模块化屏蔽了全局变量相互覆盖带来的影响之后。

CSS的层次的特点是,下层的CSS规则,可以覆盖上层的CSS规则,所以我觉得网页理想的CSS规划是每一个CSS模块都能明确其所属层次。

这样的网页CSS层次如下图所示,这也是我们当前项目中的结构:

[img]http://dl.iteye.com/upload/attachment/0079/0897/5c1b0831-2494-3901-804a-77be162fdbcc.png[/img]

1. reset.css : 底层类库提供的通用
2. project.css :项目内通用的css,在我们的项目中可能存在其他项目的project.css,因为我希望不同项目中间的组件可以共享
3. widget.css :组件的通用css
4. widgetInstance.css :组件实例特有的css

可以看出在css分层次规划的背景下,css文件的依赖次序是固定的,4 -> 3 -> 2 -> 1 。 回过头来css的加载顺序应该是1、2、3、4,必须串行。

我们看到这样的规划下css模块是一棵树,如果projcet2.css依赖widget1-1.css显然是不合理的。而js的模块依赖是一张没有闭环即可的图(闭环==循环引用)。

简单总结下[b]理想的CSS规划策略[/b]:
[color=darkblue]
1. 为网页的css划分层次,让每个css模块有明确的层次属性
2. 同一层次内的不同css模块,相互间不应该存在冲突的定义
3. 下层css模块只可以覆盖其所依赖的上层css模块中的部分规则
[/color]

[b][size=large]CSSLoader的要点[/size][/b]

[b]精确的的onload事件[/b]
我们必须保证上层css加载进来、规则完全应用好之后才能加载下层css。这样我们就需要一个精确的onload事件,在早期的getScript方法加载css文件时,只要把请求发了就立即触发onload,以草草达到和js模块一致的回调。而最近这方面的研究已经比较完善,kissy1.2以及seajs的很早的版本就支持真正的css文件onload回调。推荐seajs的做法,因为404时同样会触发回调。代码在这里[url]https://github.com/seajs/seajs/blob/master/src/util-fetch.js#L82[/url],测试用例在这里[url]http://seajs.org/tests/issues/load-css/test.html[/url],感谢玉伯。

我们注意到这里的css加载实现是动态构建link节点,但是在IE下有效的link节点有个数限制,所以方案并不完美。那么其实其他所有为页面引入css的方法都可以用,但前提是需要有靠谱的回调。甚至可以研究下,让css文件也能将载入和规则应用分离开的高级样式引入方式。

[b]并发处理[/b]
一定存在同时初始化多个widget的情况,那么并发加载inst1-1-1和inst1-1-2时,要确保其共同的上层,如projcet1.css最先加载并且不能重复加载。在这一点上可以,参照JSLoader的实现模式,将已经载入的模块记录在案。这一点也引出了@import的问题,如果两个inst里边都写了@import project1.css,那么可能会出现inst1-1-1覆盖projcet1的规则又被覆盖回去的情况。

更进一步,当一个加载任务队列正在进行时,又开启了另一个加载任务队列,如果我们能按照css模块的层次顺序,将两个队列merge成一个队列再继续执行,看起来会更好些。

[b][size=large]API示意[/size][/b]
简单构想了一下扩展后的API,一串CSS加载任务发起还是由JS模块承担。

基于Kissy的话,需要add方法,为js模块增加cssRequires配置项,区别于requires,cssRequires数组内的文件需要按顺序串行加载,能够标识css模块的层次更好。


KISSY.add('mywidget',function(){
//...
},{
requires:['mod-a','mod-b'],
cssRequires:['reset.css:1','projcet.css:2','widget.css:3','instance.css:4']
});


在KISSY.use的时候原有逻辑负责js的加载,同时丢一个任务给新实现的cssLoader,去按层次加载css。

[b][size=large]脆弱的方案[/size][/b]
这绝对不是一个完美的解决方案,当前的实现方案基于“理想的CSS规划策略”,需要分层、同层次不冲突、仅向依赖的上层覆盖,这三点完全做到,而这里最大的挑战似乎是如何技术化的保障同一层次的css不会出现冲突。

在天然的浏览器环境没有很好的提供我们需要的feature时,等待它进化还是不现实的,很多时候就是靠我们能掌控全局这一优势,在不同环节各退一步,达成一个可以勉强运转体系。

好在这个方案是在开发时运转,而且明确的css规划也为真正线上产品的css打包或自动combo提供了便利。

[b][size=large]简单发散一下[/size][/b]

[b]让层次描述更灵活[/b]
约定4个层次,还是太死板了,我们可以参照zIndex的设计,让层次数值之间存在大量空隙供扩展。

[b]其他实现方案[/b]
可能有把css写在js里的方案,有基于less、sass扩展的方案,有开发时跑一个本地服务的方案(类似grunt)。这些都好,但对于如今我们手上的项目来说不够敏捷。其实没准有人已经实现过,欢迎知道的同学推荐。


[b][size=large]最后[/size][/b]
真的不太会CSS,如果有误请帮忙指出,如果有好的方案或者相关的研究都推荐过来吧,谢谢~
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值