了解前端理论: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的一句代码实现表格的简单筛选
Jul 26 Javascript
js判断上传文件的类型和大小示例代码
Oct 18 Javascript
jQuery列表拖动排列具体实现
Nov 04 Javascript
JavaScript获取浏览器信息的方法
Nov 20 Javascript
onclick和onblur冲突问题的快速解决方法
Apr 28 Javascript
jQuery File Upload文件上传插件使用详解
Dec 06 Javascript
详解plotly.js 绘图库入门使用教程
Feb 23 Javascript
深入浅析Vue全局组件与局部组件的区别
Jun 15 Javascript
JS+HTML5 canvas绘制验证码示例
Dec 05 Javascript
egg.js的基本使用和调用数据库的方法示例
May 18 Javascript
JavaScript命名空间模式实例详解
Jun 20 Javascript
vue双向绑定数据限制长度的方法
Nov 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
php三维数组去重(示例代码)
2013/11/26 PHP
微信公众平台开发之天气预报功能
2015/08/31 PHP
php实现用户登陆简单实例
2017/04/04 PHP
Linux基于php-fpm模式的lamp搭建phpmyadmin的方法
2018/10/25 PHP
在标题栏显示新消息提示,很多公司项目中用到这个方法
2011/11/04 Javascript
javascript中全局对象的parseInt()方法使用介绍
2013/12/19 Javascript
node.js中的http.get方法使用说明
2014/12/14 Javascript
JavaScript中的anchor()方法使用详解
2015/06/08 Javascript
使用原生的javascript来实现轮播图
2017/02/24 Javascript
使用mint-ui开发项目的一些心得(分享)
2017/09/07 Javascript
jQuery实现html双向绑定功能示例
2017/10/09 jQuery
js中getBoundingClientRect的作用及兼容方案详解
2018/02/01 Javascript
基于JQuery实现页面定时弹出广告
2020/05/08 jQuery
详细分析Node.js 模块系统
2020/06/28 Javascript
JS中循环遍历数组的四种方式总结
2021/01/23 Javascript
[52:05]EG vs OG 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/18 DOTA
python通过floor函数舍弃小数位的方法
2015/03/17 Python
Python检测生僻字的实现方法
2016/10/23 Python
windows7 32、64位下python爬虫框架scrapy环境的搭建方法
2018/11/29 Python
在python中使用with打开多个文件的方法
2019/01/07 Python
Python分布式进程中你会遇到的问题解析
2019/05/28 Python
Windows 安装 Anaconda3+PyCharm的方法步骤
2019/06/13 Python
浅谈Pandas Series 和 Numpy array中的相同点
2019/06/28 Python
捷克厨房用品购物网站:Tescoma
2018/07/13 全球购物
美国领先的个性化礼品商城:Personalization Mall
2019/07/27 全球购物
土耳其新趋势女装购物网站:Addax
2020/01/07 全球购物
财务总监岗位职责
2014/03/07 职场文书
爱与责任演讲稿
2014/05/20 职场文书
化妆品活动策划方案
2014/05/23 职场文书
大学生职业生涯规划大赛作品(精品)
2014/09/17 职场文书
公司新人试用期自我评价
2014/09/17 职场文书
教师听课评语大全
2014/12/31 职场文书
目标责任书格式范文
2015/05/11 职场文书
不会写演讲稿,快来看看这篇文章!
2019/08/06 职场文书
处世之道:关于真诚相待的名言推荐
2019/12/02 职场文书
英国数字版游戏销量周榜公布 《小缇娜的奇幻之地》登顶
2022/04/03 其他游戏