Gatsby 是一個基於 React ,用於搭建靜態站點的開源框架,用於幫助 開發者構建運行速度極快的網站。能夠說它是一個靜態站點生成器,Gatsby 主要的應用的技術是 React 和 GraphQL。javascript
官網:https://www.gatsbyjs.org/css
Gatsby 能快速使用 React 生態系統來生成靜態網站,它具有更高的性能,並且 Gatsby 的生態也很強大。html
當你想本身動手搭建我的博客時,考慮的是 SEO 要好,並且你不用理會數據庫和服務器等複雜的部署設置,Gatsby 構建的網站爲靜態站點,你能夠很輕鬆的將網站部署在不少服務上。Gatsby 不須要等待請求時生成頁面,而是預先生成頁面,所以網站的速度會很快。java
Gatsby 中運用了 React, react-router, Webpack 以及 GraphQL 等新技術,也跟隨了技術潮流。node
上面咱們說到了 GraphQL,沒了解的同窗可能不太清楚。react
GraphQL 是一種用於 API 的查詢語言,是 Restful API 的替代品。
至於替代 Restful API 一說,我的以爲如今 Restful API 佔據絕對的主導地位,當前仍是很難被撼動的。由於目前來看,GraphQL 也有它的缺點。git
看到這裏沒學過 GraphQL 也不用怕,由於用到的很少並且 Gatsby 內置一個界面方便咱們操做。GraphQL 目前國外比較火,它做爲一門新技術,即便不深刻咱們也能夠了解一下。程序員
這裏咱們固然要吹捧它的優勢,上面的定義你可能會疑惑,API 不就是後端寫好的接口嗎,怎麼還能查詢?github
GraphQL 咱們把這個單詞拆開的話是: Graph(圖形或圖表) + QL(Query Language),它是一種圖形化的查詢語言,描述客戶端如何像服務端請求數據的語法規範。聽起來模模糊糊,那它相對 Restful API 又解決了什麼痛點呢?數據庫
相信多數人確定遇到過的場景,好比項目管理系統,進入某頁面展現一個列表,列表上只需展現標題,但你請求返回的數據卻多了其餘信息,好比建立者、建立時間等。這些信息在這裏是多餘的,但它可能有它本身存在的理由,或許是其餘場景須要返回這些信息,所以該接口就直接包含了全部數據。
這一點,GraphQL 能準確獲取你想要的數據,很少很多,想要返回指定的什麼數據,就返回什麼數據。數據由應用來控制,而不是服務器。
GraphQL 查詢不只可以得到資源的屬性,還能沿着資源間引用進一步查詢。典型的 REST API 請求多個資源時得載入多個 URL,而 GraphQL 能夠經過一次請求就獲取你應用所需的全部數據。這樣一來,即便是比較慢的移動網絡鏈接下,使用 GraphQL 的應用也能表現得足夠迅速。
GraphQL API 基於類型和字段的方式進行組織,而非入口端點。你能夠經過一個單一入口端點獲得你全部的數據能力。GraphQL 使用類型來保證應用只請求可能的數據,還提供了清晰的輔助性錯誤信息。應用可使用類型,而避免編寫手動解析代碼。
Gatsby 使用了 GraphQL,做爲在本地管理資源的一種方式。
npm install -g gatsby-cli
gatsby new projectName:根據 starter 建立一個新項目
gatsby new gatsby-blog-demo https://github.com/gatsbyjs/gatsby-starter-hello-world
咱們這裏使用官方最簡單版本的 hello-world 模板進行開發,若是你直接使用
gatsby new gatsby-blog-demo
默認會使用 gatsby-starter-default 來新建項目
cd gatsby-blog-demo gatsby develop
打開 localhost:8000
,就能夠看到輸出了一句Hello world!
這裏介紹 Gatsby 的幾個經常使用命令:
打開http://localhost:8000/__graphql
,就會看到 GraphQL 的調試界面,這裏能夠查看全部的數據、以及調試 query 語句是否返回相應的數據。能夠直接在左側點擊選中,就能夠自動生成 query 語句。
若是運行後你的控制檯出現:
React-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work.
能夠這樣解決:
npm install @hot-loader/react-dom
在根目錄下新建gatsby-node.js
exports.onCreateWebpackConfig = ({ getConfig, stage }) => { const config = getConfig() if (stage.startsWith('develop') && config.resolve) { config.resolve.alias = { ...config.resolve.alias, 'react-dom': '@hot-loader/react-dom' } } }
再從新啓動項目,就能夠消除這個警告
├── .cache ├── public ├── src | └── pages // 該文件夾下的文件會被映射爲路由 | └── index.js ├── static // 靜態資源 ├── .prettierignore ├── .prettierrc ├── gatsby-config.js // 基本配置文件 ├── LICENSE ├── package.json ├── README.md └── yarn.lock
這裏最初始的模板只有一個配置文件,通常項目中都會有 4 個配置文件
基本配置文件。整個 Gatsby 站點的配置文件。能夠在裏面配置網站的相關基本信息。
node 相關配置文件。這個文件只會在 build 期間運行一次,好比動態建立一些頁面。
客戶端相關配置。一些瀏覽器相關的 API 經過在這個文件裏去實現,好比一些監聽路由變化,註冊 serviceWorker 等等。
服務端渲染相關配置文件。使用 Gatsby 的服務端渲染 API 自定義影響服務端渲染的默認設置。
咱們在 pages
目錄下建立一個文件about.js
,則對應的路由路徑爲/about
import React from 'react' const About = () => { return <div>about me</div> } export default About
打開http://localhost:8000/about
,顯示的是咱們剛剛建立的頁面
pages
目錄下的文件名便是路由路徑,但有一個比較特殊,當匹配不到路徑時,Gatsby 會跳轉到 404 頁面,若是咱們在 pages 下建立文件名404.js
,會使用咱們自定義的 404 頁面。當前咱們隨便輸入一個不存在的頁面路徑:http://localhost:8000/test
,頁面就會報錯。
咱們新建一個文件 404.js
import React from 'react' const About = () => { return <div>404 頁面不存在</div> } export default About
再隨便輸入一個路徑,顯示出該頁面:
Gatsby 會確保將 404 頁面構建爲 404.html,默認狀況下會爲咱們建立此頁面,在該頁面也列出了網站上的全部頁面路由連接,咱們能夠經過單擊Preview custom 404 page
看咱們剛纔建立的 404 頁面。
那咱們想直接跳轉到咱們自定義的 404 頁面啊,還要咱們點擊才跳轉。緣由是處於開發環境,即當使用 gatsby develop
時,Gatsby 使用默認的 404 頁面覆蓋咱們自定義的 404 頁面,但其實是已經生效了。
咱們能夠打包,啓動一個本地的服務,模擬處於線上環境來看,這個時候纔是直接顯示咱們自定義的 404 頁面。
gatsby build gatsby serve
而後打開http://localhost:9000/test
(任意路徑),就能跳轉到咱們自定義的 404 頁面了
src/components
。import React from 'react' export default ({ children }) => ( <div style={{ margin: `3rem auto`, maxWidth: 650, padding: `0 1rem` }}>{children}</div> )
src/pages/index.js
中,加入 Layout 組件,import React from 'react' import Layout from '../components/layout' export default function Home() { return <Layout>Hello world!</Layout> }
這樣就讓總體頁面居中了,這裏都是 React 的基本操做
Gatsby 中樣式可使用多種形式,普通的 import './xxx.css
咱們就不說了。
咱們向網站添加全局樣式的最直接的方法之一就是使用全局 .css 樣式表。
在 src 目錄下新建文件夾styles
,添加一個 global.css 文件
html { background-color: #f5f5f5; }
import "./src/styles/global.css"
重啓本地服務,就發現頁面背景變爲淺灰色了。
CSS Module 能夠減小全局污染、防止命名衝突和樣式的覆蓋。
CSS Module 實現原理是:將全部模塊化的類名,修改成成惟一的名稱,即添加一些不重複的前綴或後綴。這樣使得項目中,不一樣人員編寫的樣式不會出現覆蓋和衝突。
src/styles/index.module.css
.title { color: red; } .text { color: blue; }
用法以下:src/pages/index.js
import React from "react" import Layout from "../components/layout" import styles from "../styles/index.module.css" export default function Home() { console.log(styles) return ( <Layout> <h1 className={styles.title}>Hello World</h1> <div className={styles.text}>Test</div> </Layout> ) }
運行結果:
輸出 styles
,能夠看到被導入處理的結果:
若是將其與 CSS 文件進行比較,你會發現每一個格式如今都是導入對象中指向長字符串的鍵(key),例如 title
指向 index-module--title--1i-Wh
。 這些樣式名稱是 CSS 模塊生成的。 保證它們在你的網站上是惟一的。 並且因爲必須導入它們才能使用這些類,因此對於在任何地方使用這些 CSS 樣式都沒問題。
使用 css 樣式就點到爲止了,其餘的好比 Sass 等;還有 CSS in JS 的方式,好比 Emotion, Styled Component。能夠去官網查看相應支持的 Gatsby 插件,就可使用了。
創建網站時,您可能須要重用一些經常使用的數據——好比公用的網站標題、做者信息等,咱們不可能在每一個頁面都加,因此咱們把標題存儲在一個位置,而後從其餘文件引用該位置就好了,更改的時候也只能更改這一處地方。
這些經常使用數據的存放位置就是 gatsby-config.js
文件中的 siteMetadata
對象。打開這個文件,寫入代碼:
module.exports = { /* Your site config here */ siteMetadata: { title: `Title from siteMetadata`, author: 'jacky' }, plugins: [], }
重啓服務
編輯 src/pages/about.js
import React from 'react' import { graphql } from 'gatsby' // GraphQL 獲取的數據,會當作參數傳遞到頁面組件中 // 數據的形式是 { errors, data },沒有錯誤則不會有 errors const About = ({ data }) => { console.log(data.site.siteMetadata.title) return ( <div> <h1>Title: {data.site.siteMetadata.title}</h1> <p>author: {data.site.siteMetadata.author}</p> <div>about me</div> </div> ) } export default About export const query = graphql` query { site { siteMetadata { title author } } } `
而後就拉出數據了
其中,GraphQL 查詢語句是:
{ site { siteMetadata { title, author } } }
你可能會懵逼, site 是哪裏來的,這數據格式怎麼是這樣,這就是 GraphQL 查詢語句了,咱們能夠打開 Gatsby 內置的 GraphQL 調試界面 http://localhost:8000/__graphql
。
這裏的site
即爲咱們gatsby-config.js
寫的站點配置。
依次點擊左邊的site
、siteMetadata
,而後有咱們剛纔寫的title
,author
字段 供咱們選擇,在點擊的同時,中間的框會生成查詢語句,最後點擊運行按鈕,會輸出查詢結果。
當你後面須要獲取更多複雜嵌套數據的時候,能夠直接在這個界面找和點擊,自動生成查詢語句就好了。
由咱們本身寫的查詢語句也知道,上面咱們介紹 GraphQL 的時候說的一個特色,獲取數據很少很多,即咱們能夠選擇想要的數據,好比我就想獲取標題不想獲取做者
export const query = graphql` query { site { siteMetadata { title } } } `
這裏注意,咱們須要區分兩種方式的查詢,上面的例子的查詢方式,只適用於頁面查詢,便是在src/pages
下的路由界面,若是在非頁面組件下進行 GraphQL 查詢,則不能用上面的方式,應該使用 StaticQuery
組件或者 useStaticQuery hook
。
// 頁面查詢 export const query = graphql` query{ ... } `
好比咱們建立一個組件src/components/header.js
import React from 'react' import { graphql, StaticQuery } from 'gatsby' const Header = () => ( <StaticQuery query={graphql` query { site { siteMetadata { author } } } `} render={data => { const { site: { siteMetadata }, } = data return <div>這是Header組件,做者是:{siteMetadata.author}</div> }} /> ) export default Header
或者使用 useStaticQuery 方式
import React from "react" import { useStaticQuery, graphql } from "gatsby" const Header = () => { const data = useStaticQuery( graphql` query { site { siteMetadata { author } } } ` ) return <div>這是Header組件,做者是:{data.site.siteMetadata.author}</div> } export default Header
而後在src/pages/index.js
中引入 Header 組件
import React from 'react' import Layout from '../components/layout' import Header from '../components/header' import styles from '../styles/index.module.css' export default function Home() { return ( <Layout> <Header /> <h1 className={styles.title}>Hello World</h1> <div className={styles.text}>Test</div> </Layout> ) }
運行結果以下:
這個是 Gatsby 的數據源插件,即經過該插件將各方面的數據轉換爲本地可以經過 GraphQL 提取的內容,用於設置項目的文件系統。
npm install --save gatsby-source-filesystem
module.exports = { siteMetadata: { title: `Title from siteMetadata`, author: 'jacky', }, plugins: [ { resolve: `gatsby-source-filesystem`, options: { name: `src`, // 名稱,能夠用來過濾 path: `${__dirname}/src/`, // 文件路徑 }, }, ], }
重啓服務
打開http://localhost:8000/__graphql
,關注allFile
和file
兩個可選項。
咱們點擊 allFile
這個可選項,依次選擇以下圖
這裏咱們要知道,咱們要查詢的數據基本在edges.node
下,節點 node
是圖(graph)中一個對象的特殊稱呼,每一個 File 節點都包含了你要查詢的字段。
咱們選擇的id
,relativePath
,name
字段都對應咱們 src
目錄下建立的文件, 還有不少其餘字段可選擇,由此可知咱們建立的 7 個文件的各類信息。
既然這裏能查詢出來,說明咱們組件裏面也能查出來而後使用數據,好比你能夠從組件中查詢出來作個文件列表。
這個相比傳統的 React 項目就挺厲害了,不用作什麼複雜的工做,就能輕鬆拿下這些數據。Gatsby 比較強大的就是它的生態了,不少插件都配好了,更多插件能夠看官網的插件庫介紹:https://www.gatsbyjs.org/plug...
這個是數據轉換插件,咱們用 Gatsby 作我的博客的話,它可必不可少。如今咱們程序員寫博客基本是用 markdown 語法了,作一個博客的話,不可缺乏的就是對 markdown 語法的解析。
npm install --save gatsby-transformer-remark
module.exports = { /* Your site config here */ siteMetadata: { title: `Title from siteMetadata`, author: 'jacky', }, plugins: [ { resolve: `gatsby-source-filesystem`, options: { name: `src`, // 名稱,能夠用來過濾 path: `${__dirname}/src/`, // 文件路徑 }, }, `gatsby-transformer-remark`, ], }
重啓服務
這個插件添加了 allMarkdownRemark 和 markdownRemark 兩個數據字段
在src
下建立_posts
目錄,添加一個first-blog.md
文件
--- title: "個人第一篇博客" date: "2020-06-21" tags: "React" categories: "React系列" --- ## 這是第一篇博客的標題 first blog 的內容 1
若是博客不是本身搭建的,而是直接上某平臺寫的話,對上面這段語法就可能不太瞭解,在 md 文件開頭的 ---
包裹的是 frontmatter(前言),而後在裏面能夠添加文章的各類關鍵詞信息。
而後咱們就能夠在http://localhost:8000/__graphql
查詢到咱們寫的 md 文件的詳情信息了
咱們稍微再改動,建立多兩個 md 文件
second-blog.md
--- title: "個人第二篇博客" date: "2020-06-22" tags: "Vue" categories: "Vue系列" --- ## 這是第二篇博客的標題 second blog 的內容 2
third-blog.md
--- title: "個人第三篇博客" date: "2020-06-23" tags: "React" categories: "React系列" --- ## 這是第三篇博客的標題 third blog 的內容 3
再次查詢數據,把咱們剛剛添加的文章數據都拿下來了
既然 GraphQL 能拿到數據,說明咱們也能把它放到頁面展現出來。
新建文件src/pages/blog.js
,咱們作一個博客目錄彙總:
import React from 'react' import Layout from '../components/layout' import { graphql } from 'gatsby' const Blog = ({ data }) => { return ( <Layout> <h1>博客目錄</h1> <div> {data.allMarkdownRemark.edges.map(({ node }) => { return ( <div key={node.id} style={{ border: '1px solid #000', margin: '10px', padding: '10px', }} > <h2>{node.frontmatter.title}</h2> <div>分類{node.frontmatter.categories}</div> <div>標籤:{node.frontmatter.tags}</div> <div>發佈時間:{node.frontmatter.date}</div> </div> ) })} </div> </Layout> ) } export default Blog export const query = graphql` query { allMarkdownRemark { edges { node { id frontmatter { tags title date categories } } } } } `
打開http://localhost:8000/blog
,博客目錄就展現出來了:
但有個問題,每篇博客的信息確實能拿出來,可是咱們要連接啊,便是點擊博客標題,進入博客詳情頁面,便是文章路徑,因此接下來咱們將建立頁面。
這個時候咱們就要創建gatsby-node.js
文件了,這個配置文件裏面的代碼是 node 層相關的。
前面咱們說過 src/pages
目錄下的文件會所有渲染成路由,但若是咱們博客文章一篇篇的詳情頁要咱們本身建立一個 js 文件,這確定不合理了。
咱們會用到兩個 API:
每當建立新節點(或更新)時,Gatsby 都會調用 onCreateNode 函數。
向gatsby-node.js
寫入代碼:
exports.onCreateNode = ({ node }) => { console.log(node.internal.type) }
重啓服務,查看終端,你會發現打印出不少建立的節點:SitePage
, SitePlugin
, Site
, SiteBuildMetadata
, Directory
, File
, MarkdownRemark
,咱們關注的只有MarkdownRemark
,因而修改函數使其僅僅記錄 MarkdownRemark 節點;咱們使用每個 md 文件的名稱來做爲路徑,如要獲取文件名稱,你須要遍歷一遍它的父節點 File
,File
節點包含了咱們須要的文件數據。
exports.onCreateNode = ({ node, getNode }) => { if (node.internal.type === `MarkdownRemark`) { const fileNode = getNode(node.parent) console.log(`\n`, fileNode.relativePath) } }
重啓服務,查看終端,打印以下:
_posts/first-blog.md _posts/second-blog.md _posts/third-blog.md
相對路徑出來,接下來咱們要建立路徑,可使用gatsby-source-filesystem
插件帶的建立路徑的函數
const { createFilePath } = require(`gatsby-source-filesystem`) exports.onCreateNode = ({ node, getNode }) => { if (node.internal.type === `MarkdownRemark`) { console.log(createFilePath({ node, getNode, basePath: `pages` })) } }
重啓服務,打印結果以下:
/_posts/first-blog/ /_posts/second-blog/ /_posts/third-blog/
接下來,向 API 傳遞一個函數createNodeField
,該函數容許咱們在其餘插件建立的節點裏建立其餘字段。
exports.onCreateNode = ({ node, getNode, actions }) => { const { createNodeField } = actions if (node.internal.type === `MarkdownRemark`) { const slug = createFilePath({ node, getNode, basePath: `pages` }) createNodeField({ node, name: `slug`, value: slug, // 一般用 slug 一詞表明路徑 }) } }
重啓服務,打開http://localhost:8000/__graphql
,就能查詢到路徑了
createPages 這個 API 用於添加頁面。在建立頁面以前,首先要有頁面模板,這樣建立頁面就能指定所使用的模板了。
新建文件src/templates/blog-post.js
import React from 'react' import Layout from '../components/layout' export default () => { return ( <Layout> <div>Hello blog post</div> </Layout> ) }
而後更改 gatsby-node.js
的createPages
函數:
const path = require(`path`) exports.createPages = async ({ graphql, actions }) => { const result = await graphql(` query { allMarkdownRemark { edges { node { fields { slug } } } } } `) result.data.allMarkdownRemark.edges.forEach(({ node }) => { createPage({ path: node.fields.slug, component: path.resolve(`./src/templates/blog-post.js`), context: { slug: node.fields.slug, }, }) }) }
重啓服務,隨便輸入一個路徑,跳轉到默認的 404 頁面,就會看到自動生成三篇博客的路徑了,點擊任一篇,跳轉的是咱們剛建立的 blog-post.js
組件。
咱們要的是顯示博客內容,因此咱們須要對模板文件再進行改造:
import React from 'react' import { graphql } from 'gatsby' import Layout from '../components/layout' export default ({ data }) => { const post = data.markdownRemark return ( <Layout> <div> <h1>{post.frontmatter.title}</h1> <div dangerouslySetInnerHTML={{ __html: post.html }} /> </div> </Layout> ) } export const query = graphql` query($slug: String!) { markdownRemark(fields: { slug: { eq: $slug } }) { html frontmatter { title } } } `
上面是動態變量查詢的寫法,GraphQL 的語法,能夠看看 (https://graphql.org/learn/que...
完成以後,好比我點擊第二篇博客,就能夠正確進入頁面並顯示內容了
爲了更完善,咱們給 blog 目錄頁面添加可跳轉的連接,加入 slug
查詢字段, 增長 Link
跳轉,這個用法與 React 路由的 Link
差很少一致,涉及不深的狀況下暫且當成相同用法。
src/pages/blog.js
import React from 'react' import Layout from '../components/layout' import { graphql, Link } from 'gatsby' const Blog = ({ data }) => { return ( <Layout> <h1>博客目錄</h1> <div> {data.allMarkdownRemark.edges.map(({ node }) => { return ( <Link to={node.fields.slug} key={node.id}> <div style={{ border: '1px solid #000', margin: '10px', padding: '10px', }} > <h2>{node.frontmatter.title}</h2> <div>分類{node.frontmatter.categories}</div> <div>標籤:{node.frontmatter.tags}</div> <div>發佈時間:{node.frontmatter.date}</div> </div> </Link> ) })} </div> </Layout> ) } export default Blog export const query = graphql` query { allMarkdownRemark { edges { node { id frontmatter { tags title date categories } fields { slug } } } } } `
如今點擊博客,就能跳轉到對應頁面了,雖然沒作樣式頁面比較醜,但只要在src/_posts
目錄新建 md 文件,而後這裏就能跟着自動刷新,算是一個超簡易的博客了。
中止開發服務,構建生產版本,把靜態文件輸出到 public 目錄中
gatsby build
在本地查看生產環境版本,運行:
gatsby serve
接下來,就能夠在localhost:9000
訪問咱們剛纔作的網站了
本項目的 github 地址
至此,咱們教程就介紹到這裏了。