你需要知道的Sass插值


你也許會不時地寫寫 Sass 玩玩,你也會很享受它帶給你各種便利。但還有一件事,你並不一定完全了解:插值 (interpolation) - 將一個占位符,替換成一個值。好了,你們都很幸運,因為今天我將把這種問題說清楚。

插值。啥玩意兒?

插值,通常是指變量插值,或者變量替換。這不是Sass獨有的。實際上,你可以在很多編程語言中,發現這種特性。比如 PHP, Perl, Ruby, Tcl, Groovy, Unix shells, 等等。我們經常說的是,插入一個變量,或者插入一個表達式。

我們還是先來看看一個例子吧。如果你有PHP的基礎,接下來應該會很容易了解。比如說,你想打印一個包含變量的字符串,最常見的方式:

  $description = "awesome";
  echo "Tuts+ is " . $description . "!";

這就不是插值的方式,你這是字符串的連接。這其中,連接了三個字符串:"Tuts+ is "awesome(被 $description所引用) 以及 "!"。現在,我們就來看看使用插值而非字符串連接的方式:

  $description = "awesome";
  echo "Tuts+ is ${description}!";

包裹在變量兩邊的花括號會告訴PHP,用字符串打印出變量的值。值得注意的是:它需要在雙引號里面才可以工作(大多語言都是這樣)。

不管怎樣,這就是變量/表達式插值。是使用字符串連接,還是使用插值,這都取決於你自己。但可以說一點:插值其實就是字符串連接的語法糖而已。

Sweet

Sass的插值

我們先看看在Sass中,變量替換是怎么工作。
Sass變量的命名,就像PHP一樣,都有着美元符號的前綴($)。兩者的對比,顯得就結束了,因為談到插值,兩者的表現是不同的。有一個很好的解釋是:Sass是基於Ruby的,它使用 ${}進行表達式替換。

在Sass中,你會這樣做:

  $description: "awesome";
  @warn "Tuts+ is #{$description}!";

Awesome

需要注意的是,變量中的$ 不能像PHP一樣丟掉。變量被#{}包裹了起來。另外值得一提的是:你可以插入任何類型的變量,不僅僅是字符串。
比如:

  $answer: 42;
  @warn "The Answer to the Ultimate Question of Life, the Universe, and Everything is #{$answer}.";

什么時候應該使用插值

現在你應該明白什么是插值了,也知道怎么在Sass中工作的了。是時候,開始着手實際場景了。首先,我們會再次使用剛才做過的@warn指令,它將打印相應內容到控制台。

字符串 (Strings)

假設你有一組叫$colors名的顏色映射(映射是指一個存儲了一系列 key/value 組合的變量),但你已經受夠了一次又一次地敲 map-get($colors, ...)。所以,你寫了一個簡單的color()函數,使用key去獲得相應的值。
把這些組合一下就是:

  // _config.scss
  $colors: (
    "primary": tomato,
    "secondary": hotpink
  );

  // _function.scss
  @function color($key) {
    @return map-get($colors, $key);
  }

  // _component.scss
  .el {
    background-color: color(primary);
  }

一切都很好,不是嗎?現在,當你敲錯名稱,或者去取一個不存在的key時,你想給出錯誤的信息。這將通過 @warn指令來完成。color()函數如下:

  @function color($key) {
    @if not map-has-key($colors, $key) {
      @warn "Key not found.";
    }
    @return map-get($colors, $key);
  }

還不錯。如果你想知道哪個key沒有找到呢?

  @function color($key) {
    @if not map-has-key($colors, $key) {
      @warn "Key `#{$key}` not found.";
    }
    @return map-get($colors, $key);
  }

嘣~~變量插值。調用 color(awesomeness) 時,將會拋出以下信息:

Key awesomeness not found

這真的很棒,但我們卻不知道上下文是什么。為使后來的方便,我們可以將映射的名稱寫到錯誤信息里面。

  @function color($key) {
    @if not map-has-key($colors, $key) {
      @warn "Key `#{$key}` not found in $colors map.";
    }
    @return map-get($colors, $key);
  }

在這個場景里,由於這個 $colors 變量沒有使用插值,它將打印以下信息:

Key awesomeness not found in $colors map.

CSS函數 (Functions)

到目前為止,我們已經見到了最常見的變量替換場景:打印字符串中變量的內容。這確實是一個好的示例,但我覺得應該還有更好的場景:CSS函數中的變量,比如 calc()

假設你想基於側邊欄的寬度設置主容器的大小。你是一個勤奮的前端開發者,已經把這個寬度存儲在一個變量中,所以,你可能會這樣做:

  $sidebar-width: 250px;

  .main {
    width: calc(100% - $sidebar-width);
  }

