利用react-bootstrap打造React風格頁面


文章目錄

  1. Gatsby初試啼聲
  2. 利用react-bootstrap打造React風格頁面
  3. 了解Gatsby中GraphQL如何運作
  4. Gatsby GraphQL讀取JSON
  5. 在Gatsby GraphQL中組合出完美資料
  6. Gatsby程序化產生頁面
  7. 上線個人頁面到Netlify+啟動Netlify CMS

在上一篇文章裡面我們已經利用模板將一個簡單的網站架起來了,現在我們來進行首頁的修改。
src/pages/index.js

import React from "react"
import { Link } from "gatsby"

import Layout from "../components/layout"
import SEO from "../components/seo"
import Button from "../components/button"

class IndexPage extends React.Component {
  render() {
    const siteTitle = "Gatsby Starter Personal Website"

    return (
      <Layout location={this.props.location} title={siteTitle}>
        <SEO
          title="Home"
          keywords={[`blog`, `gatsby`, `javascript`, `react`]}
        />
        ...
      </Layout>
    )
  }
}

export default IndexPage

在模板提供的首頁裡,已經有一個Layout的Component,我們可以利用這個已經完成的Component來省下一些工作,對他進行修改就可以重複使用。

導入bootstrap

因為是使用React,所以我在這裡選擇使用react-bootstrap這個套件庫,來更符合React Component的特色,但是要直接使用Bootstrap也是沒有問題的。

首先安裝react-bootstrap

npm install --save react-bootstrap bootstrap

src/pages/index.js中匯入bootstrap css。

import 'bootstrap/dist/css/bootstrap.min.css';

接著在src/components/layout匯入會之後使用到的Component。

import Container from 'react-bootstrap/Container';
import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';

class layout extends React.Component {
  render() {
    ...
  }
}

先把Bootstrap的container做出來,並在裡面加入Navbar

class layout extends React.Component {
  render() {
    const { children, title } = this.props;

    return (
      <Container>
        <Navbar>
          <Navbar.Brand as={Link} to="/">Herbert Lin</Navbar.Brand>
          <Nav className="ml-auto">
            <Nav.Item>
              <Nav.Link as={Link} to="/work">Work</Nav.Link>
            </Nav.Item>
            <Nav.Item>
              <Nav.Link as={Link} to="/blog">Blog</Nav.Link>
            </Nav.Item>
            <Nav.Item>
              <Nav.Link as={Link} to="/about">About</Nav.Link>
            </Nav.Item>
          </Nav>
        </Navbar>
        {children}
      </Container>
    );
  }
}

除此之外,我也沿用了原本Layout裡面有的屬性title以及childrenchildren是為什麼Layout可以被重複利用的原因,只要是在Layout底下傳入的JSX就可以以children的形式插入。
在上一篇文章提到過,網站內部連結可以利用Gatsby Link套件,所以這裡把Link傳到Navbar.BrandNav.Link中,讓他們做使用。

接下來製作footer。因應後續footer想要全版的樣式,所以我把他做到另外一個container裡面。

class layout extends React.Component {
  render() {
    const { children, title } = this.props;

    return (
      <Container>
        ...
      </Container>
      <Container>
        <Row>
          <Col md={8}>
            <h3>Herbert Lin</h3>
            <a href="mailto:herbert.lin.7@gmail.com">herbert.lin.7@gmail.com</a>
          </Col>
          <Col md={4}>
            // 這裡做 social media
          </Col>
        </Row>
      </Container>
    );
  }
}

這裡會發現React報錯,因為render只能return一個根元素,所以這裡使用React.Fragment把他包裝成一個單一元素。

class layout extends React.Component {
  render() {
    const { children, title } = this.props;

    return (
      <React.Fragment>
        <Container>
          ...
        </Container>
        <Container>
          ...
        </Container>
      </React.Fragment>
    );
  }
}

製作social media

在繼續之前,為了製作social media部份,要先裝react-fontawesome,這樣才能夠使用各種social media的圖示。

$ npm i --save @fortawesome/fontawesome-svg-core  @fortawesome/free-brands-svg-icons @fortawesome/react-fontawesome

並且在檔案中匯入font-awesome。

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { fab } from '@fortawesome/free-brands-svg-icons';

class layout extends React.Component {
  render() {
    library.add(fab);
    ...
  }
}

接下來加入一些個人的social media。

