了解前端理论:rscss和rsjs


Posted in Javascript onMay 23, 2019

在前端开发中,我们会尝试去定一些规则和约定,来让项目质量更高,更易于维护。而对于这些规则和约定,我们也会希望它内容简单,容易理解。

rscss和rsjs是一套比较新,也比较小巧的前端开发规则和约定,其中rs代表Reasonable System,所以可以理解为,追求“合理”的css和js。本文除了介绍它们,还会有一点补充以及我自己的看法,也推荐你点击链接阅读原作者给出的完整内容。

从css的疑问开始

rscss希望有效地改善写css中的这样几个常见问题(css哲学三问):

  • 这个class到底什么意思?
  • 这个class还有地方用到吗?
  • 我新写的这个class,会有冲突吗?

组件原则

rscss首先推崇的是以组件(Components)为基础的思考方式。在各类前端框架中,几乎都可以看到组件,如Bootstrap和Materialize:

了解前端理论:rscss和rsjs

一个组件是这样的感觉:

了解前端理论:rscss和rsjs

小到一个按钮,大到整个web应用,可见的视觉元素都可以这样当做一个组件。

组件的命名

rscss推荐组件至少使用两个单词的命名,中间用短横线(-)连接:

.search-form { /* ... */ }
.article-card { /* ... */ }

组件的元素

组件内部的更细小的部分,当做组件的元素(Elements)。

了解前端理论:rscss和rsjs

元素的命名

为了和前面的组件区分开来,元素的命名只使用一个单词。

显然,只有一个单词是很容易冲突的,因此rscss建议以关系选择符把元素和组件关联起来:

.search-form > .field { /* ... */ }
.search-form > .action { /* ... */ }

推荐子选择符 > 而不是包含选择符 (空格),以更好地避免冲突:

.article-card .title { /* okay */ }
.article-card > .author { /* ✓ better */ }

如果确实需要用到多个单词,直接连接它们(不使用短横线等分隔符),以体现区别:

.profile-box > .firstname { /* ... */ }

为每一个组件的元素使用class名,不要使用标签选择符。有名字的元素会更有语义。

多种属性或状态

无论是组件还是元素,都可以有多种属性或状态(Variants,也可以叫变体):

了解前端理论:rscss和rsjs

属性或状态的命名

使用短横线(-)开头来命名表示属性或状态的class。

/* component variants */
.like-button.-wide { /* ... */ }
.like-button.-disabled { /* ... */ }

/* element variants */
.shopping-card > .title.-small { /* ... */ }

对命名方式的解释

rscss推荐的短横线作为前缀的class名可能会让你有一点惊讶,可以这样写的吗?答案是的确可以,而且搭配得还相当巧妙。为什么这么说呢?请看w3c对css标识符的解释:

In CSS, identifiers (including element names, classes, and IDs in selectors) can contain only the characters [a-zA-Z0-9] and ISO 10646 characters U+0080 and higher, plus the hyphen (-) and the underscore (_); they cannot start with a digit, two hyphens, or a hyphen followed by a digit.

其中ISO 10646等同于Unicode。可以看到,w3c特意在css标识符一般使用的英文字母、数字以及一部分Unicode字符(U+0080以上)之外,提到了短横线(-)和下划线(_)也是可用的。

以短横线作为前缀的class名相当于有了一个特殊的标记,一眼就可以提醒你这是一个表示属性或状态的class。

组件嵌套

组件是可以嵌套的。

了解前端理论:rscss和rsjs

对应html类似这样:

<div class="article-link">
 <div class="vote-box">
 ...
 </div>
 <h3 class="title">...</h3>
 <p class="meta">...</p>
</div>

嵌套中的属性或状态

当一个组件位于另一个组件内部的时候,可能会想要这个组件表现得特别一点。这个时候,建议不要使用关系选择符把它们耦合在一起:

.article-header > .vote-box > .up { /* ✗ avoid this */ }

建议的做法是为组件增加一个属性或状态class:

<div class="article-header">
 <div class="vote-box -highlight">
 ...
 </div>
 ...
</div>

然后以这个class为基础来定义特别的样式:

.vote-box.-highlight > .up { /* ... */ }

这样做的目的是让一个组件的样式不依赖其所处的位置。OOCSS的原则之一,Separate container and content,也是这样的理念。

布局思想