然后,你會驚訝地發現,根本不work。沒有報錯,容器的大小卻又不正確。如果你去審查你的dom元素,你會看到這個 — 被划掉了。因為,這是不合法的。

  .main {
    width: calc(100% - $sidebar-width);
  }

現在我們應該想到:calc() 是一個CSS函數,不是一個Sass函數。這就是說Sass會將整個表達式解釋成一個字符串。你可以試試:

$type-of-expression: type-of(calc(100% - $sidebar-width)); // string

因為這是一個字符串,難怪Sass表現和之前@warn中的$colors字符串一樣。$sidebar-width被認為是一個常規字符串,所以打出來就是它自己。但這都不是我們所想要的,是吧?我們用插值這樣做。

  .main {
    width: calc(100% - #{$sidebar-width});
  }

現在當Sass編譯這個樣式文件時,它會用#{$sidebar-width}的值,250px替換#{$sidebar-width}。最后,便是合法的CSS表達式。

  .main {
    width: calc(100% - 250px);
  }

任務完成!我們僅僅在這里談了calc(),但其實和其他CSS 原生函數是一樣的,包括偽類。比如:url()linear-gradient()radial-gradient()cubic-bezier()

以下是另一個使用CSS函數的例子:

  @for $i from 1 through $max {
    .el:nth-of-type(#{$i}) {
      // ...
    }
  }

這是一個你有可能遇到過的場景:for循環和:nth-*()選擇器一起使用。再一次說明,你需要使用插值變量,才能最終得到想得到的結果。

小結:Sass會把CSS函數認為是字符串,所以想要在最后獲得它們的值,要求你轉義所有同它們一起使用的變量。

CSS指令 (Directives)

我們將視轉移到另一個有趣的變量插值場景:CSS指令,比如@support@page,最重要的還是@media

現在,Sass是怎樣解析CSS指令,尤其是demia指令的。我已經查看過Sass的源碼,當我Ruby環境有點問題的時候,我找到一些有趣的事情。

  def query_expr
    interp = interpolation
    return interp if interp
    return unless tok(/\(/)
    res = ['(']
    ss
    res << sass_script(:parse)

    if tok(/:/)
      res << ': '
      ss
      res << sass_script(:parse)
    end
    res << tok!(/\)/)
    ss
    res
  end

第一行告訴Sass,如果有一個插值表達式的話,便返回media query。如果找到一個開括號((),便會一直往下走,解析所有的東西,反之就會拋出一個錯誤。我們在這里試試一個例子:

  $value: screen;

  @media $value {
    // ...
  }

毫不驚訝的是,這失敗了:

Invalid CSS after "@media ": expected media query (e.g. print, screen, print and screen), was "$value {"

就像錯誤信息提示的那樣,它期待一個media query。在這里,如果你的變量在 @media 字符串后面,需要使用插值才可以。比如:

  $value: screen;

  @media #{$value} {
    // ...
  }

和我們之前討論的Ruby轉義規則一樣,如果@media后面緊跟(()),你就不再需要插值變量了,因為Sass會求出所有在這些括號里面的值。比如:

  $value: 1336px;

  @media (max-width: $value) {
    // ...
  }

在這個示例中,Sass將這個表達式(max-width: $value)轉化成(max-width: 1337px),最后生成合法的CSS結果。所以,我們便沒有必要再對變量轉義了。

選擇器 (Selectors)

好的,這將是最后一個示例:使用變量作為一個選擇器,或者選擇器的一部分。這不是一種常用的用法,以下的做法不太符合常識:

  $value: custom;

  selector-$value {
    property: value;
  }

不幸的是,這根本無法編譯成功:

Invalid CSS after "selector-": expected "{", was "$value {"

這是media query那個例子的原因,差不多。Sass有着自己的方式解析一個CSS選擇器。如果它遇到了不可預知的東西,比如一個未經處理的美元符號,它就會直接崩潰。

幸運的,解決這個問題也相當簡單(你也已經知道怎么做了):沒錯,使用插值變量!

  $value: custom;

  selector-#{$value} {
    property: value;
  }

結語

在最后,Sass的插值沒有看起來那么簡單。在一些示例之中,你必須轉義你的變量,一些示例中,你卻不必。如果是那樣,你有兩種方式處理:

  • 要么你便等着錯誤信息,然后轉義
  • 要么你轉義除了常規CSS值(你清楚地知道,它會工作得很好)外的所有地方

不管怎樣,我希望你明白了插值變量是如何工作的。如果你有什么需要添加的,請在留言中指出。

原文:All You Ever Need to Know About Sass Interpolation


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM