🍣

@use と@forwardの違い、使い分けについて(Sass)

2022/10/14に公開

introduction

googleで「@use @forward 違い」と書くと、@importとの違いとか、基本的なことしか書いてなく、違いについて解説している記事に関してもアフィリエイト感のあふれる記事しかない。信用ならねぇよ…ということで、俺が書こうやないか^^みたいな感じがきっかけ。
まぁ別に公式ドキュメントをみれるやつはわかるし、以下のような優秀なサイトがあるおかげで別に俺が教科書的な説明をする必要はない。俺が書くべきは、「実際の使い分けをどうするか」を丁寧に記述するべきだと思う。それがうまくできるかはわからんけど。いや、しらんけど。

sassに関しては、公式ドキュメントが英語ばっかりなので、今回は以下のサイトを公式ドキュメントとして扱う。
https://www.webdesignleaves.com/pr/css/css_basic_08.html

@useの使い方その1-他のファイルをロードする

@useは、1つ目の使い方として、他のファイルで書いているコードを自分のファイルに持ってくることができる。とりあえず実際にuseを使っているところを見せよう。階層構造が以下のようになっているとする。

sass
├── _code.scss
├── _lists.scss
└── style.scss

そして、それぞれのファイル内は、この様になっている。

_code.scss
code {
  padding: .25em;
  line-height: 0;
}
_lists.scss
ul {
  text-align: left;
  list-style-type: none;
 
  & & {
    padding: {
      bottom: 0;
      left: 0;
    }
  }
}
style.scss
@use 'code';
@use 'lists';

このとき、コンパイルして出力されるファイルは以下の通りだ。

コンパイル後のstyle.css
code {
  padding: 0.25em;
  line-height: 0;
}
 
ul {
  text-align: left;
  list-style-type: none;
}
ul ul {
  padding-bottom: 0;
  padding-left: 0;
}

ほら、他のファイルからコードを引っ張ってコードをコンパイルしている。

@useの使い方その2-メンバーの参照

変数や、mixin、functionのことをまとめてメンバーと呼ぶ。@useを使うときの特殊な仕様なのだが、@useは他のファイルから単純にコードを引っ張ってくるだけじゃなくて、メンバーに対してはカプセル化を行って、そのメンバーが他に影響を与えないようにしている(スコープを作って、他に影響を与えないようにするsassの本質からうまく派生している機能だと思う)。では、引っ張ってきたコードのメンバーを参照するにはどうすればよいのだろうか?それには、namespaceを活用する。さっきメンバーをカプセル化したと思うんだけど、そのカプセル(空間)にそれぞれ名前を与えてやることで、名前のある空間になるだろ?これがnamespace。図書館でも、単純に本が並べられているわけじゃなくて、カテゴリーに分けられているから、そのカテゴリーのところへ行けば自分の求めている本が見つかりやすいだろ?あれと同じで、空間に名前を与えてやることによって、空間を容易に探せるようにする。
実際に名前を当てて、それを検索している例を見せよう。

_variables.scss
$theme-color: #049A36;
_corner.scss
$radius: 3px;
 
@mixin rounded {
  border-radius: $radius;
}
style.scss
@use "corners";
@use "variables";
 
.button {
  @include corners.rounded;   /*名前空間(corners)を指定*/
  padding: 5px + corners.$radius;  /*名前空間(corners)を指定*/
  background-color: variables.$theme-color; /*名前空間(variables)を指定*/
  color: #fff;
}

このとき、コンパイルされるcssはこの様になる。

コンパイル後のstyle.css
.button {
  border-radius: 3px;
  padding: 8px;
  background-color: #049A36;
  color: #fff;
}

これをすることによって、どのファイルからの出力なのかわかりやすくなるだろ。

@useの問題点-2段階でファイルを読み込もうとするとき

どこが問題点なん?

例えば、以下のようにファイル構造がなっているとする。まぁ全て同じディレクトリってことやな。

style.scss
_foo.scss
_variables.scss

んで、variableの機能をstyleで使いたい!ってなった時に困るんだな。以下のようにファイルを作ってみよう。

_variables.scss
$theme-color: #049A36;
_foo.scss
@use "variables";
style.scss
@use 'foo';
 
.foo {
  color: foo.$theme-color; 
}

これ、style.scssの方でエラーがでるんだな。なんでやとおもう?これは、さっき言ったメンバーのカプセル化が悪さをするんだ。まず、fooでuseを用いてvariableの空間を作ってるだろ?んでさらに、styleではfooの空間をつくってる。すると、メンバーに対して、2重で空間を作っていることに気づくだろうか??これ、例えるなら北海道によくある二重ドアみたいな構造になっているわけ。一度開けても、もう一度開けないとメンバーの参照はすることは出来ない。

