了解前端理论: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 28 Javascript
防止文件缓存的js代码
Jan 10 Javascript
jQuery contains过滤器实现精确匹配使用方法
Apr 12 Javascript
javascript中比较字符串是否相等的方法
Jul 23 Javascript
让复选框只能选择一项的方法
Oct 08 Javascript
JavaScript String(字符串)对象的简单实例(推荐)
Aug 31 Javascript
一次记住JavaScript的6个正则表达式方法
Feb 22 Javascript
vue+iview 兼容IE11浏览器的实现方法
Jan 07 Javascript
Node.js中Koa2在控制台输出请求日志的方法示例
May 02 Javascript
Vuex的实战使用详解
Oct 31 Javascript
JavaScript实现栈结构Stack过程详解
Mar 07 Javascript
JavaScript与JQuery框架基础入门教程
Jul 15 Javascript
微信小程序使用字体图标的方法
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调用mysql存储过程
2007/02/14 PHP
codeigniter教程之多文件上传使用示例
2014/02/11 PHP
详解PHP安装mysql.so扩展的方法
2016/12/31 PHP
ThinkPHP Where 条件中常用表达式示例(详解)
2017/03/31 PHP
微信公众平台开发教程②微信端分享功能图文详解
2019/04/10 PHP
纯CSS3实现质感细腻丝滑按钮
2021/03/09 HTML / CSS
jquery 打开窗口返回值实现代码
2010/03/04 Javascript
Jquery公告滚动+AJAX后台得到数据
2011/04/14 Javascript
js下获得客户端操作系统的函数代码(1:vista,2:windows7,3:2000,4:xp,5:2003,6:2008)
2011/10/31 Javascript
ASP.NET jQuery 实例2 (表单中使用回车在TextBox之间向下移动)
2012/01/13 Javascript
jquery实现表格奇数偶数行不同样式(有图为证及实现代码)
2013/01/23 Javascript
微信小程序开发(二)图片上传+服务端接收详解
2017/01/11 Javascript
AngularJS框架中的双向数据绑定机制详解【减少需要重复的开发代码量】
2017/01/19 Javascript
微信小程序 登录的简单实现
2017/04/19 Javascript
JavaScript设置名字输入不合法的实现方法
2017/05/23 Javascript
微信小程序使用checkbox显示多项选择框功能【附源码下载】
2017/12/11 Javascript
简化版的vue-router实现思路详解
2018/10/19 Javascript
详解JavaScript中typeof与instanceof用法
2018/10/24 Javascript
京东优选小程序的实现代码示例
2020/02/25 Javascript
[02:39]我与DAC之Newbee.Moogy:从论坛到TI
2018/03/26 DOTA
Python使用django搭建web开发环境
2017/06/09 Python
利用Python实现微信找房机器人实例教程
2019/03/10 Python
Python进程间通信 multiProcessing Queue队列实现详解
2019/09/23 Python
python实现提取COCO,VOC数据集中特定的类
2020/03/10 Python
keras分类模型中的输入数据与标签的维度实例
2020/07/03 Python
使用before和:after伪类制作css3圆形按钮
2014/04/08 HTML / CSS
如何使用html5与css3完成google涂鸦动画
2012/12/16 HTML / CSS
Java Servlet的主要功能和作用是什么
2014/02/14 面试题
个人培训自我鉴定
2014/03/28 职场文书
校庆活动方案
2014/03/31 职场文书
中文专业求职信
2014/06/20 职场文书
房地产销售员岗位职责
2015/04/11 职场文书
周恩来的四个昼夜观后感
2015/06/03 职场文书
html5实现点击弹出图片功能
2021/07/16 HTML / CSS
python程序的组织结构详解
2021/12/06 Python
VMware虚拟机安装 Windows Server 2022的详细图文教程
2022/09/23 Servers