為 WordPress 驅動的網站構建 Gatsby 主題
已發表: 2022-03-10Gatsby 是一個建立在 React 之上的開源框架。 使用 Gatsby,您可以從(幾乎)任何地方提取數據並使用它來生成靜態或動態網站。 數據可以從 CMS 中提取,這無疑將 WordPress 帶到了桌面上。 在繼續通過 WordPress 儀表板管理內容的同時,您可以獲得靜態網站的優勢(速度、安全性、靜態託管)。
Gatsby 框架的特點之一是它提出了主題作為定制工具。 作為擁有 WordPress 深厚背景的人,我發現 Gatsby 主題的概念特別吸引人。 我曾經設計和開發 WordPress 主題。 然而,隨著對 Jamstack 解決方案的興趣日益濃厚,我逐漸轉向使用 WordPress 作為無頭 CMS。 在本文中,我想分享一些我從這次過渡中學到的概念。
注意:在我們繼續之前,讓我們關注我們將要使用的工具。 Gatsby 提供了一個官方的 gatsby-source-wordpress 插件。 為了使它工作,我們需要準備我們的 WordPress 端。 更準確地說,我們需要通過 GraphQL API 公開 Gatsby 風格的 WordPress 數據。 實際上,這意味著安裝兩個 WordPress 插件 WPGraphQL 和 WPGatsby。 兩者都可以通過官方 WordPress 插件存儲庫獲得,不需要任何配置。
什麼是蓋茨比主題?
Gatsby 主題是一組在 Node.js 包中抽像出來的共享功能。 因此,主題註定要發布(到像 npm 這樣的註冊表)並作為可安裝的依賴項重用。
既然我們在這裡談論 Gatsby 和WordPress ,我將立即澄清它 - 與 WordPress 主題有相似之處,但我們不應該將 WordPress 主題的概念等同於 Gatsby 主題。 對於有 WordPress 背景的人(比如我自己)來說,這種分離可能在一開始就具有挑戰性。
WordPress 主題是一個強制性的模板系統,它定義了我們在前端看到的內容。 一個好的WordPress 主題的責任到此結束。 它不應該引入任何功能,因為功能是插件的領域。 因此,WordPress 生態系統中的主題和插件之間存在嚴格的分離。 主題應該負責表示層,插件負責功能方面。
按照蓋茨比的定義,主題負責功能。 那我們不應該稱它們為插件嗎? 實際上,Gatsby 和 WordPress 一樣,既有插件又有主題。 插件,就像主題一樣,是實現 Gatsby API 的可安裝 Node.js 包。 事實上,一個 Gatsby 主題就是一個 Gatsby 插件。 如果插件擁有網站上的一個部分、一個頁面或頁面的一部分——我們稱之為主題。
此外,與 WordPress 不同,Gatsby 不需要使用主題來構建網站。 相反,您可能會通過設置如下結構的項目來開始創建站點:
這沒關係,除非您有多個站點需要維護。 在這種情況下,您可能希望抽象流程的公共部分並分別管理(版本和更新)它們。
感謝 Gatsby 主題系統,您可以將共享部分捆綁到一個包(或多個包)中,發布包並最終將它們安裝到眾多應用程序中。 請注意,我使用了複數形式的包——您可以在一個項目中組合多個主題。
兒童主題和陰影
在使用 Gatsby 和 WordPress 時,您將確定所有項目共有的一些核心功能。 我的意思是:獲取數據並動態構建頁面。 擁有一個處理數據源邏輯和頁面創建的主題似乎是值得的。 另一方面,您決定顯示頁面的方式可能會因項目而異。 無論您在核心級別設置什麼,您都可能需要在某些時候覆蓋。
一種可能的方法是擁有一個核心(父)主題並在核心主題之上構建子主題。
蓋茨比兒童主題是什麼意思?
讓我們繼續比較 WordPress 子主題。 WordPress 子主題允許我們添加功能和覆蓋模板。 它們提供了一種增強和修改現有主題的安全方法。
Gatsby 子主題使用父主題作為其插件。 然後我們可以使用陰影的概念,使子主題能夠覆蓋父主題文件; 這類似於在子主題中覆蓋 WordPress 模板。 影子意味著我們可以覆蓋 webpack 包中包含的src
目錄中的文件。 值得強調的是,在項目級別(我們將主題作為包使用)是可能的。 我們將在本文後面看到它的實際應用。
使用 WordPress,我們僅限於一個父主題,一個子主題,並且無法進行進一步的鏈接。 憑藉 Gatsby 主題的靈活性,我們可以走得更遠。 可以構建不同配置的子父鏈。
現在讓我們看看 Gatsby 主題的實際應用。 在我們的示例中,我們將構建兩個主題, gatsby-theme-wp-parent
及其子主題gatsby-theme-wp-child
。 為了簡單起見,我選擇了這個設置。 在現實世界的場景中,您可能希望將您的功能分解為更多主題,每個主題都有一些特定的職責。
我們將發布我們的主題,將它們安裝在項目中,並通過項目級陰影添加進一步的自定義。
開發設置
最後一張圖描繪了最終用戶的項目(站點)的結構,其中主題被使用。 它們作為項目的依賴項安裝。 此設置假定主題可通過某個 npm 存儲庫獲得,這意味著我們已經發布了它們。 我們還沒有。 我們需要先構建父子主題。 但是開發設置是什麼樣的? 我們的主題是兩個獨立的包,但我們需要在開發過程中在單個項目中並行處理它們。 此外,我們想在同一個項目中設置一個直接實現主題的演示。
可能的解決方案之一是紗線工作區。 使用 yarn 工作空間,我們在一個單一的單一存儲庫中工作,在項目根級別使用一個鎖定文件。 此外,依賴關係可以鏈接在一起,這意味著工作空間相互依賴,我們在開發過程中使用本地版本。
如何設置紗線工作區? 首先,確保全局安裝了 yarn。 接下來,在 monorepo 的根目錄中,添加指定工作空間的package.json
文件:
{ "private": true, "workspaces": [ "packages/*", "demo" ] }
現在,每個主題都是packages
中的一個子文件夾,具有自己的package.json
文件和一個空的主條目index.js
。 我添加的每個主題都是這樣進行的:
mkdir packages/gatsby-theme-wp-parent touch packages/gatsby-theme-wp-parent/package.json packages/gatsby-theme-wp-parent/index.js
使用package.json
如下:
{ "name": "@pehaa/gatsby-theme-wp-parent", "version": "1.0.0", "license": "MIT", "main": "index.js" }
我們將進一步討論主題發布。 但是,目前,讓我們注意我們將發布我們的主題作為範圍包; 我在這裡使用我的暱稱@pehaa
作為範圍。 請記住,如果您決定將作用域包發佈到公共 npm 註冊表 https://registry.npmjs.org,則必須明確聲明公共訪問權限並將以下內容添加到其package.json
文件中:
"publishConfig": { "access": "public" }
除了主題之外,我們還需要一個demo
工作區,我們將從中試用我們的代碼。 該演示必須是一個"private"
包,因為它不應該被發布。
// demo/package.json { "private": true, "name": "demo", "version": "1.0.0", "scripts": { "build": "gatsby build", "develop": "gatsby develop", "clean": "gatsby clean" } }
通過設置工作區,我們可以通過指定腳本和工作區,從我們的 monorepo 中的任何位置運行開發或構建腳本,如下所示:
yarn workspace demo develop
順便說一句,您不僅限於單個demo
。 例如,我們的GatsbyWPThemes
monorepo 包含多個我們添加到examples
目錄的演示。 在這種情況下,根級package.json
文件定義工作空間如下:
"workspaces": [ "packages/*", "examples/*" ]
構建蓋茨比主題
首先,我們需要安裝react
、 react-dom
和gatsby
。 我們需要將這三個安裝為每個主題中的對等依賴項 ( -P
) 和我們的演示中的依賴項。 我們還將父主題安裝為子主題的依賴項,將子主題安裝為演示的依賴項。
yarn workspace @pehaa/gatsby-theme-wp-parent add -P react react-dom gatsby yarn workspace @pehaa/gatsby-theme-wp-child add -P react react-dom gatsby yarn workspace @pehaa/gatsby-theme-wp-child add "@pehaa/gatsby-theme-wp-parent@*" yarn workspace demo add react react-dom gatsby "@pehaa/gatsby-theme-wp-child@*"
注意:您不能在沒有版本號的情況下添加@pehaa/gatsby-theme-wp-parent
或@pehaa/gatsby-theme-wp-child
。 您必須將其指定為@*
或@1.0.0
。 沒有它,npm 將嘗試從存儲庫中獲取包,而不是使用本地包。 稍後,當我們使用 Lerna 發布我們的包時,所有*
將自動更新到當前主題版本並保持同步。
家長主題
現在讓我們關注父主題及其依賴項:
yarn workspace @pehaa/gatsby-theme-wp-parent add gatsby-source-wordpress gatsby-plugin-image gatsby-plugin-sharp gatsby-transformer-sharp gatsby-awesome-pagination
我們父主題的職責是加載處理和顯示圖像所需的源插件和三個插件。 我們將它們全部加載到gatsby-config.js
文件中。
// gatsby-config.js module.exports = (options) => { return { plugins: [ 'gatsby-plugin-sharp', // must have for gatsby 'gatsby-transformer-sharp', // must have for gatsby images 'gatsby-plugin-image', { resolve: 'gatsby-source-wordpress', options: { url: `${options.wordPressUrl}/graphql`, }, }, ], } }
除了採購內容外,我們還需要為我們的 WordPress 內容動態構建路由。 我們需要為 WordPress 靜態頁面、單個帖子、博客存檔、類別存檔和標籤存檔創建路由。 Gatsby 提供createPages
API 作為 Gatsby Node API 的一部分。 讓我們看一下負責創建單個帖子的代碼。
exports.createPages = async ({ graphql, actions }) => { const { createPage } = actions const postsQuery = await graphql(` query GET_POSTS { allWpPost(sort: {order: DESC, fields: date}) { edges { node { uri id } } } } `) const posts = postsQuery.data.allWpPost.edges posts.forEach(({ node }) => { createPage({ path: node.uri, component: path.resolve('../src/templates/post-query.js'), context: { // Data passed to context is available in page queries as GraphQL variables // we need to add the post id here // so our blog post template knows which blog post it should display id: node.id }, }) }) }
您可以在此 GitHub 存儲庫中找到完整的代碼。 您可能會注意到它因頁麵類型而異。 文章、頁面或存檔是不同的,尤其是為後者實現的分頁。 儘管如此,它仍然遵循相同的模式:
- 運行異步
graphql
“獲取項目”查詢; - 循環生成的項目並為每個項目運行
createPage
輔助函數,傳遞:- 路徑,
-
component
——模板文件; 蓋茨比必須知道每一頁應該顯示什麼, -
context
— 模板(在component
字段中提供)可能需要的任何數據。
由於我們不想擔心父主題中的 UI 部分——我們將其委託給我們將在子主題中隱藏的組件。
// src/templates/post-query.js import { graphql } from "gatsby" import Post from "../components/Post" export default Post export const pageQuery = graphql` query ($id: String!) { wpPost(id: { eq: $id }) { # query all usefull data } } `
Post
組件可以訪問模板文件中定義的graphql
頁面查詢中的數據。 我們的組件通過 props 作為props.data
接收查詢結果。 我們的組件文件與模板分離,但可以訪問其數據。 使用此設置,我們可以隱藏Post
組件,而無需重寫查詢。
// src/components/Post.js import React from 'react' const Post = (props) => { return <pre>{JSON.stringify(props.data, null, 2)}</pre> } export default Post
兒童主題
現在,讓我們轉到子主題並添加它的依賴項。
注意:我選擇使用 Chakra UI 作為組件庫,它基於情感並帶有自己的 Gatsby 插件。 我們還需要從@wordpress/block-library
安裝特定於 WordPress 內容的樣式。
yarn workspace @pehaa/gatsby-theme-wp-child add @chakra-ui/gatsby-plugin @chakra-ui/react @emotion/react @emotion/styled @wordpress/block-library framer-motion gatsby-plugin-webfonts html-react-parser
子主題的職責是 UI 部分,我們需要覆蓋父主題生成的裸骨輸出。 為了使陰影起作用,我們需要遵循父主題的文件結構。 例如,要覆蓋gatsby-theme-wp-parent/src/components/Post.js
中的Post
組件,我們需要在gatsby-theme-wp-child/src/@pehaa/gatsby-theme-wp-parent/components
中創建一個Post.js
文件gatsby-theme-wp-child/src/@pehaa/gatsby-theme-wp-parent/components
。 @pehaa
中間文件夾對應於gatsby-theme-wp-parent
包的範圍。
將選項傳遞給主題
我們在gatsby-config.js
文件中加載和配置 gatsby 插件。 我們將在設置中包含三個配置文件,每個級別一個,父主題、子主題和演示。
├── demo │ └── gatsby-config.js ├── packages │ ├── gatsby-theme-wp-child │ │ └── gatsby-config.js │ └── gatsby-theme-wp-parent │ └── gatsby-config.js └── ...
在演示級別,配置加載子主題,如下所示:
// demo/gatsby-config.js module.exports = { plugins: [ { resolve: '@pehaa/gatsby-theme-wp-child', options: { wordPressUrl: process.env.GATSBY_WP_URL, /* other options */ }, }, ], }
正如您在上面看到的,我們將選項傳遞給子主題。 這些將在子主題級別的配置文件中可用。 這是可能的,因為 Gatsby 插件已將配置導出為函數。 因此,當我們加載提供一些選項的插件時,插件會將它們作為其配置函數的參數接收。 特別是,我們傳遞給主題的選項可以像這樣“轉發”到它的父級主題:
// gatsby-theme-wp-child/gatsby-config.js const defaultFonts = ... module.exports = (options) => { // destructure option to extract fonts const {fonts, ...rest} = options return { plugins: [ { resolve: `@pehaa/gatsby-theme-wp-parent`, options: { // "forward" the options gatsby-theme-wp-child options to its parent theme ...rest } }, '@chakra-ui/gatsby-plugin', { resolve: `gatsby-plugin-webfonts`, options: { fonts: fonts || defaultFonts }, }, ], } }
讓我們再看看上面的代碼。 請注意,我們在子主題級別定義字體,但我們保留通過主題選項修改它們的可能性。
// demo/gatsby-config.js module.exports = { plugins: [ { resolve: `@pehaa/gatsby-theme-wp-child`, options: { wordPressUrl: process.env.GATSBY_WP_URL, fonts: { google: [{family: "Rubik"}], }, }, }, ], }
在配置我們的主題時,我們應該記住主題只是一個包,最終用戶不會直接訪問它的代碼。 因此,提前考慮並公開適當的設置是一個好主意。 如果我們的主題加載需要配置的插件,我們可能應該從項目(演示)級別一直向下傳遞插件選項。
讓我們看一個例子。 我們的父主題使用gatsby-source-wordpress
插件從 WordPress 中獲取數據。 這個插件帶有一堆選項,其中一些可能對構建過程至關重要,例如schema.requestConcurrency
或schema.timeout
。 但是,同樣,父主題只是一個包,最終用戶無法編輯其gatsby-config
文件。 這似乎很明顯,但我們在 Gatsby WP Themes 的初始版本中不知何故錯過了它。 但是,通過快速修復,用戶可以從項目的配置中傳遞gatsby-plugin-source-wordpress
選項...
// user's project gatsby-config.js module.exports = { plugins: [ { resolve: `@pehaa/gatsby-theme-wp-child`, options: { wordPressUrl: process.env.GATSBY_WP_URL, gatsbySourceWordPressOptions: {}, // ... }, }, ], }
…通過子主題和父主題到目標插件:
// packages/gatsby-theme-wp-parent/gatsby-config.js module.exports = (options) => { return { plugins: [ // ... { resolve: `gatsby-plugin-source-wordpress`, options: { url: `${options.wordPressUrl}/graphql`, ...options.gatsbySourceWordPressOptions }, }, ], } }
CSS 主題
支持主題的 CSS-in-JS 解決方案似乎非常適合 Gatsby 主題。 我們的 Gatsby 子主題將使用 Chakra UI 框架,我們將稍微定制它的 CSS 主題。 是的,Chakra UI 也使用了“主題”的概念。 在此上下文中,主題是存儲設計系統樣式值、比例和/或設計標記的 JavaScript 對象。 為避免混淆,我將其稱為“CSS 主題”。 我們已經安裝了所需的@chakra-ui
包和 Gatsby 插件@chakra-ui/gatsby-plugin
。 讓我們探索插件的代碼以了解它是如何工作的。 它實際上將我們的 Gatsby 應用程序包裝到ChakraProvider
並公開src/theme.js
文件以進行陰影處理,這樣我們就可以像這樣進行:
/* packages/gatsby-theme-wp-child/src/@chakra-ui/gatsby-plugin/theme.js */ import { extendTheme } from "@chakra-ui/react" const theme = { fonts: { body: "Karma, sans-serif", heading: "Poppins, sans-serif", }, styles: { global: { body: { color: "gray.700", fontSize: "xl", }, }, }, components: { Button: { baseStyle: { borderRadius: "3xl", }, defaultProps: { colorScheme: "red", }, }, }, } export default extendTheme(theme)
我們再次使用了陰影的概念。 這裡的關鍵方面是我們創建theme.js
文件的位置。
稍後,我們將看到如何在用戶的項目級別上隱藏 CSS 主題。
使用 Lerna 發布主題
主題準備就緒後,您需要發布它們。 如果您想公開分享您的代碼,您很可能會將其發佈到公共 npm 註冊表。 如果您以前從未發布過包,您可以通過在本地計算機上使用 Verdaccio 來熟悉該過程。
在 Gatsby WP Themes,我們使用 Cloudsmith 的優質服務。 Cloudsmith 支持 npm 包的全功能註冊表,為私有註冊表提供高級選項,為公共註冊表提供免費解決方案。 我將繼續使用免費的 Cloudsmith 解決方案。 創建帳戶後,創建一個新的存儲庫; 我的是pehaa/gatsby-wp-theming
。
為了通過命令行更改 Cloudsmith 註冊表,您需要提供此註冊表的登錄憑據。 只需輸入:
npm login --registry=https://npm.cloudsmith.io/organistion/repository_name/
您將被要求提供您的用戶名、密碼(即您的 API KEY)和電子郵件。
對於多包 git 存儲庫,您可能希望使用 Lerna 來促進發布。 Lerna 與紗線工作區非常匹配。 您可以使用npm install --global lerna
全局安裝 Lerna CLI。 要在我們的項目中啟動它,我們將運行以下命令:
lerna init --independent
上面的命令將在 monorepo 的根目錄中創建一個lerna.json
文件。 您需要手動添加"useWorkspaces" : true
和"npmClient": "yarn"
; 如果它不是默認的公共 npm 之一,您可能還需要指定command.publish.registry
。
{ "npmClient": "yarn", "useWorkspaces": true, "version": "independent", "command": { "publish": { "registry": "https://cloudsmith.io/organisation/repository_name" } } }
然後, lerna publish
命令發布自上次發布以來已更改的包。 默認情況下,Lerna 會提示正在更新的每個包的版本更改。 您可以通過運行跳過提示:
lerna publish [major|minor|patch|premajor|preminor|prepatch|prerelease] --yes
您還可以將 Lerna 配置為使用 Conventional Commits Specification 來確定版本提升並生成 CHANGELOG.md 文件。 有了所有可用的選項,您應該能夠使您使用 Lerna 的方式適應您的工作流程。
在項目中使用主題
現在,讓我們停止開發服務器並從用戶的角度來看。 我們將創建一個新項目gatsby-wp-site
,它將gatsby-theme-wp-child
實現為從 npm 存儲庫安裝的包。 在我們的項目文件夾中,我們將安裝我們的四個依賴項: gatsby
、 react
、 react-dom
和主題本身。 由於我們使用 Cloudsmith 發布@pehaa
-scoped 包,我們必須添加一個.npmrc
文件,其中我們指定@pehaa
-scoped 存儲庫,如下所示:
mkdir gatsby-wp-site cd gatsby-wp-site echo "@pehaa:registry=https://npm.cloudsmith.io/pehaa/gatsby-wp-theming/" >> .npmrc yarn init -yp yarn add react react-dom gatsby @pehaa/gatsby-theme-wp-child
我們的網站幾乎準備就緒。 我們只需要創建一個gatsby-config.file
來加載主題並提供 WordPress URL。 完成後,我們就可以運行gatsby build
了。
// gatsby-config.js module.exports = { plugins: [ { resolve: "@pehaa/gatsby-theme-wp-child", options: { wordPressUrl: "https://yourwordpress.website" } } ] }
我們的網站已準備就緒。
定制呢? 我們仍然可以利用陰影。 此外,項目級別始終優先考慮陰影。 讓我們通過重寫 Footer 組件來看看它的實際效果。 現在,我們的頁腳在@pehaa/gatsby-theme-wp-child/src/components/Footer.js
中定義。 我們需要創建src
文件夾並重新創建以下文件結構:
gatsby-wp-site ├── src │ └── @pehaa │ └── gatsby-theme-wp-child │ └── components │ └── Footer.js
有了上面的文件結構,我們準備提供一個新版本的站點頁腳。 例如:
import React from "react" import { useStaticQuery, graphql } from "gatsby" import { Box } from "@chakra-ui/react" const Footer = () => { const data = useStaticQuery(graphql` query { wp { generalSettings { title } } } `) return ( <Box as="footer" p="6" fontSize="sm" bg="gray.700" color="white" mt="auto" textAlign="center" > <b>{data.wp.generalSettings.title}</b> - Built with WordPress and GatsbyJS </Box> ) } export default Footer
最後,讓我們看看如何使用 CSS 主題。 使用以下代碼,正確位於src/@chakra-ui/gatsby-plugin/theme.js
中,您可以在項目中擴展默認主題。
// src/@chakra-ui/gatsby-plugin/theme.js import { extendTheme } from "@chakra-ui/react" const theme = { /* ... */ } export default extendTheme(theme)
在大多數情況下,這並不是您所需要的。 新的 CSS 主題忽略了來自gatsby-theme-wp-child
,而您希望擴展 Gatsby 子主題中設置的 CSS 主題。 後者是可能的,因為extendTheme
函數允許您傳遞多個對象。 要使其工作,您必須從gatsby-theme-wp-child
導入 CSS 主題,並將其作為第二個參數傳遞給extendTheme
函數:
// src/@chakra-ui/gatsby-plugin/theme.js import theme from "@pehaa/gatsby-theme-wp-child/src/@chakra-ui/gatsby-plugin/theme" import { extendTheme } from "@chakra-ui/react" const extendedTheme = { fonts: { body: "Rubik, sans-serif", heading: "Rubik, sans-serif", }, /* ... */ } export default extendTheme(extendedTheme, theme)
你可以在這裡看到該站點,它是從這個 GitHub 存儲庫的主分支部署的。
包起來
您剛剛看到了 Gatsby 主題化的實際應用。 使用主題方法,您可以快速設置多個 Gatsby 站點,其中大部分代碼都保存在主題包中。 我們還看到瞭如何將項目的各個部分分成包以及如何利用陰影。
在我們的示例中,我們遵循了兩個主題設置,主題之間存在父子關係。 這可能並不總是一個理想的選擇。
有時,您可能希望在 UI 自定義方面走得更遠。 在這種情況下,您可能會考慮直接加載和隱藏父主題,而不是使用子主題。 在實際場景中,您可能會選擇一些子級主題來負責 UI 的不同、可重用部分(例如評論、表單或搜索)。
Smashing 雜誌的進一步閱讀
- 使用 Gatsby 函數構建 API
- Gatsby 無服務器功能和國際空間站
- 使用 Gatsby 函數和 Stripe 將開源軟件貨幣化
- 與 Marcy Sutton 一起粉碎播客第 20 集:什麼是蓋茨比?