class layout extends React.Component {
  render() {
    return (
      <React.Fragment>
        <Container>
          ...
        </Container>
        <Container className="pt-6">
          <Row>
            ...
            <Col md={4} className="d-flex align-items-center justify-content-end">
              <div className="social-media">
                <ul>
                  <li>
                    <a href="https://github.com/XcrossD">
                      <FontAwesomeIcon icon={['fab', 'github']} />
                    </a>
                  </li>
                  <li>
                    <a href="https://www.facebook.com/herbert.lin">
                      <FontAwesomeIcon icon={['fab', 'facebook']} />
                    </a>
                  </li>
                  <li>
                    <a href="https://www.instagram.com/gummypearin/">
                      <FontAwesomeIcon icon={['fab', 'instagram']} />
                    </a>
                  </li>
                  <li>
                    <a href="https://www.linkedin.com/in/herbert-lin-28240446/">
                      <FontAwesomeIcon icon={['fab', 'linkedin']} />
                    </a>
                  </li>
                </ul>
              </div>
            </Col>
          </Row>
        </Container>
      </React.Fragment>
    );
  }
}

做到目前為止的成果如下:

調整樣式

做到這個階段Layout基本的架構有了,但是還需要調整樣式。
在第一篇文章中我說我不喜歡自帶的typographyjs,所以我會把他卸除。有興趣的朋友可以自行前往typographyjs官網研究這個套件是否符合需求。

在這個專案裡面如果可以透過加上className的方式利用bootstrap調整,我會優先採用這種方式,因為只要在加上className屬性即可。其他的部份我會使用styled components來調整樣式,但是一樣如果有偏好的styling可以自行使用。可以參考Gatsby styling文檔設定其他系統。

卸除typographyjs與其週邊套件

npm uninstall --save gatsby-plugin-typography react-typography typeface-merriweather typeface-montserrat typography typography-theme-wordpress-2016

gatsby-config.js

// 刪除以下內容
    {
      resolve: `gatsby-plugin-typography`,
      options: {
        pathToConfigModule: `src/utils/typography`,
      },
    },

gatsby-browser.js

// 刪除以下內容
import "typeface-montserrat"
import "typeface-merriweather"

刪除 src/utils 資料夾(裡面包含typography.js)

// 修改有匯入src/utils/typography的檔案
// 有以下檔案
// 現在先隨便修改,後面會再把這些檔案修改完成
src/components/layout.js
src/components/bio.js
src/pages/blog.js
src/templates/blog-post.js

src/components/bio.js

...
              style={{
                marginRight: `20px`,
                marginBottom: 0,
                minWidth: 50,
                borderRadius: `100%`,
              }}
...

src/pages/blog.js

...
                <h3
                  style={{
                    marginBottom: `10px`,
                  }}
                >
...

src/templates/blog-post.js

...
        <h1>{post.frontmatter.title}</h1>
        <p
          style={{
            display: `block`,
            marginBottom: `40px`,
            marginTop: `-40px`,
          }}
        >
          {post.frontmatter.date}
        </p>
        <MDXRenderer>{post.body}</MDXRenderer>
        <hr
          style={{
            marginBottom: `40px`,
          }}
        />
...

重新執行gatsby develop,更新設定。

加上樣式

使用styled components之前需要匯入
import styled from "styled-components";
但是原本在模板裡已經匯入過所以這只是提醒

src/components/layout.js
先調整Navbar的部份。

class layout extends React.Component {
  render() {
    return (
      <React.Fragment>
        <Container>
          <Navbar className="mb-3 pl-0">
            <Navbar.Brand as={Link} to="/">Herbert Lin</Navbar.Brand>
            <Nav className="ml-auto">
              <Nav.Item>
                <Nav.Link as={Link} to="/work">Work</Nav.Link>
              </Nav.Item>
              <Nav.Item>
                <Nav.Link as={Link} to="/blog">Blog</Nav.Link>
              </Nav.Item>
              <Nav.Item>
                <Nav.Link as={Link} to="/about">About</Nav.Link>
              </Nav.Item>
            </Nav>
          </Navbar>
          {children}
        </Container>
        <Container>
          ...
        </Container>
      </React.Fragment>
    );
  }
}

只有加上了一些bootstrap classNamebootstrap文檔

再來調整footer的部份。

const FooterWrapper = styled.footer`
  background-color: #171717;
  color: #f1f1f1;
  margin-top: 100px;
  padding: 50px 0;
`;

const EmailLink = styled.a`
  text-decoration: none;
  background-color: transparent;
  color: #f1f1f1;
  &:hover, &:visited, &:active {
    color: #f1f1f1;
  }
`;

const SocialMedia = styled.div`
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: flex-end;
  align-content: stretch;
  text-align: right;
`;

const SocialMediaUl = styled.ul`
  list-style: none;
  display: inline-block;
  margin: 0;
  padding: 0;
`;

const SocialMediaLi = styled.li`
  display: inline-block;
  margin: 0 18px 0 18px;
  padding: 0;
`;

