3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ReactでSPAなポートフォリオサイトを作ってみた

Last updated at Posted at 2021-11-05

ReactでSPAなポートフォリオを作ってみたので、紹介とざっくり解説です。
備忘録なのでこれからReactを始める際の参考にでもしていただけると幸いです。
ソースコード:GitHub
ポートフォリオ

環境

npx create-react-app --template typescriptで環境構築しています。
バージョンはざっくり以下の通り。

//Node.js//
node -v
v16.9.1

//npm//
npm -v
7.21.1
package.json
{
  "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コンポーネントで各ページのコンポーネントを設定。

Pages.tsx
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>の外で使うと怒られるので注意。

App.tsx
const App = () => {
  return (
    <>
      <BrowserRouter basename={process.env.PUBLIC_URL}>
          <Header/>
          <Pages/>
          <Footer/>
      </BrowserRouter>
    </>
  );
};

export default App;

styled-components

今回勉強も兼ねてstyled-componentsを使用しました。

Sassでこう書いていたものが、

Hoge.tsx
const Hoge = () => {
  return(
    <div className="hoge">
      <h1 className="hoge__title">title</h1>
    </div>
  );
}
hoge.scss
.hoge{
  background-color: black;

  &__title{
    font-size: 20px;
  }
}

styled-componentsを使うとこう書けます。

Hoge.tsx
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を使った書き方はこのようになります。

Hoge.tsx
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を利用すれば、独自のリセット用スタイルを追加することも出来ます。
ルート付近に設置すれば全体にスタイルが流れてくれます。

App.tsx
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を設定します。

theme.ts
export const theme = {
  color:{
    dark: "#222",
    gray: "#333",
    white: "#fff"
  },
  font:{
    primary: 18px,
    scondary: 12px
  }
//other themes//
}

themeを使用したいコンポーネントを<ThemeProvider>でwrapします。(大抵はルート付近)
<ThemeProvider>のthemeプロパティに先程作成したthemeを渡すと配下のコンポーネントから参照できます。

App.tsx
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を使う側ではこのようになります。

Hoge.tsx
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が使えます。

Hoge.tsx
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として切り出しました。

utility.tsx
...
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};
  `}
`;
...
Hoge.tsx
import { Title } from "../styles/utility"

const Hoge = () => {
  return(
    <Wrapper>
      <Title>title</Title>
    </Wrapper>
  );
};

const Wrapper = styled.div`
  background-color: ${props => props.theme.color.dark};
`;

この要領で汎用的なスタイルを切り出していけるのですが、個人的に課題があります。

  1. ユーティリティのTitleでは無くコンポーネント独自のタイトルを定義したいとなったときや、
    Titleを拡張して使いたいとなった場合、ある程度命名規則がないと可読性が下がる。
  • そもそもコンポーネント毎に閉じたスコープでスタイルを管理する事が出来るstyled-componentsの良さが薄まるのではないか。

当然開発方針によって運用は変わると思いますが、プロダクトに採用している現場ではどのようにしているか気になりました。

#GitHub Pagesで公開
このポートフォリオはGitHub Pagesを利用して公開しています。
いくつか手順がありますが、今回はgh-pagesを使用しました。

package.jsonにscriptを定義して、

package.json
"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を使うと効率よく開発することができました。
また、単純なサイトだからこそあまり恩恵がない部分なども見えてきたので良い勉強になりました。

3
6
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
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?