rscss推荐除一些具有固定宽高的特定元素(如头像,logo)外,组件本身不定义任何影响布局位置的属性:

  • 定位(positiontopleftrightbottom
  • 浮动(floatclear
  • 外边距(margin
  • 尺寸(widthheight

这样做的意思是说,如果把组件看做一个整体,它应该是自适应的。

需要定义布局位置属性的情况

如果要定义组件的影响布局位置的属性,建议使用关系选择符把组件和它所处的环境关联起来:

.article-list > .article-card {
 width: 33.3%;
 float: left;
}

.article-card { /* ... */ }
.article-card > .image { /* ... */ }
.article-card > .title { /* ... */ }
.article-card > .category { /* ... */ }

在上面这段代码可以注意到,“组件本身的外观”与“组件在某一环境中的位置”被明确地分离了。

辅助类

rscss推荐辅助类(Helpers)单独存放一个文件,且class名以下划线(_)开头。辅助类也常会用到!important,对应的,应尽可能少使用辅助类。

._pull-left { float: left !important; }
._pull-right { float: right !important; }

下划线(_)作为前缀的class名,如前文已经解释过的那样,也是作为一个特殊的标记提醒你这是一个辅助类,请谨慎使用它。

辅助类在前端框架中也很常见。

rscss与其他css理论的比较

rscss的组件(Component),元素(Element)等概念,在BEM、SMACSS这些css理论中也有类似的存在。它们比较起来是这样的:

RSCSS BEM SMACSS Component Block Module Element Element Sub-Component Layout ? Layout Variant Modifier Sub-Module & State

以上就是rscss的主要内容了,下面来看看rsjs。

关注传统web应用的rsjs

rsjs关注的是非单页应用(non-SPA web application),也就是我们通常理解的有很多页,主要使用jQuery,而且每个页都可以有自己的.js文件的传统网站。

现在已经有了可遵循的JavaScript代码本身的风格指南,因此,rsjs只对一些其他的要点提出建议,如命名空间,文件组织方式。

行为原则

rsjs推荐把由JavaScript实现的交互功能当做一次只影响一个组件(Component)的行为(Behavior)。下面是一个参考示例:

<div class="main-navbar" data-js-collapsible-nav>
 <button class="expand" data-js-expand>Expand</button>

 <a href="/">Home</a>
 <ul>...</ul>
</div>
/* Behavior - behaviors/collapsible-nav.js */

$(function () {
 var $nav = $("[data-js-collapsible-nav]");
 if (!$nav.length) return;

 $nav
 .on("click", "[data-js-expand]", function () {
  $nav.addClass("-expanded");
 })
 .on("mouseout", function () {
  $nav.removeClass("-expanded");
 });
});

这其中包含了多项建议。

使用data属性

建议使用html5的data自定义属性data-js-___来标记和一个行为有关的DOM元素。

相比用ID和class来选取元素,这种data属性的形式一方面更具有明确的意义,提醒你这是一个和交互行为有关的元素,另一方面更易于复用,在任何DOM结构里添加这样的data属性即可获得对应的行为。

为每个行为单独建立文件

建议每一个行为对应的JavaScript代码都分离到单独的文件里,并以文件名明示。文件名可以参照data-js-___这个属性名里的对应名称,这样,根据属性名就很容易找到对应的JavaScript代码。

一个可能的文件目录结构:

└── javascripts/
 └── behaviors/
   ├── collapsible-nav.js
   ├── avatar-hover.js
   ├── popup-dialog.js
   └── notification.js

不使用行内JavaScript

在html中不要以<script>...</script>onclick=""等形式添加行内JavaScript代码。通过保持行为的逻辑代码独立于html,可以使代码更易于维护。

从rsjs的内容来看,在已有React、Vue等库的今天,“行为独立于内容”的约定仍然对传统的以jQuery为主的Web应用有一定意义。

初始数据的获取方式

传统Web站点的一个常见的场景是,后端语言在页面中预先输出某些数据,然后JavaScript会取用它们。你可能见到过下面这样<script>标签的实现方式,但显然,根据上一条建议,这是应避免的。

<script>
window.UserData = { email: "john@gmail.com", id: 9283 }
</script>

rsjs建议的方案是,如果这些数据只需要一个组件使用,可以利用之前提到的data属性(保存为值),由行为的JavaScript代码来自行取出。

<!-- ✓ Used by the user-info behavior -->
<div class="user-info" data-js-user-info='{"email":"john@gmail.com","id":9283}'>

如果是多个组件使用的数据,可以使用<head>里的meta标签。

<head>
 ...
 <!-- option 1 -->
 <meta property="app:user_data" content='{"email":"john@gmail.com","id":9283}'>

 <!-- option 2 -->
 <meta property="app:user_data:email" content="john@gmail.com">
 <meta property="app:user_data:id" content="9283">

命名空间

rsjs建议使用尽可能少的全局变量。共用的类,函数,放到单个Object里,比如叫App

if (!window.App) window.App = {};

App.Editor = function() {
 // ...
};

在多个行为之间可复用的帮助方法,可以单独建立Object,并将它们分文件保存在helpers/

/* helpers/format_error.js */
if (!window.Helpers) window.Helpers = {};

Helpers.formatError = function (err) {
 return "" + err.project_id + " error: " + err.message;
};

第三方库的处理

rsjs建议如果引入第三方库,也做成组件行为的形式。比如,Select2的功能,可以只影响带有属性data-js-select2的元素。

// select2.js -- affects `[data-js-select2]`
$(function () {
 $("[data-js-select2]").select2();
});

所有第三方库的代码可以集中到一个类似vendor.js的文件,并和站点本身的代码各自独立。这样,当站点更新代码的时候,用户可以直接利用缓存,而并不需要再次获取这些第三方库代码。

rsjs对自己的归纳

rsjs认为自身的内容更偏向于对开发者友好,也就是更易于维护,而在性能上(对用户友好)可能没有做到最好。以上提到的各项建议,也是有利有弊,rsjs只是在权衡了利弊的基础上得到的更利于长期维护的结论。

rsjs不是万金油,它不适用于单页应用(SPA)等前端功能很复杂的情况。它关注的是的那种多个网页,每个网页一点JavaScript交互的传统网站。

结语

rscss和rsjs所用的“合理”是一个很取巧的表述,不是完美,不是最好,也不是出色,它只是在说希望代码能“合乎道理”。rscss和rsjs大概就是这样,以简约的风格,不长的篇幅,追求着“小而合理”。

目前rsjs还在更新中(work-in-progress),rscss则已经比较成熟。很推荐试试其中你也认为合理的建议!

Javascript 相关文章推荐
jQuery 前的按键判断代码
Mar 19 Javascript
javascript encodeURI和encodeURIComponent的比较
Apr 03 Javascript
web基于浏览器的本地存储方法应用
Nov 27 Javascript
让jQuery Mobile不显示讨厌loading界面的方法
Feb 19 Javascript
js验证框架实现代码分享
May 18 Javascript
jQuery实现右下角可缩放大小的层完整实例
Jun 20 Javascript
vue iview组件表格 render函数的使用方法详解
Mar 15 Javascript
20多个小事例带你重温ES10新特性(小结)
Sep 29 Javascript
JS原形与原型链深入详解
May 09 Javascript
Vue中keep-alive的两种应用方式
Jul 15 Javascript
在vue中使用echarts(折线图的demo,markline用法)
Jul 20 Javascript
vue-cli 3如何使用vue-bootstrap-datetimepicker日期插件
Feb 20 Vue.js
微信小程序使用字体图标的方法
May 23 #Javascript
个人小程序接入支付解决方案
May 23 #Javascript
一篇文章介绍redux、react-redux、redux-saga总结
May 23 #Javascript
微信小程序上传图片到php服务器的方法
May 23 #Javascript
React精髓!一篇全概括小结(急速)
May 23 #Javascript
微信小程序实现上传word、txt、Excel、PPT等文件功能
May 23 #Javascript
微信小程序实现文件、图片上传功能
Aug 18 #Javascript
You might like
PHP入门学习的几个不错的实例代码
2008/07/13 PHP
PHP+MySQL 制作简单的留言本
2009/11/02 PHP
PHP zlib扩展实现页面GZIP压缩输出
2010/06/17 PHP
探讨如何在PHP开启gzip页面压缩实例
2013/06/09 PHP
PHP操作MongoDB GridFS 存储文件的详解
2013/06/20 PHP
PHP使用内置函数生成图片的方法详解
2016/05/09 PHP
PHP设计模式之工厂模式与单例模式
2016/09/28 PHP
php封装的验证码类分享
2017/02/26 PHP
在 Laravel 中动态隐藏 API 字段的方法
2019/10/25 PHP
繁简字转换功能
2006/07/19 Javascript
如何实现JS函数的重载
2006/09/22 Javascript
javascript 获取图片尺寸及放大图片
2013/09/04 Javascript
jquery foreach使用示例
2013/09/12 Javascript
JS案例分享之金额小写转大写
2014/05/15 Javascript
谈一谈js中的执行环境及作用域
2016/03/30 Javascript
一次围绕setTimeout的前端面试经验分享
2017/06/15 Javascript
使用webpack搭建react开发环境的方法
2018/05/15 Javascript
JavaScript中window和document用法详解
2020/07/28 Javascript
python中实现k-means聚类算法详解
2017/11/11 Python
用TensorFlow实现lasso回归和岭回归算法的示例
2018/05/02 Python
浅谈Python 多进程默认不能共享全局变量的问题
2019/01/11 Python
python ChainMap 合并字典的实现步骤
2019/06/11 Python
ORM Django 终端打印 SQL 语句实现解析
2019/08/09 Python
使用python远程操作linux过程解析
2019/12/04 Python
使用OpenCV校准鱼眼镜头的方法
2020/11/26 Python
CSS3 毛玻璃效果
2019/08/14 HTML / CSS
Ariat英国官网:为世界顶级马术运动员制造最优质的鞋类和服装
2020/02/14 全球购物
土木工程应届生自荐信
2013/09/24 职场文书
森林防火工作方案
2014/02/14 职场文书
外语系毕业生求职自荐信
2014/04/12 职场文书
消防标语大全
2014/06/07 职场文书
2014年小学生教师节演讲稿范文
2014/09/10 职场文书
2014年安全工作总结范文
2014/11/13 职场文书
2014矛盾纠纷排查调处工作总结
2014/12/09 职场文书
CentOS安装Nginx并部署vue
2022/04/12 Servers
使用JS前端技术实现静态图片局部流动效果
2022/08/05 Javascript