const SocialMediaLink = styled.a`
  text-decoration: none;
  background-color: transparent;
  color: #f1f1f1;
  &:hover, &:visited, &:active {
    color: #f1f1f1;
  }
`;

class layout extends React.Component {
  render() {
    return (
      <React.Fragment>
        <Container>
          ...
        </Container>
        <FooterWrapper>
          <Container className="pt-6">
            <Row>
              <Col md={8} className="d-flex flex-column justify-content-center">
                <h3>Herbert Lin</h3>
                <EmailLink href="mailto:herbert.lin.7@gmail.com">herbert.lin.7@gmail.com</EmailLink>
              </Col>
              <Col md={4} className="d-flex align-items-center justify-content-end">
                <SocialMedia className="social-media">
                  <SocialMediaUl>
                    <SocialMediaLi>
                      <SocialMediaLink href="https://github.com/XcrossD">
                        <FontAwesomeIcon icon={['fab', 'github']} size="lg" />
                      </SocialMediaLink>
                    </SocialMediaLi>
                    <SocialMediaLi>
                      <SocialMediaLink href="https://www.facebook.com/herbert.lin">
                        <FontAwesomeIcon icon={['fab', 'facebook']} size="lg" />
                      </SocialMediaLink>
                    </SocialMediaLi>
                    <SocialMediaLi>
                      <SocialMediaLink href="https://www.instagram.com/gummypearin/">
                        <FontAwesomeIcon icon={['fab', 'instagram']} size="lg" />
                      </SocialMediaLink>
                    </SocialMediaLi>
                    <SocialMediaLi>
                      <SocialMediaLink href="https://www.linkedin.com/in/herbert-lin-28240446/">
                        <FontAwesomeIcon icon={['fab', 'linkedin']} size="lg" />
                      </SocialMediaLink>
                    </SocialMediaLi>
                  </SocialMediaUl>
                </SocialMedia>
              </Col>
            </Row>
          </Container>
        </FooterWrapper>
      </React.Fragment>
    );
  }
}
  1. 將背景顏色調為黑色。如果直接對Container進行調整會發現因為bootstrap container有設定padding的關係,所以多加了一個FooterWrapper來達到全版的效果。
  2. 雖然已經使用color: white把文字變為白色了,超連結的顏色還是需要調整,所以製作了EmailLink,讓超連結的顏色永遠保持白色的狀態。
  3. 利用css flexbox調整social media區塊:橫向排列、去除li預設樣式、li之間加上間隔。
  4. 加大social media圖示,傳入size="lg"屬性。
  5. 利用bootstrap classNameContainer加上padding、兩個Col內容置中。

首頁再編輯非Navbarfooter區域,我們就有一個首頁了。
src/pages/index.js

import React from "react";
import { Link } from "gatsby";
import styled from "styled-components";
import Jumbotron from 'react-bootstrap/Jumbotron';
import Button from 'react-bootstrap/Button';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';

import 'bootstrap/dist/css/bootstrap.min.css';

import Layout from "../components/layout";
import SEO from"../components/seo";

const Hero = styled(Jumbotron)`
  &&& {
    background-color: white;
  }  
`;

class IndexPage extends React.Component {
  render() {
    return (
      <Layout>
        <SEO
          title="Home"
          keywords={[`blog`, `herbert lin`, `javascript`, `react`, `python`, `data science`]}
        />
        <Hero className="px-0">
          <h1>Hi! I'm Herbert.</h1>
          <p>
            Previous front end developer.
            <br />
            Currently located in Taiwan, trying to transition into data-related stuff.
          </p>
          <Link to="/about/">
            <Button variant="primary">Learn more about me</Button>
          </Link>
        </Hero>
        <Row>
          <Col>
            <h2 className="mb-4">Recent work</h2>
          </Col>
        </Row>
        <Row>
          // 這裡之後會製作一個Component跟work一起共用
        </Row>
        <Row className="mt-4">
          <Col>
            <Link to="/work/">
              <Button variant="primary">View more</Button>
            </Link>
          </Col>
        </Row>
      </Layout>
    )
  }
}

export default IndexPage

在這裡我沿用了原本模板裡面做好的SEO。如果去觀察src/components/seo.js就會發現那是使用react-helmet製作幫助優化SEO的Component,所以我們就直接沿用了。
實際效果如下:

本篇文章的程式碼的實作可以在github repo2-react-bootstrap-page 找到

參考資源

#React #react-bootstrap #gatsby





現在很多網站都不需要複雜的結構,使用靜態網站呈現就可以了。可以產生靜態網站的框架有很多,這次介紹使用React系列的Gatsby來快速製作一個個人頁面。

留言討論