前言关注点分离原则流行了很多年。在实践中,HTML、CSS和JavaScript通常是分开编写和维护的。早期的框架angularjs就是如此,直到React在争论中的出现,引领了焦点混合的趋势,驱使开发者重新审视CSS工程的发展。尴尬的CSS相对于JavaScript的快速发展,CSS的发展是缓慢且相对停滞的。随着前端功能的扩展成为常态,前端工程化越来越成熟,CSS的先天缺陷也越来越明显:全局范围缺乏高级编程特性、代码冗余、极度压缩、和依赖管理。最大的缺陷来自于全局范围,类名在全局范围内生效。多人协作中风格不一致,随时可能引发蝴蝶效应。为了避免多人协作的风格冲突,社区提出了OOCSS、BEM等方法论,但在实践中完全取决于团队的执行力。作者也为合理的命名而苦恼。为了避免冲突,类名冗长、乏味和痛苦。缺乏高级编程功能具有同样深远的影响。社区开发的预处理器可以有效缓解。Sass、less和stylus殊途同归。postcss异军突起,基本实现了变量、嵌套、变量、混合、扩展、逻辑。随着CSS规范的逐步推进,高级的编程特性备受期待。作者大胆断言,前端工程的进步已经基本解决了缺乏高级CSS编程特性的问题。代码冗余,极度压缩对开发影响相对较小。经典的bootstrap包含大量冗余代码,但丝毫不影响它的流行。目前比较难解决的是依赖管理。NPM已经成为事实上的JavaScript包管理工具,但CSS从未开发出可用的管理模型。Sass,比如bootstrap-sass、Bourbon等,显然不能满足需求。以React为首的混合焦点,以组件为中心的开发模式有效避免了CSS缺乏依赖管理的缺陷。笔者认为,依赖管理的弊端是完全可控的,未来的发展交给未来吧。前沿的组件化前端发展日新月异,React在争议中进入了人们的视野。典型的React组件同时包含结构、样式和行为。例子如下:/***@description-litecomponent*@author-huang.jian*/exportclassCounterextendsComponent{constructor(props){super(props);this.state={时间戳:Date.now()};}render(){return();}}前端应用由组件聚合而成,组件层面面向CSSAbstraction,解决大型应用的CSS维护挑战。社区出现的CSSINJS方案目前看来是一个可行的方案。它的本质在于通过JavaScript来声明和维护样式。以styled-components为例:constButton=styled.button`border-radius:3px;填充:0.25em1em;颜色:淡紫红色;border:2pxsolidpalevioletred;`;functionButtons(){return(PrimaryButton);}样式寄生组件,组件挂载时动态插入样式,实现on-按需加载,类名动态生成,作用域隔离。另一种思路是通过style属性传入嵌入的样式,完全避免了选择器全局作用域的问题。//官方例子有删减varRadium=require('radium');varReact=require('react');varcolor=require('color');//你可以动态创建你的样式对象或者分享给//component.varstyles={base:{color:'#fff',},primary:{background:'#0074D9'},warning:{background:'#FF4136'}};@RadiumclassButtonextendsReact.Component{render(){return({this.props.children});}}面向组件开发,为样式管理提供更多可能,完全使用JavaScript抽象、管理和维护样式,略显激进,但也是一种解决方案。客观分析目前主流的CSSINJS方案与传统方法相比如下:优点:隔离作用域——样式通过嵌入生效,或者生成唯一的类名,避免选择器冲突;高级编程特性——充分利用JavaScript的能力增强对样式的控制;样式按需挂载——只加载页面需要的样式,有效避免样式冗余;依赖管理——寄生在组件上,利用现有的NPM生态进行包管理;动态样式--可以更简单直接地修改样式缺点:现有生态无法复用,特性完全依赖于库的实现;编辑器代码补全、语法检查、语法高亮等需要插件支持;伪类选择器(disabled,:before,:nth-child)支持怪异;style属性驼峰式命名;uniquepath作者并不完全认同CSSINJS的概念,也不反对将其应用到生产项目中。CSS中最严重的问题,没有CSS-in-JS还有其他解决方案,就是笔者目前使用的CSSModule方案。工程化的方式,把选择器编译成一个唯一的类名,用JavaScript管理选择器和元素的关联,就这样。//Header.jsximportstylefrom'./Header.css'//{header:'Header__header--3kSIq_0'}exportdefaultfunctionHeader(){return(Header!!!
);}优点:隔离作用域——类名编译生成,有效避免选择器冲突;样式按需加载——采用tree-shaking机制,只保留有引用的选择器,有效避免样式冗余;依赖管理——关联组件,使用现有的NPM机制进行包管理;充分利用现有生态——编辑器高亮、自动补全、sass、postcss高级编程特性;缺点:缺乏动态样式特性——不能充分利用JavaScript的能力增强对样式的控制;本文未涉及的单文件组件的主观感受也是可行方案之一,目前Vue、Angular等框架都在使用。笔者始终认为,与其创造更多的抽象技术让前端的学习曲线更陡峭,不如用工程化的方法修复已有的缺陷,求同存异。面对各种技术方案,最好的方案就是适合实际项目的方案。选择预处理器PostCSS、BEM还是动态编译需要考虑业务场景、团队习惯等因素。关注公众号获取最新消息,支持作者。