じゃあ2段階で突撃すればええやん^^

ってかんがえる輩がおるか?俺といっしょやな。つまりさっきの例で行くと、

style.scss
@use 'foo';
 
.foo {
  color: foo.variables.$theme-color; 
}

ってすればええやん^^みたいに考えてみる。これ、実際にやってみるとわかるけど、以下のようにエラーを吐くんやな。

Error: expected "(".
  ╷
4 │   color: foo.variables.$theme-color; 
  │                       ^
  ╵

これをみてみると、二段階で名前空間に潜る構文は用意されていないことがわかる。これなんで用意されてないのか、理由わかる?クソどうでもいいねんけどな。

なんで二段階でメンバーカプセルを潜れないの?

まぁ一つの理由としてあまりにも実用性が低いってことやろな。だって名前空間をつけるだけでもめんどくさいのに、二段階も突っ込んで長文コードつくってみ?めんどいが2倍になるやろ。どんなもんでも2倍にしていいってわけじゃないねん。ドラえもんでも、バイバインをつかってどらやき増やして大変なことになったやん。そういうことや(どういうこと?)。
2つ目には、展開する必要性がないファイルも展開してしまうということ。foo.variableメンバーが必要だなって時はいいけど、foo.variableメンバーが一つも必要じゃないときはわざわざカプセル展開する必要あるか?ないよな。しかもさ、fooの中に、2つのカプセルがあったとして、それぞれに更に2つのカプセルが隠れてて、、、って感じの状況が考えられたとき、一つのコンパイルにつき2^nのカプセルを展開せなあかんのか?めんどくね?たぶんその状況を考えたとき、展開すべきカプセルかどうかがすぐに分かったらいいなってなるやろ?あんねんその機能。それがforwardや。

forwardの使い方-二段階でメンバーの参照

これが必要になってくるのは、基本的に二段階でメンバーのカプセルがつくられてしまうとき。そのとき、さっきも言ったように「展開すべきカプセルですよ〜」って簡単につたえられればいいよな。forwardは、展開すべきカプセルだけにつけて認識させることができる。まぁただ、useのときのようにちょっと構造は違うのでそのへんに関しては後で詳しく扱う。実際に使っているところを見よう。

_variables.scss
$theme-color: #049A36;
_foo.scss
@forward "variables"; 
style.scss
@use 'foo';
 
.hoge {
  color: foo.$theme-color;
}

このとき、コンパイルして出力されるファイルは以下のようになる。

.hoge {
  color: #049A36;
}

このように、メンバーのカプセルを二段階潜って探索することができているように見えるが、構造としては違う。なぜなら、本来variablesカプセルにある変数がfooカプセルに潜るだけで取得できているのだ。

forwardの構造

@useと違うのは、メンバーをカプセル化していないということ。@forwardは、メンバーをカプセル化するのではなく、転送することによってメンバーを使えるようにしている。つまり、メンバーの扱い方が全く異なっているのである。いいかえるのならば、実は二段階のメンバーカプセルを潜っているのではなくて、1段階メンバーカプセルを潜って、それより深いところからメンバーを転送してもらっているのだ。

forwardの注意点

forwardはとても特殊で、転送するという概念に特化しているため、参照することが出来ない。どういうことか。
以下のファイルを見てほしい。

_foo.scss
@forward "variables";

.bar {
  color: variables.$theme-color;  /* エラー There is no module with the namespace "variables".*/
}

foo内で、variablesを利用しようとしてもできない。これは、更に上位のファイルへと転送をする機能にのみ作用をするので、useとは区別して利用される。以下のように。

_foo.scss
@forward "variables";
@use "variables"; /* @use を追加 */
 
.bar {
  color: variables.$theme-color;  /* @use を追加して variables のメンバーにアクセス*/
}

多段階な構造について

例えば、forwardを2回使うことを考えよう。以下のような構造を考える。

_variables.scss
$theme-color: #049A36;
_anpan.scss
@forward "variables"; 
_foo.scss
@forward "anpan"; 
style.scss
@use 'foo';
 
.hoge {
  color: foo.$theme-color;
}

これに関しても、先程と同じような結果になる。これが意味するのは、forwardは何回でも使ってもいい。最下層から最上層までメンバーを転送する必要がある場合に便利であるということだ。

まとめ

@useと@forwardは、他のファイルをロードするという機能においては同じだが、扱い方が全く異なる。1段階潜るならuseを用いて、二段階潜る時は、メンバーのカプセル化を意識して使い分けをするのがいいと思う。@useと@forwardの使い分けに関しては、@import勢は億劫になってしまうが、これを見て自由自在に使ってくれる人が一人でも増えたら嬉しい。もし良かったらいいねを2n-1回(nは自然数)してくれると嬉しい。

Discussion