LoginSignup
4
7

More than 3 years have passed since last update.

React で eject せずに Scoped SASS (.scss) を使う

Last updated at Posted at 2019-12-05

概要

  • scoped sass (ファイル内限定で適用されるスタイル) を使いたいでござる
  • でもnpm run ejectはしたくないでござる
  • cra-sass を導入するとかんたんにできるでござる

参考文献

実行環境

  • create-react-app で作った react project
    • 既存プロジェクトなのでversionわからん すまん
  • TypeScript

サンプルコード (変更前)

node-sass を入れてふつーにscssを使うとこうなる。

Sample.tsx

Sample.tsx
import * as React from "react";
import "./Sample.scss";

export const Sample: React.FC = () => {
  return (
    <div className="outer">
      OUTER
      <div className="inner">INNER</div>
      <ul>
        {["red", "blue", "green"].map((each, index) => (
          <li className={each} key={index}>
            {each.toUpperCase()}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Sample;

Sample.scss

Sample.scss
.outer {
  &,
  * {
    display: flex;
    flex-direction: column;
    padding: 8px;
    border-left: 1px solid gray;
  }

  font-size: 1.2rem;
  .inner {
    font-weight: bold;
  }
  ul {
    li {
      &.red {
        color: red;
      }
      &.green {
        color: green;
      }
      &.blue {
        color: blue;
      }
    }
  }
}

ビルド結果(html)

<div class="outer">
  OUTER
  <div class="inner">INNER</div>
  <ul>
    <li class="red">RED</li>
    <li class="blue">BLUE</li>
    <li class="green">GREEN</li>
  </ul>
</div>

実行結果

この実装の問題点

Sample.scss に記述したスタイルのscopeはグローバルである。
すなわち、Sample.tsx と同時にロードされるコンポーネントに、
同じclassName(例えば.outer)が割りあたっていると、互いに影響を受け合いバグの原因となる

解決策

閉じたscopeを扱うことのできるsass loaderを導入する

導入手順

cra-sass を実行
npx cra-sass

するとなんかいっぱいインストールしてプロジェクトが魔改造される

package.json
@ devDependencies
+    "cra-sass": "0.0.5",

@ dependencies
+    "node-sass-chokidar": "^1.4.0",
+    "npm-add-script": "^1.1.0",
+    "npm-run-all": "^4.1.5",

@ scripts
-    "start": "react-scripts start",
-    "build": "react-scripts --max-old-space-size=2048 build",
+    "start": "npm-run-all -p watch-css start-js",
+    "build": "npm run build-css && react-scripts build",
     "test": "react-scripts test",
-    "eject": "react-scripts eject"
+    "eject": "react-scripts eject",
+    "build-css": "node-sass-chokidar src/ -o src/",
+    "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
+    "start-js": "react-scripts start"

--max-old-space-size=2048 とか無くなってぶっ壊れてんじゃん!
ってことで無駄にぶっこわされたとこは直しておく

package.json
-    "build": "react-scripts --max-old-space-size=2048 build",
+    "build": "npm run build-css && react-scripts --max-old-space-size=2048 build",

サンプルコード(リファクタ後)

Sample.scss

Sample.module.scss に改名する

Sample.tsx

  • scssのimport
  • classNameの割当てのしかた

だけを変更

Sample.tsx
import * as React from "react";
import styles from "./Sample.module.scss";

export const Sample: React.FC = () => {
  return (
    <div className={styles.outer}>
      OUTER
      <div className={styles.inner}>INNER</div>
      <ul>
        {["red", "blue", "green"].map((each, index) => (
          <li className={styles[each]} key={index}>
            {each.toUpperCase()}
          </li>
        ))}
      </ul>
    </div>
  );
};

export default Sample;

ビルド結果

<div class="Sample_outer__144wv">
  OUTER
  <div class="Sample_inner__EiBfI">INNER</div>
  <ul>
    <li class="Sample_red__1ktYQ">RED</li>
    <li class="Sample_blue__32ZOZ">BLUE</li>
    <li class="Sample_green__2OrZU">GREEN</li>
  </ul>
</div>
css部分の抜粋
.Sample_outer__144wv {
  font-size: 1.2rem; }
  .Sample_outer__144wv,
  .Sample_outer__144wv * {
    display: flex;
    flex-direction: column;
    padding: 8px;
    border-left: 1px solid gray; }
  .Sample_outer__144wv .Sample_inner__EiBfI {
    font-weight: bold; }
  .Sample_outer__144wv ul li.Sample_red__1ktYQ {
    color: red; }
  .Sample_outer__144wv ul li.Sample_green__2OrZU {
    color: green; }
  .Sample_outer__144wv ul li.Sample_blue__32ZOZ {
    color: blue; }

その他やったこと

scriptsが壊されてないかチェックしよう

start, build, test が、 cra-sass によって破壊されている恐れがある
特にdefaultから変更している場合注意しよう

.cssが.scssと同階層に出力されるようになってうっおとしい

  • node-sass-chokidar のしわざくさい
  • でもoutputしなくするオプションとかなさげ
  • めんどいから、別階層に吐かせて、ignoreすることにした
package.json
-    "build-css": "node-sass-chokidar src/ -o src/",
-    "watch-css": "npm run build-css && node-sass-chokidar src/ -o src/ --watch --recursive",
+    "build-css": "node-sass-chokidar src/ -o built-css/",
+    "watch-css": "npm run build-css && node-sass-chokidar src/ -o built-css/ --watch --recursive",
.gitignore
+/built-css

node-sass をすでに使っていた場合、不要になる

npm r node-sass

おしまい

これで快適な React x Scoped SASS 生活が始まる

4
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
7