ReactでSPAなポートフォリオを作ってみたので、紹介とざっくり解説です。
備忘録なのでこれからReactを始める際の参考にでもしていただけると幸いです。
ソースコード:GitHub
ポートフォリオ
環境
npx create-react-app --template typescript
で環境構築しています。
バージョンはざっくり以下の通り。
//Node.js//
node -v
v16.9.1
//npm//
npm -v
7.21.1
{
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"typescript": "^4.4.4",
"react-router-dom": "^5.3.0",
"styled-components": "^5.3.3",
"styled-media-query": "^2.1.2",
"styled-reset": "^4.3.4",
"gh-pages": "^3.2.3"
}
Routing
react-router-domで各ページのルーティングをしています。
Pagesコンポーネントで各ページのコンポーネントを設定。
const Pages = () => {
return(
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/about" component={About} />
<Route exact path="/skill" component={Skill} />
<Route exact path="/works" component={Works} />
</Switch>
);
};
export default Pages;
App.tsxで<BrowserRouter>
を設定。
ちなみにHeaderに<Link />
を使用しているのですが、うっかり<BrowserRouter>
の外で使うと怒られるので注意。
const App = () => {
return (
<>
<BrowserRouter basename={process.env.PUBLIC_URL}>
<Header/>
<Pages/>
<Footer/>
</BrowserRouter>
</>
);
};
export default App;
styled-components
今回勉強も兼ねてstyled-componentsを使用しました。
Sassでこう書いていたものが、
const Hoge = () => {
return(
<div className="hoge">
<h1 className="hoge__title">title</h1>
</div>
);
}
.hoge{
background-color: black;
&__title{
font-size: 20px;
}
}
styled-componentsを使うとこう書けます。
import styled from "styled-components";
const Hoge = () => {
return(
<Wrapper>
<Title>title</Title>
</Wrapper>
);
};
const Wrapper = styled.div`
background-color: black;
`;
const Title = styled.h1`
font-size: 20px;
`;
コンポーネント内でスコープが閉じるため命名規則で悩まなくて良くなります。
また、すべてjs(またはts)で書けるため値の共有が容易になります。
今回はシンプルなサイトのためそれほど大きな恩恵は有りませんが、全体的にスタイルの管理がしやすくなるという印象でした。
media query
styled-componentsでmedia queryを設定する方法はいくつかありますが、
今回はstyled-media-queryというパッケージを使用しました。
styled-media-queryを使った書き方はこのようになります。
import styled from "styled-components";
import media from "styled-media-query";
const Hoge = () => {
return(
<Wrapper>
<Title>title</Title>
</Wrapper>
);
};
const Wrapper = styled.div`
background-color: black;
${media.lessThan("medium")`
background-color: white;
`};
`;
const Title = styled.h1`
font-size: 20px;
${media.lessThan("medium")`
font-size: 14px;
`};
`;
syled-componentsのテンプレートリテラル内で${media.メソッド名(ブレイクポイント)`スタイル`;};
のようにすることで実装出来ます。
メソッドにはlessThan
greaterThan
between
があり、ブレイクポイントには事前に定義されている"medium"
等の値か任意の数値を渡します。
それほど多くの機能はないので、GitHubを見ればすぐに使えます。
個人的には、Sassで@mixin
で定義して@include mq(u-sp){}
みたいにするのと同じ感覚で使えるので非常に使いやすかったです。
Reset CSS
styled-componentsでもreset CSSを適用できます。
色々とやり方は有りそうですが、今回はstyled-resetを使用しました。
styled-componentsのcreateGlobalStyle
を利用すれば、独自のリセット用スタイルを追加することも出来ます。
ルート付近に設置すれば全体にスタイルが流れてくれます。
import { createGlobalStyle } from 'styled-components'
import reset from 'styled-reset'
const GlobalStyle = createGlobalStyle`
${reset}
*, *::after, *::before {
box-sizing: border-box;
}
`
const App = () => {
return (
<>
<ResetStyle/>
<BrowserRouter basename={process.env.PUBLIC_URL}>
<Header/>
<Pages/>
<Footer/>
</BrowserRouter>
</>
);
};
Themeの設定
styled-componentsにはReactのContext機能を基にしたThemeProviderがあります。
まずはthemeを設定します。
export const theme = {
color:{
dark: "#222",
gray: "#333",
white: "#fff"
},
font:{
primary: 18px,
scondary: 12px
}
//other themes//
}
themeを使用したいコンポーネントを<ThemeProvider>
でwrapします。(大抵はルート付近)
<ThemeProvider>
のthemeプロパティに先程作成したtheme
を渡すと配下のコンポーネントから参照できます。
import { ThemeProvider } from "styled-components";
import { theme } from './styles/theme';
const App = () => {
return (
<>
<ResetStyle/>
<BrowserRouter basename={process.env.PUBLIC_URL}>
<ThemeProvider theme={theme}>
<Header/>
<Pages/>
<Footer/>
</ThemeProvider>
</BrowserRouter>
</>
);
};
themeを使う側ではこのようになります。
import styled from "styled-components";
const Hoge = () => {
return(
<Wrapper>
<Title>title</Title>
</Wrapper>
);
};
const Wrapper = styled.div`
background-color: ${props => props.theme.color.dark};
`;
const Title = styled.h1`
font-size: ${props => props.theme.font.primary};
`;
<ThemeProvider theme={theme}>
で設定したthemeは配下のコンポーネントに自動的にpropsとして渡されるため、props.theme
で参照できます。
アニメーション
styled-componentsでも@keyframes
が使えます。
import styled, { keyframes } from "styled-components";
const Hoge = () => {
return(
<Wrapper>
<Title>title</Title>
</Wrapper>
);
};
//keyframes定義//
const fadeUp = keyframes`
from{
opacity: 0;
transform: translateY(30px);
}
to{
opacity: 1;
transform: translateY(0);
}
`;
//animationプロパティで適用//
const Title = styled.h1`
animation: ${fadeUp} 0.4s linear;
`;
スタイルの共通化
タイトルやセクション単位でレイアウトを調整するWrapper等、多くのコンポーネントでスタイルが共通する要素をutilityとして切り出しました。
...
export const Title = styled.h1`
color: ${props => props.theme.color.white};
font-size: ${props => props.theme.font.large};
${media.lessThan("medium")`
font-size: ${props => props.theme.font.small};
`}
`;
...
import { Title } from "../styles/utility"
const Hoge = () => {
return(
<Wrapper>
<Title>title</Title>
</Wrapper>
);
};
const Wrapper = styled.div`
background-color: ${props => props.theme.color.dark};
`;
この要領で汎用的なスタイルを切り出していけるのですが、個人的に課題があります。
- ユーティリティの
Title
では無くコンポーネント独自のタイトルを定義したいとなったときや、
Title
を拡張して使いたいとなった場合、ある程度命名規則がないと可読性が下がる。
- そもそもコンポーネント毎に閉じたスコープでスタイルを管理する事が出来るstyled-componentsの良さが薄まるのではないか。
当然開発方針によって運用は変わると思いますが、プロダクトに採用している現場ではどのようにしているか気になりました。
#GitHub Pagesで公開
このポートフォリオはGitHub Pagesを利用して公開しています。
いくつか手順がありますが、今回はgh-pagesを使用しました。
package.jsonにscriptを定義して、
"scripts": {
"build": ...,
"deploy": "gh-pages -d dist"
}
dist
の部分はbundleしたファイルが出力されるディレクトリ名です。build
とかpublic
とか。
そしたら、コマンドを叩くだけでデプロイできます。
//buildして//
npm run build
//deployする//
npm run deploy
仕組み的にはgh-pagesブランチを切るやり方と同じです。
deployコマンドを叩くとGitHubで自動的にgh-pagesのブランチが作られ、distの中身だけがpushされた状態になっています。
まとめ
半分くらいstyled-componentsの話になってしました。
単純なサイトでもReactを使うと効率よく開発することができました。
また、単純なサイトだからこそあまり恩恵がない部分なども見えてきたので良い勉強になりました。