了解前端理论: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 相关文章推荐
页面使用密码保护代码
Apr 10 Javascript
Javascript中查找不以XX字符结尾的单词示例代码
Oct 15 Javascript
JS按字节截取字符长度实例
Nov 20 Javascript
js随机生成网页背景颜色的方法
Feb 26 Javascript
Javascript基础_标记文字的实现方法
Jun 14 Javascript
js实现StringBuffer的简单实例
Sep 02 Javascript
jQuery基本选择器之标签名选择器
Sep 03 Javascript
Bootstrap基本模板的使用和理解1
Dec 14 Javascript
angularjs使用directive实现分页组件的示例
Feb 07 Javascript
Vue框架中正确引入JS库的方法介绍
Jul 30 Javascript
看看“疫苗查询”小程序有温度的代码
Jul 31 Javascript
JS+canvas五子棋人机对战实现步骤详解
Jun 04 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
第十一节--重载
2006/11/16 PHP
php下安装配置fckeditor编辑器的方法
2011/03/02 PHP
php设计模式 DAO(数据访问对象模式)
2011/06/26 PHP
PHP中数组合并的两种方法及区别介绍
2012/09/14 PHP
ThinkPHP实现转换数据库查询结果数据到对应类型的方法
2017/11/16 PHP
javascript 密码强弱度检测万能插件
2009/02/25 Javascript
js 弹出框只弹一次(二次修改之后的)
2013/11/26 Javascript
js验证输入是否为手机号码或电话号码示例
2013/12/30 Javascript
基于JS实现无缝滚动思路及代码分享
2016/06/07 Javascript
一步一步封装自己的HtmlHelper组件BootstrapHelper(二)
2016/09/14 Javascript
jquery dataTable 后台加载数据并分页实例代码
2017/06/07 jQuery
Vue.js实现微信过渡动画左右切换效果
2017/06/13 Javascript
Js自定义多选框效果的实例代码
2017/07/05 Javascript
基于ionic实现下拉刷新功能
2018/05/10 Javascript
vue.js删除列表中的一行
2018/06/30 Javascript
基于JS实现数字动态变化显示效果附源码
2019/07/18 Javascript
layui实现三级联动效果
2019/07/26 Javascript
用VsCode编辑TypeScript的实现方法
2020/05/07 Javascript
Jquery滑动门/tab切换实现方法完整示例
2020/06/05 jQuery
vue实现自定义多选按钮
2020/07/16 Javascript
[00:33]DOTA2上海特级锦标赛 CDEC战队宣传片
2016/03/04 DOTA
初步介绍Python中的pydoc模块和distutils模块
2015/04/13 Python
django 常用orm操作详解
2017/09/13 Python
解决PyCharm的Python.exe已经停止工作的问题
2018/11/29 Python
Python数据可视化:幂律分布实例详解
2019/12/07 Python
Python Django搭建网站流程图解
2020/06/13 Python
matplotlib基础绘图命令之imshow的使用
2020/08/13 Python
python中Mako库实例用法
2020/12/31 Python
Tarte Cosmetics官网:美国最受欢迎的化妆品公司之一
2017/08/24 全球购物
高中生毕业自我鉴定范文
2013/12/22 职场文书
班级心理活动总结
2014/07/04 职场文书
仓管员岗位职责范本
2015/04/01 职场文书
《花钟》教学反思
2016/02/17 职场文书
两行代码解决Jupyter Notebook中文不能显示的问题
2021/04/24 Python
浅谈Python数学建模之整数规划
2021/06/23 Python
Python selenium绕过webdriver监测执行javascript
2022/04/12 Python