UDN-企业互联网技术人气社区

板块导航

浏览  : 4380
回复  : 2

[CSS3] 全局CSS的终结

[复制链接]
cat77的头像 楼主
发表于 2015-10-30 19:34:43 | 显示全部楼层 |阅读模式
  CSS类名总是作用在同一的全局作用域里面。

  任何一个跟CSS有长时间打交道的开发者,都不得不接受CSS那具有侵略性的全局特性,明显地这是一种文档流时代的设计模型。而对于今天现代web应用,更应该积极提出一种更健全的样式环境。

  每一个CSS类名都有可能与其它元素产生的意想不到副作用,又或者产生冲突。更令人吃惊的是,我们的class的效果可能在全局作用域的互相影响下(原文这里比喻为全局唯一性战争),最终在页面上产生很少的效果或者根本没有效果。

  任何时候我们改变一个CSS文件,我们都需要小心翼翼地考虑全局环境是否产生冲突。没有其他前端技术是需要如此之多的规范和约束,而这仅仅是为了保持最低级别的可维护性。

  、、、

  但我们不能一直这样下去。是时候摆脱这种全局样式的折磨。开启局部CSS的时代!

  “在其他语言,全局环境的修改需要变动的代码很少”

  在JavaScript的社区中,感谢Browserify,Webpack和JSPM,让我们的代码变得模块化,每个模块有明确的依赖及其输出的API。然而,不知怎么的,CSS视乎总时被忽略掉。

  我们中许多人,包括我自己,一直使用CSS工作这么长时间,我们都没有发现缺少局部性作用域,是一种问题。因为没有浏览器厂商的重大帮助下我们也能够解决。即使这样,我们仍然需要等待着,大部分用户能使用上浏览器的ShadowDOM的支持。

  在全局作用域问题上,我们已经使用一系列的命名规范来编码。想OOCSS, SMACSS,BEM和SUIT,每一个都提供着一种方式模拟健全的作用域规则,达到避免命名冲突效果。

  虽然驯服CSS无疑是一个巨大的进步,但这些方法都没有解决我们样式表上真正的问题。无论我们选择哪个规范,我们依然被卡在全局类名上。

  但,在2015年的四月22号将会发生改变。

  、、、

  正如我们此前的一篇文章涉及到——“Block,Element,修改你的JavaScript组件”——我们可以利用Webpack把我们的CSS

  作为一种JavaScript模块来引用。如果这听起来很陌生,去读读这篇文章会是一个good idea,以免你错失接下来要讲的内容。

  使用Webpack的css-loader,引用一个组件的CSS如下:
  1. require('./MyComponent.css');
复制代码

  乍一看,这很奇怪,我们引用的是CSS而不是JavaScript

  通常,一个require引入的应该提供一些局部作用域。如果不是,明显低会产生全局作用域的副作用,这是一种拙劣的设计。而CSS的全局作用域特性,却必定产生这样的副作用。

  因此我们在思考

  、、、

  2015年4月22日,Tobias Koppers这位对Webpack孜孜不倦的代码提交者,提交了一个css-loader新特性的版本提交。当时叫placeholder,而现在叫local-scope。这个特性允许我们输出classname从我们的CSS到使用中的JavaScript代码。

  简而言之,下面这种写法:
  1. requrie('./MyComponent.css');
复制代码

  我们改为
  1. import styles from './MyComponent.css';
复制代码

  看看我们导出的CSS是怎么样的,我们的代码大概如下:
  1. :local(.foo){
  2.     color: red;
  3. }
  4. :local(.bar){
  5.     color:blue;
  6. }
复制代码

  在上面的例子中我们使用css-loader的定制的语法  :local(.idntifier) ,输出了两个的标识符,foo和bar。

  这些标识符对应着class strings,这将用在JavaScript文件中去。例如,当我们使用React:
  1. import styles from './MyComponent.css';
  2. import React, { Component } from 'react';
  3. export default class MyComponent extends Component {
  4.   render() {
  5.     return (
  6.       <div>
  7.         <div className={styles.foo}>Foo</div>
  8.         <div className={styles.bar}>Bar</div>
  9.       </div>
  10.     );
  11.   }
  12. }
复制代码

  重要的是,这些标识符映射的class strings,在全局作用域上是保证唯一的。

  我们不再需要给所有的类名添加冗长的前缀来模拟范围。多个组件可以自定义自己的foo和bar标识符。——不像传统的全局作用域的模式,也不会产生命名冲突。

  、、、

  非常关键的一点,不得不承认这已经发生了巨大转变。

  我们现在更有信心地大胆修改我们的CSS,不用小心翼翼地怕影响其他页面的元素。我们引入了一个健全的作用域模式

  全局CSS的好处是,组件间通过通用的class来达到复用的效果——这仍然可以在局部作用域模型上实现。关键的区别是,就像我们编码在其他语言上,我们需要显式地引入我们依赖的类。假想一下在全局命名环境,我们引入的局部CSS不需要很多。

  “编写可维护的CSS现在是值得提倡的,但不是通过谨慎地准守一个命名约定,而是在开发过程中通过独立的封装”

  由于这个作用域模型,我们把实际的classname的控制权移交给Webpack。幸运的是,这是我可以配置的。默认情况下,css-loader会把标识符转换成为hash。

  例如:
  1. :local(.foo){....}
