[关闭]
@bornkiller 2017-12-18T12:12:07.000000Z 字数 3100 阅读 1297

杂谈 CSS IN JS

前端实践


前言

关注点分离(separation of concerns)原则多年来大行其道,实践中一般将 HTMLCSSJavaScript 分开编写维护,早期框架 angularjs 即是如此,直到 React 争议中问世,引领关注点混合趋势,驱使开发者重新审视 CSS 工程化发展。

尴尬的CSS

相对于 JavaScript 的突飞猛进,CSS 的发展缓慢,相对止步不前。随着前端职能扩大化成为常态,前端工程化日趋成熟,CSS 先天缺陷愈发明显:

最大的缺陷 来自于全局作用域,class name 全局生效,多人协作中的风格不一致,随时可能引发蝴蝶效应。为规避多人协作的风格冲突,社区提出 OOCSSBEM 等方法论,但实践中完全取决于团队执行力度,笔者也曾苦恼于合理的命名,为避免冲突,导致类名冗长,无聊且痛苦。

缺乏高级编程特性 影响同样深远,社区发展的预处理器能够有效缓解,sasslessstylus殊途同归,postcss 异军突起,基本实现变量、嵌套、变量、混合、扩展和逻辑等。随着 CSS 规范逐步推进,高级编程特性完全可期。笔者大胆断言,前端工程化的推进,已经基本解决 CSS高级编程特性缺乏 的问题。

代码冗余,极限压缩对开发的影响相对很小,经典的 bootstrap 就包含大量的冗余代码,但丝毫不影响其流行程度。

目前难以解决的是依赖管理,NPM 已经成为事实上的 JavaScript 包管理工具,而 CSS 始终没有发展出可用的管理模式,sass 的浅尝辄止,例如 bootstrap-sass, Bourbon等,显然无法满足需求。随着 React 引领的关注点混合,以组件为核心的开发模式,有效规避了 CSS 缺乏依赖管理的缺陷,笔者认为,依赖管理弊端完全可控,未来的发展,留给未来述说。

新锐的组件化

前端发展日新月异,React 在众人争议中进入视野,典型的 React 组件同时包含结构、样式、行为,示例如下:

  1. /**
  2. * @description - lite component
  3. * @author - huang.jian <hjj491229492@hotmail.com>
  4. */
  5. export class Counter extends Component {
  6. constructor(props) {
  7. super(props);
  8. this.state = {
  9. timestamp: Date.now()
  10. };
  11. }
  12. render() {
  13. return (
  14. <Card title="React Timestamp">
  15. <Alert message={`React Timestamp: ${this.state.timestamp}`} type="success"/>
  16. <Alert message={`React Timestamp: ${this.state.timestamp}`} type="info"/>
  17. <Alert message={`React Timestamp: ${this.state.timestamp}`} type="warning"/>
  18. </Card>
  19. );
  20. }
  21. }

前端应用由组件聚合而成,组件层面对 CSS 进行抽象,从而解决大型应用的 CSS 维护难题。社区出现的 CSS IN JS 解决方案,目前看来就是可行解决方案,其本质在于通过 JavaScript 来声明,维护样式,以 styled-components 举例:

  1. const Button = styled.button`
  2. border-radius: 3px;
  3. padding: 0.25em 1em;
  4. color: palevioletred;
  5. border: 2px solid palevioletred;
  6. `;
  7. function Buttons() {
  8. return (
  9. <Button>Normal Button</Button>
  10. <Button primary>Primary Button</Button>
  11. );
  12. }

样式寄生组件之中,组件挂载时,动态插入样式,实现按需加载,动态生成类名,隔离作用域。另外一种思路,通过 style 属性传入内嵌样式,完全规避选择器全局作用域的问题。

  1. // 官方示例有删减
  2. var Radium = require('radium');
  3. var React = require('react');
  4. var color = require('color');
  5. // You can create your style objects dynamically or share them for
  6. // every instance of the component.
  7. var styles = {
  8. base: {
  9. color: '#fff',
  10. },
  11. primary: {
  12. background: '#0074D9'
  13. },
  14. warning: {
  15. background: '#FF4136'
  16. }
  17. };
  18. @Radium
  19. class Button extends React.Component {
  20. render() {
  21. return (
  22. <button
  23. style={[
  24. styles.base,
  25. styles[this.props.kind]
  26. ]}>
  27. {this.props.children}
  28. </button>
  29. );
  30. }
  31. }

面向组件开发,为样式管理提供更多的可能性,完全使用 JavaScript 抽象,管理,维护样式,略显激进,但也不失为一种解决方案。

客观的分析

目前主流的 CSS IN JS 方案与传统的方式对比如下:

优势:

劣势:

独辟蹊径

笔者并不完全认同 CSS IN JS 的理念,也不反对将其应用于生产项目。CSS 中最严重的问题,不通过 CSS-in-JS 也能
有其他解决方案,也就是笔者当前使用的 CSS Module 方案。通过工程化的方式,将选择器编译为独一无二的类名,使用 JavaScript 管理选择器与元素的关联,仅此而已。

  1. // Header.jsx
  2. import style from './Header.css'
  3. // { header: 'Header__header--3kSIq_0' }
  4. export default function Header() {
  5. return (<div className={style.header}>Header!!!</div>);
  6. }

优势:

劣势:

主观的感悟

本文未涉及的 单文件组件 也是可行方案之一,目前 VueAngular 等框架采用。笔者始终认为,与其创造更多抽象的技术让前端学习曲线更加陡峭,不如通过工程化的手段来修复存在的缺陷,理念上求同存异。面对各种技术方案,适合实际项目的方案才是最好的方案,选用预处理器 PostCSSBEM,亦或动态编译,都需要结合业务场景、团队习惯等因素决策。

关注公众号,获取动态,支持作者。
qrcode_for_gh_d8efb59259e2_258.jpg-26.1kB

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注