文章目錄
- Gatsby初試啼聲
- 利用react-bootstrap打造React風格頁面
- 了解Gatsby中GraphQL如何運作
- Gatsby GraphQL讀取JSON
- 在Gatsby GraphQL中組合出完美資料
- Gatsby程序化產生頁面
- 上線個人頁面到Netlify+啟動Netlify CMS
有了對GraphQL初步的認識後,我們可以嘗試來加入其他資料來源讓GraphQL讀取。
在利用react-bootstrap打造React風格頁面裡面我們製作了social media相關的元件,但是不夠好。主要的問題有兩個:
- 擴充性不好。如果還要增加其他聯絡方式,我們只能複製貼上。裡面的資訊也是死的。
- 沒辦法在其他地方使用。我想要在「關於我」的頁面裡面也放入social media讓畫面更加完整。
接下來我會把social media做成一個Component
並串接GraphQL。
把social media做成一個Component
在components內新增一個叫做socialmedia.js的檔案,接著把之前layout.js
裡面的程式碼搬過來。
src/components/layout.js
import SocialMedia from "./socialmedia";
...
<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 />
</Col>
</Row>
</Container>
</FooterWrapper>
...
src/components/socialmedia.js
import React from "react";
import styled from "styled-components";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { fab } from '@fortawesome/free-brands-svg-icons';
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 socialmedia extends React.Component {
render() {
library.add(fab);
return (
<SocialMedia className="social-media">
<SocialMediaUl>
<SocialMediaLi>
<SocialMediaLink href="https://github.com/XcrossD">
<FontAwesomeIcon icon={['fab', 'github']} />
</SocialMediaLink>
</SocialMediaLi>
<SocialMediaLi>
<SocialMediaLink href="https://www.facebook.com/herbert.lin">
<FontAwesomeIcon icon={['fab', 'facebook']} />
</SocialMediaLink>
</SocialMediaLi>
<SocialMediaLi>
<SocialMediaLink href="https://www.instagram.com/gummypearin/">
<FontAwesomeIcon icon={['fab', 'instagram']} />
</SocialMediaLink>
</SocialMediaLi>
<SocialMediaLi>
<SocialMediaLink href="https://www.linkedin.com/in/herbert-lin-28240446/">
<FontAwesomeIcon icon={['fab', 'linkedin']} />
</SocialMediaLink>
</SocialMediaLi>
</SocialMediaUl>
</SocialMedia>
);
}
}
export default socialmedia;
規劃資料格式
了解GraphQL的運作後,可以先規劃我們要的資料格式。需要的資料應該是一個javascript物件,裡面有各個社群媒體的ID,例如:
{
"github": "XcrossD",
"facebook": "herbert.lin",
"instagram": "gummypearin",
"linkedin": "herbert-lin-28240446"
}
另外也需要一個儲存網址前段的地方,這樣我們更改ID就可以了。
{
"github": "https://github.com/",
"facebook": "https://www.facebook.com/",
"instagram": "https://www.instagram.com/",
"linkedin": "https://www.linkedin.com/in/"
}
有這樣的規劃之後我們先反過來看看之前沒有刪掉的src/components/bio.js
。
/**
* Bio component that queries for data
* with Gatsby's StaticQuery component
*
* See: https://www.gatsbyjs.org/docs/static-query/
*/
import React from "react"
import { StaticQuery, graphql } from "gatsby"
import Image from "gatsby-image"
import styled from "styled-components"
import { rhythm } from "../utils/typography"
function Bio() {
return (
<StaticQuery
query={bioQuery}
render={data => {
const { author, social } = data.site.siteMetadata
return (
<Container>
<Image
fixed={data.avatar.childImageSharp.fixed}
alt={author}
style={{
marginRight: rhythm(1 / 2),
marginBottom: 0,
minWidth: 50,
borderRadius: `100%`,
}}
imgStyle={{
borderRadius: `50%`,
}}
/>
<p>
Written by <strong>{author}</strong> who lives and works in San
Francisco building useful things.
{` `}
<a href={`https://twitter.com/${social.twitter}`}>
You should follow him on Twitter
</a>
</p>
</Container>
)
}}
/>
)
}
const bioQuery = graphql`
query BioQuery {
avatar: file(absolutePath: { regex: "/profile-pic.jpg/" }) {
childImageSharp {
fixed(width: 50, height: 50) {
...GatsbyImageSharpFixed
}
}
}
site {
siteMetadata {
author
social {
twitter
}
}
}
}
`
const Container = styled.div`
display: flex;
`
export default Bio
- 這裡串接GraphQL的方式是
StaticQuery
,這是Gatsby在v2中加入的功能。關於page query
與StaticQuery
的差別可以參考官方文檔以及這個討論串。因為我們不需要在query內傳入變數,所以socialmedia
內會使用StaticQuery
。 - 有發現要了一個資料
data.site.siteMetadata.social
裡面有twitter嗎?我們也可以在同樣的位置放其他社群媒體的ID。
把社群媒體的ID加入GraphQL
gatsby-config.js
siteMetadata: {
// edit below
title: `Gatsby Starter Personal Blog`,
author: `Kyle Matthews`,
description: `A starter personal blog with styled components, dark mode, and Netlify CMS.`,
siteUrl: `https://gatsby-starter-blog-demo.netlify.com/`,
social: {
twitter: ``, // 可留可不留,如果是空的GraphQL不會回傳
github: `XcrossD`,
facebook: `herbert.lin`,
instagram: `gummypearin`,
linkedin: `herbert-lin-28240446`
},
},
這樣就解決了一部分的資料了,我們還需要獲得網址前段的資料。首先我們先在src/data
建立socialurl.json
,把上面的JSON放到裡面。
接著還是一樣在gatsby-config.js
裡面,我們需要讓資料夾的位置被讀取,並安裝一個可以讓Gatsby讀取JSON的外掛。
npm install --save gatsby-transformer-json
...
plugins: [
`gatsby-plugin-netlify-cms`,
`gatsby-plugin-styled-components`,
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
`gatsby-plugin-offline`,
`gatsby-plugin-react-helmet`,
`gatsby-plugin-feed-mdx`,
// 新增新的外掛
`gatsby-transformer-json`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `data`,
path: `${__dirname}/content/data`
}
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/blog`,
name: `blog`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content/assets`,
name: `assets`,
},
},
更改好後重新執行gatsby develop
更新。
這時候我們在GraphQL互動工具()會發現多了allDataJson
與dataJson
欄位,這是我們新增的gatsby-transforer-json
外掛所產生的。
執行以下query後,就可以得到我們在JSON裡面放的資料了。
query MyQuery {
dataJson {
facebook
github
instagram
linkedin
}
}
// 回傳資料
{
"data": {
"dataJson": {
"facebook": "https://www.facebook.com/",
"github": "https://github.com/",
"instagram": "https://www.instagram.com/",
"linkedin": "https://www.linkedin.com/in/"
}
}
}
串接GraphQL
參考src/components/bio.js
檔案裡面的StaticQuery
部份內容,我們可以對socialmedia.js
進行修改。
src/components/socialmedia.js
import { StaticQuery, graphql } from "gatsby";
class socialmedia extends React.Component {
render() {
library.add(fab);
return (
<StaticQuery
query={socialMediaQuery}
render={data => {
}}
/>
);
}
}
const socialMediaQuery = graphql`
query SocialMediaQuery {
site {
siteMetadata {
social {
facebook
github
instagram
linkedin
}
}
}
socialUrl: dataJson {
facebook
github
instagram
linkedin
}
}
`
有了資料之後,為了動態產生圖示我在這裡使用Array.map(),根據不同的社群媒體置入相對應的資訊。
<StaticQuery
query={socialMediaQuery}
render={data => {
const { social } = data.site.siteMetadata;
const { socialUrl } = data;
return (
<SocialMedia className="social-media">
<SocialMediaUl>
{Object.keys(social).map(site => (
<SocialMediaLi key={`social-media-${site}`}>
<SocialMediaLink href={`${socialUrl[site]}${social[site]}`}>
<FontAwesomeIcon icon={['fab', site]} size="lg" />
</SocialMediaLink>
</SocialMediaLi>
))}
</SocialMediaUl>
</SocialMedia>
);
}}
/>
如此一來,social media就變成一個Component
,而且有擴充性了。
製作介紹頁
簡單的製作一下一個更詳細的介紹頁。新增一個檔案about.js
於src/pages
。
src/pages/about.js
import React from "react";
import styled from "styled-components";
import Button from 'react-bootstrap/Button';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Layout from "../components/layout";
import SEO from "../components/seo";
import SocialMedia from "../components/socialmedia";
const ActionTitle = styled.h4`
margin-top: 18px;
padding-bottom: 9px;
`;
class AboutPage extends React.Component {
render() {
return (
<Layout>
<SEO
title="About"
keywords={[`blog`, `Herbert Lin`, `javascript`, `react`, `gatsby`]}
/>
<Row>
<Col className="profile">
<h1>I'm Herbert Lin, a programmer who likes to dabble in different stuff.</h1>
<ActionTitle>Email me</ActionTitle>
<a href="mailto:herbert.lin.7@gmail.com">herbert.lin.7@gmail.com</a>
<ActionTitle>Follow me</ActionTitle>
<SocialMedia />
</Col>
<Col>
<p>Previously a front end developer at <a href="https://celinius.com.tw">Celinius</a>, I'm now looking to transition to a field related to data, as I believe that data is the furture.</p>
<p>I'm also working at non-profit organization, <a href="https://www.xchange.com.tw">XChange</a>, whose purpose is to expand the influence of Taiwanese talents around the globe.</p>
<p>Outside of being a programmer, I'm an avid language learner, an anime lover, and a gamer.</p>
<a target="_blank" rel="noopener noreferrer" href="https://www.dropbox.com/s/likvy8592uf3orm/herbert_lin_resume.pdf?dl=0">
<Button variant="secondary">Resume</Button>
</a>
</Col>
</Row>
</Layout>
)
}
}
export default AboutPage;
除了介紹篇幅短了點,其他沒什麼大問題。但是我們發現因為SocialMedia
之前是做在黑色背景的,所以圖示是白色的。在這裡我們可以考慮使用styled components
的方式調整,但是既然做成一個Component
了我們乾脆讓他可以透過屬性辨別現在的背景顏色。
src/components/socialmedia.js
const SocialMediaLinkWhite = styled.a`
text-decoration: none;
background-color: transparent;
color: #f1f1f1;
&:hover, &:visited, &:active {
color: #f1f1f1;
}
`;
const SocialMediaLink = styled.a`
text-decoration: none;
background-color: transparent;
color: #171717;
&:hover, &:visited, &:active {
color: #171717;
}
`;
class socialmedia extends React.Component {
render() {
const { className } = this.props;
const darkBackground = this.props.darkBackground | false;
library.add(fab);
return (
<StaticQuery
query={socialMediaQuery}
render={data => {
const { social } = data.site.siteMetadata;
const { socialUrl } = data;
return (
<SocialMedia className={`social-media ${className}`}>
<SocialMediaUl>
{Object.keys(social).map(site => (
<SocialMediaLi key={`social-media-${site}`}>
{darkBackground ? (
<SocialMediaLinkWhite href={`${socialUrl[site]}${social[site]}`}>
<FontAwesomeIcon icon={['fab', site]} size="lg" />
</SocialMediaLinkWhite>
) : (
<SocialMediaLink href={`${socialUrl[site]}${social[site]}`}>
<FontAwesomeIcon icon={['fab', site]} size="lg" />
</SocialMediaLink>
)}
</SocialMediaLi>
))}
</SocialMediaUl>
</SocialMedia>
);
}}
/>
);
}
}
除了darkBackground
屬性外,為了在外頭加上bootstrap class
也把className
屬性傳入。
因為在這裡使用了javascript OR所以反而要去需要黑色背景的地方傳入屬性。
src/components/layout.js
...
<SocialMedia darkBackground={true} />
...
最後再調整一下SocialMedia
與email之間的距離。
src/pages/about.js
...
<SocialMedia className="mt-3" />
...
實際效果如下:
本篇文章的程式碼的實作可以在github repo中 4-reading-json-in-graphql 找到