复制代码

编译为:
  1. ._1rJwx92-gmbvaLiDdzgXiJ { … }
复制代码

  在开发环境调试来讲,会带带来一些阻碍。为了令到我们的classes变得更加有用,我们可在Webpack的config里面设置css-loader的参数,配置class的格式。
  1. loaders: [
  2.   ...
  3.   {
  4.     test: /\.css$/,
  5.     loader: 'css?localIdentName=[name]__[local]___[hash:base64:5]'
  6.   }
  7. ]
复制代码

  在这一次,我们的foo这个class会比之前编译的更加好辨认:
  1. .MyComponent__foo___1rJwx { … }
复制代码

  我们能清晰地看得到标识符的名字,以及他来自哪个组件。使用node_env环境变量,我们能根据开发模式和生产环境配置不同的class命名模式。
  1. loader: 'css?localIdentName=' + (
  2.   process.env.NODE_ENV === 'development' ?
  3.     '[name]__[local]___[hash:base64:5]' :
  4.     '[hash:base64:5]'
  5. )
复制代码

  一旦我们发现这个特性,我们不用犹豫地在我们最新的项目上本地化起来。如果按照惯例,我们已经为组件化而使用BEM命名CSS,这真是天作之合。

  有趣的是,一种现象很快地出现了,我们大部分CSS文件里只有局部化class:
  1. :local(.backdrop) { … }
  2. :local(.root_isCollapsed .backdrop) { … }
  3. :local(.field) { … }
  4. :local(.field):focus { … }
  5. etc…
复制代码

  全局性的class仅仅在web应用里面的一小部分,本能地引开出一个重要问题:

  “如果不需要特殊语法,我们的class默认是局部性的,而让全局性的class需要例外。怎么样?”

  如果这样,我们上面的代码就变成如下:
  1. .backdrop { … }
  2. .root_isCollapsed .backdrop { … }
  3. .field { … }
  4. .field:focus { … }
复制代码

  虽然这class通常会过于模糊,但当他们转换为css-lodaer的局部作用域的格式后将会消除这一问题。并且确保了明确的模块作用域来使用。

  少数情况,我们无法避免全局样式,我们可以明确地表明一个特殊的全局语法。例如,当样式使用ReactCSSTransitionGroup来生成一个无作用域classes。
  1. .panel :global .transition-active-enter{…}
复制代码

  在这个例子中,我们不只是使用本地化方式命名我的模块,我们也命名了一个不在我们的作用域上的全局class。

  、、、

  一旦我开始调查我如何实现这个默认局部化class语法,我们意识到它不会太困难。

  为了达到这个目的,我们推荐PostCSS——一个神奇的工具允许你编写自定义的CSS转换插件。今天最受欢迎的CSS构建工具Autoprefixer实际上是PostCSS插件,同时为一个独立的工具而已。

  为让局部CSS正式地使用,我已经开源了一个高度实验性质的插件postcss-local-scope。它仍然在发展,所以在生产环境中使用你需要控制风险。

  如果你使用Webpack,这是非常简单的流程:挂上postcss-loader和postcss-local-scope在你的CSS构建流程。比起文档,我已经创建了一个示例库——postcss-local-scope-example。里面显示了怎么使用的例子。

  令人激动的是,引入局部作用域仅仅是一个开始。
让构建工具处理classname有一些潜在的巨大影响。从长远来看,我们应该停止人为的编译器,而是让计算机来优化输出。

  “在未来,我们可以在一个最优的编译时间内,自动化找出可重用的样式,生成可组件之间共享的class”

  一旦你尝试了局部CSS,你就回不去了。真正体验过,样式的局部作用性在所有浏览器上运行正常,你会难以忘记的体验。

  引入局部作用域对我们处理CSS有重大的的连锁反应。命名规范,重用模式,潜在的样式抽离,分包等等,都会直接受到这种转变的影响。我们仅仅在这里开始了局部CSS的时代。

  理解这种转变的影响是我们依旧需要努力。伴随你有价值的投入和实验,我希望这是作为一个更大的社区的一次谈话

  “加入我们,check出postcss-local-scope-example的代码,眼见为实”

  一旦你行动了,我认为你会同意这并不夸张: 全局CSS的日子将会终结,局部CSS才是未来。

  后记:

  2015年5月24日: postcss-local-scope的最初想法已经被Webpack的TobiasKoppers所接受。这意味着改项目已经被弃用了。现在我们初步确认在css-loader上通过一个module的标志可以支持CSS Modules。我创建了一个库来演示CSSModules在css-loader上的用法,包括类的继承及职能组件间共享样式等。

相关帖子

发表于 2015-11-12 11:35:21 | 显示全部楼层
个人认为html5作为下一代的标准语言,有很大的意义
使用道具 举报

回复

发表于 2015-11-14 16:50:21 | 显示全部楼层
路过 帮顶 嘿嘿
使用道具 举报

回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部