小而合理的前端理論:rscss和rsjs

在前端開發中,咱們會嘗試去定一些規則和約定,來讓項目質量更高,更易於維護。而對於這些規則和約定,咱們也會但願它內容簡單,容易理解。javascript

rscssrsjs是一套比較新,也比較小巧的前端開發規則和約定,其中rs表明Reasonable System,因此能夠理解爲,追求「合理」的css和js。本文除了介紹它們,還會有一點補充以及我本身的見解,也推薦你點擊連接閱讀原做者給出的完整內容。css

從css的疑問開始

rscss但願有效地改善寫css中的這樣幾個常見問題(css哲學三問):html

  • 這個class到底什麼意思?前端

  • 這個class還有地方用到嗎?html5

  • 我新寫的這個class,會有衝突嗎?java

組件原則

rscss首先推崇的是以組件Components)爲基礎的思考方式。在各種前端框架中,幾乎均可以看到組件,如BootstrapMaterializegit

前端框架裏的組件

一個組件是這樣的感受:github

組件

小到一個按鈕,大到整個web應用,可見的視覺元素均可以這樣當作一個組件。web

組件的命名

rscss推薦組件至少使用兩個單詞的命名,中間用短橫線(-)鏈接:bootstrap

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

組件的元素

組件內部的更細小的部分,當作組件的元素Elements)。

組件的元素

元素的命名

爲了和前面的組件區分開來,元素的命名只使用一個單詞

顯然,只有一個單詞是很容易衝突的,所以rscss建議以關係選擇符把元素和組件關聯起來:

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

推薦子選擇符 > 而不是包含選擇符 (空格),以更好地避免衝突:

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

若是確實須要用到多個單詞,直接鏈接它們(不使用短橫線等分隔符),以體現區別:

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

爲每個組件的元素使用class名,不要使用標籤選擇符。有名字的元素會更有語義。

多種屬性或狀態

不管是組件仍是元素,均可以有多種屬性或狀態Variants,也能夠叫變體):

可變的屬性或狀態

屬性或狀態的命名

使用短橫線(-)開頭來命名錶示屬性或狀態的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。

組件嵌套

組件是能夠嵌套的。

嵌套組件

對應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

關於BEM、SMACSS以及前文出現過的OOCSS的介紹,能夠參考之前的這篇文章

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

關注傳統web應用的rsjs

rsjs關注的是非單頁應用(non-SPA web application),也就是咱們一般理解的有不少頁,主要使用jQuery,並且每一個頁均可以有本身的.js文件的傳統網站。

如今已經有了可遵循的JavaScript代碼自己的風格指南,所以,rsjs只對一些其餘的要點提出建議,如命名空間,文件組織方式。

行爲原則

rsjs推薦把由JavaScript實現的交互功能當作一次隻影響一個組件(Component)的行爲(Behavior)。下面是一個參考示例:

<!-- Component -->
<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>標籤的實現方式,但顯然,根據上一條建議,這是應避免的。

<!-- ✗ Avoid -->
<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則已經比較成熟。很推薦試試其中你也認爲合理的建議!

(從新編輯自個人博客,原文地址:http://acgtofe.com/posts/2017...

相關文章
相關標籤/搜索