概要
- 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 生活が始まる