如何使用 React 和 WordPress 為您的 Web 應用程序構建皮膚
已發表: 2022-03-10因此,您已經將自己培訓為一名網絡工程師,現在想要為您的客戶建立一個快速的在線商店。 產品列表應該立即出現,搜索也應該不會浪費超過一秒鐘。 那是白日夢的東西嗎?
不再。 好吧,至少沒有什麼是結合 WordPress 的 REST API 和現代 JavaScript 庫 React 無法實現的。
等等,什麼? WordPress REST API?
是的,WordPress REST API 將幫助您為 Web 應用程序構建後端基礎。 如果您正在構建內容驅動的解決方案,這對於您的 Web 應用程序的後端技術來說是一個不錯的選擇。 WordPress 也將與其他技術順利互操作; 您可以使用 Node.js 作為應用程序的焦點來連接到其他 RESTful 服務。
WordPress REST API 是 WordPress 的遊戲規則改變者,現在可以安全地稱為 Web 應用程序框架,而不僅僅是 CMS。 現在前端和後端已經完全解耦,WordPress 可以用作移動應用程序後端,也可以用作任何專注於內容的系統的後端。
但為什麼是 WordPress? 原因:您會對 WordPress 開箱即用的功能感到驚訝。 您將獲得廣泛的用戶管理、媒體管理和一組對開發人員非常友好的 API,以擴展您的工作。
在本文中,我將引導您使用名為 React 的 JavaScript 庫構建一個 SPA(單頁應用程序)WordPress 主題,並連接到 WP REST API。
讓我們開始構建主題
本文假設您已經熟悉各種現有的 WordPress API,尤其是那些推動網站美學主題開發和網站插件功能開發的 API。 我還假設您已經為 WordPress 設置了開發平台,例如 Linux 或 MacOS 環境中的 LAMP 堆棧。
不過,為簡單起見,我將僅提及在與 Windows 一起使用的 XAMPP 平台上可見的絕對路徑。
現在,讓我們在本地主機中創建一個 WordPress 實例,將其命名為“Celestial”。 這是 WordPress 主題的名稱,我們將使用它來幫助我們確定構建未來主題的方向,以便與由 WordPress REST API 支持的 Web 應用程序一起使用。 您可能已經熟悉 WordPress 深受喜愛的模板層次結構,但使用 REST API,您有機會發現不同的東西!
然後我們需要在wp-content\themes
文件夾中為主題創建一個文件夾。 導航到C:\xampp\htdocs\celestial\wp-content\themes\
(或等效)並創建一個文件夾celestial
。 在celestial
主題文件夾中添加這些文件:
-
index.php
主題的包羅萬象的文件。 -
style.css
這包含有關主題的信息(而不是實際的 CSS)。 -
functions.php
編寫 CSS 和 JS 文件的功能和導入。
如果您希望在儀表板內顯示主題圖片,請添加名為screenshot.jpg
的圖像文件。
注意:每個文件的代碼只有幾行,可以在 GitHub 上找到。
接下來,登錄到您的 WordPress 儀表板,前往外觀→主題並選擇“天體”作為主題。 現在基礎已經到位,讓我們開始創建主題。
開始使用 React 和 Webpack 為主題
React 是一個非常流行的 UI 庫,由 Facebook 支持和維護。 根據 Stack Overflow 的 2017 年開發者調查結果,“React 是開發者最喜愛的。”
為了啟動項目,您需要將項目初始化為 NPM(節點包管理器)項目。 這是通過終端中的命令npm init
完成的(在您的計算機上安裝了 Node.js 和 NPM 之後)。 初始化項目將提示您輸入某些配置信息。 初始化成功後,NPM 會在主題的根目錄下創建一個 package.json 文件。 該文件將包含項目詳細信息和項目的所有依賴項。
React 現在在 MIT 許可下,所以我們將使用 React 16 版作為這個項目的 JavaScript 庫。 React 在底層有一些很酷的特性,比如 Virtual DOM(內存中文檔的表示),並且有許多圍繞它的工具,比如 React Router。 React 也用於 WordPress 的項目 Calypso——WordPress.com 的儀表板。
我們現在將所需的 NPM 包安裝到項目中:
- 在終端中輸入
npm install --save react react-dom
並按 Enter 安裝軟件包。 由於我們將主題構建為單頁應用程序 (SPA),因此我們需要 Webpack 等工具的幫助。 我們將編寫代碼作為不同的組件,Webpack 將幫助我們將它們打包在一起並將它們輸出為單個 .js 或 .css 文件。 簡而言之,它是一個模塊捆綁器。
Webpack 必須首先全局安裝在您的計算機上。 為此,我們可以再次利用 NPM。 - 輸入命令
npm install -g webpack
以在您的系統中全局安裝最新的穩定版 Webpack。
接下來,我們將在我們的應用程序中安裝支持 Webpack 的 NPM 包。 - 轉到我的 git repo 中的package.json文件,然後將其餘的依賴項從那裡複製到你的package.json的依賴項部分。 然後再次運行
npm install
以安裝package.json中的所有包。{ "name": "celestial", "version": "1.0.0", "description": "A basic theme using the WordPress REST API and React", "main": "index.js", "dependencies": { "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "css-loader": "^0.28.7", "extract-text-webpack-plugin": "^3.0.1", "file-loader": "^1.1.5", "image-webpack-loader": "^3.4.2", "node-sass": "^4.5.3", "path": "^0.12.7", "react": "^16.0.0", "react-dom": "^16.0.0", "react-router-dom": "^4.2.2", "sass-loader": "^6.0.6", "style-loader": "^0.19.0", "url-loader": "^0.6.2", "webpack": "^3.6.0" }, "devDependencies": {}, "scripts": { "build": "webpack", "watch": "webpack --watch" }, "keywords": [ "blog", "decoupled", "react", "rest-api" ], "author": "Muhammad Muhsin", "license": "GPL-3.0" }
以上是該項目的 package.json 文件中所有必需包的列表。 - 從 GitHub 複製配置並將其粘貼到主題文件夾的webpack.config.js文件中。
var ExtractTextPlugin = require("extract-text-webpack-plugin"); var path = require('path'); module.exports = { entry: { app: './src/index.jsx' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, module: { rules: [ { test: /\.scss$/, use: ExtractTextPlugin.extract({ fallback: 'style-loader', use: ['css-loader','sass-loader'], publicPath: 'dist' }) }, { test: /\.jsx?$/, exclude: /node_modules/, use: 'babel-loader' }, { test: /\.(jpe?g|png|gif|svg)$/i, use: [ 'file-loader?name=[name].[ext]&outputPath=images/&publicPath=https://localhost/celestial/wp-content/themes/celestial/dist/images', 'image-webpack-loader' ] }, { test: /\.(woff2?|svg)$/, loader: 'url-loader?limit=10000&name=fonts/[name].[ext]' }, { test: /\.(ttf|eot)$/, loader: 'file-loader?name=fonts/[name].[ext]' } ] }, resolve: { extensions: ['.js', '.jsx'] }, plugins: [ new ExtractTextPlugin({ filename: "style.css", allChunks: true }) ] }
重要提示:請注意module.exports
→module
→rules[3]
→use
→publicPath
可以根據項目的 dist 圖像在 localhost 中的位置而改變。 如果這是錯誤的,圖像可能無法在 Web 應用程序中正確顯示。 - 之後,這些命令可用於編譯項目:
-
webpack
或npm run build
編譯項目,或者 webpack --watch
或npm run watch
編譯項目並觀察變化。
-
注意:為了更好地理解 Webpack,請閱讀 Joseph Zimmerman 在 Smashing Magazine 上的這篇文章。
擴展 WordPress REST API
WordPress REST API 帶有許多不同的端點,用於獲取帖子、頁面、媒體等。 但是,他們可能並不總是在回復中包含所有詳細信息。 例如,posts 方法不提供特色圖片的 URL 或作者姓名。 因此,我們必須對這些元素中的每一個進行單獨的調用。
但是,如果您想讓自己的數據出現在帖子響應中怎麼辦? 這就是擴展 REST API 的魅力所在。以下代碼將在對帖子請求的響應中添加兩個額外的變量,即author_name
和featured_image_src
。 代碼在functions.php文件中:
// Add various fields to the JSON output function celestial_register_fields() { // Add Author Name register_rest_field( 'post', 'author_name', array( 'get_callback' => 'celestial_get_author_name', 'update_callback' => null, 'schema' => null ) ); // Add Featured Image register_rest_field( 'post', 'featured_image_src', array( 'get_callback' => 'celestial_get_image_src', 'update_callback' => null, 'schema' => null ) ); // Add Published Date register_rest_field( 'post', 'published_date', array( 'get_callback' => 'celestial_published_date', 'update_callback' => null, 'schema' => null ) ); } add_action( 'rest_api_init', 'celestial_register_fields' ); function celestial_get_author_name( $object, $field_name, $request ) { return get_the_author_meta( 'display_name' ); } function celestial_get_image_src( $object, $field_name, $request ) { if($object[ 'featured_media' ] == 0) { return $object[ 'featured_media' ]; } $feat_img_array = wp_get_attachment_image_src( $object[ 'featured_media' ], 'thumbnail', true ); return $feat_img_array[0]; } function celestial_published_date( $object, $field_name, $request ) { return get_the_time('F j, Y'); }
使用 functions.php 文件中的其他變量擴展 REST API。
一個全局 JavaScript 變量
我們將在整個 React 應用程序中使用某些 WordPress 常量(或稱為“變量”)。 這將是有關應用程序的各種路線的信息(稍後將成為 WooCommerce 特定的路線)。
該變量在functions.php文件中定義。 它將被稱為 'CelestialSettings' 並附加到celestial-scripts
,即入隊的app.js文件的句柄:
wp_enqueue_script( 'celestial-script', get_stylesheet_directory_uri() . '/dist/app.js' , array(), '1.0', true ); $url = trailingslashit( home_url() ); $path = trailingslashit( parse_url( $url, PHP_URL_PATH ) ); wp_scripts()->add_data( 'celestial-script', 'data', sprintf( 'var CelestialSettings = %s;', wp_json_encode( array( 'title' => get_bloginfo( 'name', 'display' ), 'path' => $path, 'URL' => array( 'api' => esc_url_raw( get_rest_url( null, '/wp/v2' ) ), 'root' => esc_url_raw( $url ), ), 'woo' => array( 'url' => esc_url_raw( 'https://localhost/celestial/wp-json/wc/v2/' ), 'consumer_key' => 'ck_803bcdcaa73d3a406a0f107041b07ef6217e05b9', 'consumer_secret' => 'cs_c50ba3a77cc88c3bf46ebac49bbc96de3a543f03' ), ) ) ) );
將 WordPress (PHP) 變量傳遞到前端。
上面的代碼顯示了一個將 WordPress (PHP) 變量導入前端的示例,這是構建解耦主題時的一項重要且非常有用的技術。 該對像變量包含站點標題、路徑、API 的 URL 和根以及與 WooCommerce 相關的三個變量(稍後解釋)。
反應和 JSX
React 與其他主要的 JavaScript 庫不同。 我的意思是,我們通常在 HTML 中編寫 JavaScript。 然而,當涉及到 React 時,我們在 JavaScript 代碼中編寫 HTML。 更準確地說,我們在 JS 中編寫 JSX。 JSX 與 HTML 非常相似,但有一些不同之處。 例如, class
屬性寫為className
。 然後通過 Webpack 和 Babel 將其轉換為純 JavaScript 並保存在app.js中。
然而,編寫 JSX 有一些限制。 例如,我們的render()
方法中只能有一個子元素,它將作為組件的根元素。 但是,優點是更容易調試。 我們可以確切地知道我們在哪裡犯了錯誤,而在普通 HTML 中,我們的錯誤通常不會明確顯示。 我們將為這個項目編寫 JSX,因此 JavaScript 文件的擴展名為.jsx
。 但是,如果您願意,它也可以是.js
。
在src
文件夾中創建以下文件:
-
index.jsx
(主文件和包含 React Router 配置的文件) -
header.jsx
(標題組件) -
footer.jsx
(頁腳組件) -
posts.jsx
(用於帖子存檔) -
post-list.jsx
(posts.jsx
中單個帖子的組件) -
post.jsx
(用於單個帖子) -
products.jsx
(包含來自 WooCommerce 的所有產品) -
product.jsx
(顯示來自 WooCommerce 的單個產品) -
style.scss
(包含所有 SASS 格式的 CSS 代碼)
ReactDOM.render()
index.jsx文件是項目的根目錄。 我的意思是, index.jsx包含渲染到 DOM 的組件 App。
import { render } from 'react-dom'; // importing render from ReactDOM const App = () => ( // defining the routes <div> <Header /> <div> <Switch> <Route exact path={CelestialSettings.path} component={Posts} /> // the root path </Switch> </div> <Footer /> </div> ); // React Router const routes = ( <Router> <Route path="/" component={App} /> </Router> ); render( // rendering to the DOM by replacing #page with the root React component (routes), document.getElementById('page') // rendering the route );
其他組件在 React Router 中指定,將在訪問不同的路由時加載。
這就是我們編寫模塊化組件的方式,所有不同的組件最終都以index.jsx結尾。
有狀態與無狀態組件
您會注意到組件是以以下兩種方式之一編寫的:
-
const App = () => (
-
class Post extends React.Component {
第一種方法是我們如何編寫無狀態組件,第二種是有狀態組件的示例。 無狀態意味著組件中沒有“狀態”。 'state'本質上是一個在組件內部有信息的變量,每次變量變化時,組件都會重新渲染。 有狀態組件也稱為“智能組件”。 因此,狀態變量用於該組件內的內部通信。
第二種類型,無狀態組件中沒有狀態變量,有時稱為“啞組件”。 但是,與有狀態組件一樣,它們具有“道具”,這是從其父組件傳遞給它們的屬性。
有狀態組件具有 React 生命週期方法,而無狀態組件只有render()
方法,這是它的默認方法。
React 生命週期方法
這些是在組件生命週期的不同階段調用的方法,我們可以覆蓋這些方法以在這些實例中運行我們自己的代碼。 我們在我們的應用程序中使用以下方法:
-
constructor()
在安裝組件之前調用。 -
componentDidMount()
在安裝組件後立即調用。 -
render()
為呈現 JSX (HTML) 內容而調用的方法。 -
componentDidUpdate()
在組件更新時調用。 -
componentWillUnmount()
當要刪除組件時調用。
注意:要了解有關組件及其生命週期的更多信息,請閱讀此處的文檔。
JavaScript 承諾
我們將使用 JavaScript Promises 從我們的 WordPress REST API 獲取數據。 首先,我們的functions.php中有 REST API 的 URL,我們將它作為一個 JavaScript 變量附加在其中,我們可以從前端訪問它。
我們將使用 JavaScript 的 fetch API 方法從不同的端點獲取數據。 我們正在添加一個加載器以在獲取內容時向用戶顯示:
getMorePosts() { var that = this; var totalPages; // adding a loader jQuery("#loader").addClass("active"); this.setState({ page: this.state.page + 1 }); fetch(CelestialSettings.URL.api + "/posts/?page=" + this.state.page) .then(function (response) { for (var pair of response.headers.entries()) { // getting the total number of pages if (pair[0] == 'x-wp-totalpages') { totalPages = pair[1]; } if (that.state.page >= totalPages) { that.setState({ getPosts: false }) } } if (!response.ok) { throw Error(response.statusText); } return response.json(); }) .then(function (results) { var allPosts = that.state.posts.slice(); results.forEach(function (single) { allPosts.push(single); }) that.setState({ posts: allPosts }); // removing the loader jQuery("#loader").removeClass("active"); }).catch(function (error) { console.log('There has been a problem with your fetch operation: ' + error.message); jQuery("#loader").remove(); }); }
從各個端點獲取數據,加載程序指示進程正在運行。
使用 React 路由器
React Router 是為我們的應用程序處理客戶端路由的庫。 使用 WordPress 可以實現服務器端路由,但要實現真正的 SPA 體驗,我們需要 React Router 的幫助。
從版本 4 開始,React Router 被分成三個包: react-router
、 react-router-dom
和react-router-native
。 我們將在這個項目中使用react-router-dom
,因為它是在 Web 應用程序中使用的。
由於已經安裝了react-router-dom
,我們可以在index.jsx文件中編寫路由器配置。 代碼如下:
const App = () => ( <div> <Header /> <div> <Switch> <Route exact path={CelestialSettings.path} component={Posts} /> <Route exact path={CelestialSettings.path + 'posts/:slug'} component={Post} /> <Route exact path={CelestialSettings.path + 'products'} component={Products} /> <Route exact path={CelestialSettings.path + 'products/:product'} component={Product} /> <Route path="*" component={NotFound} /> </Switch> </div> <Footer /> </div> ); // Routes const routes = ( <Router> <Route path="/" component={App} /> </Router> ); render( (routes), document.getElementById('page') );
index.jsx 文件中的路由器配置。
上面的代碼將負責在客戶端處理的所有路由。 最後一行中的*
表示上面未提及的任何其他路線都會將用戶帶到“404 Not Found”頁面。
<Link to="">
標籤代替<a href=””>
標籤用於使用 React Router 在不同頁面之間進行鏈接:
<div className="navbar-nav"> <Link className="nav-item nav-link active" to={CelestialSettings.path} >Home <span className="sr-only">(current)</span></Link> <Link className="nav-item nav-link" to={CelestialSettings.path + "products/"} >Products</Link> </div>
使用
<Link to="">
標籤在不同頁面之間進行鏈接。獲取測試數據
現在您已經創建了主題,是時候添加一些數據了。 添加數據的一種方法是創建我們自己的內容。 但是,有一種更簡單(更好)的方法可以將數據添加到我們的 WordPress 網站。 此方法從外部源導入佔位符數據:
- 前往 https://codex.wordpress.org/Theme_Unit_Test 並下載主題單元測試數據
- 前往工具 > 導入 > WordPress安裝 WordPress 導入器。
- 安裝 WordPress 導入器後,單擊運行導入器。
- 在導入器中單擊“選擇文件”
- 選擇下載的文件並導入 WordPress 主題單元測試數據
現在您必須選擇theme-unit-test-data.xml文件,所有佔位符內容現在都在您的站點上。
WooCommerce 集成
現在,我們已經準備好使用 React 為我們的商店提供動力了。 為此,我們將使用products.jsx和product.jsx文件,它們的代碼分別類似於post.jsx和post.jsx 。
我們將在“woo”下的 CelestialSettings 添加另外三個變量(請參閱全局 JavaScript 變量):
- 網址
consumer_key
-
consumer_secret
Consumer key 和 Consumer secret 必須從Dashboard → WooCommerce → Settings → API → Keys/Apps生成。
對於 woo URL,您必須手動添加它(因為 WooCommerce 僅允許通過 SSL 傳輸,所以添加帶有 https 的 URL,即https://localhost/celestial/wp-json/wc/v2/
)。
複製 Consumer 密鑰和 Consumer Secret 並將它們粘貼到functions.php中的適當位置。 這將作為通過 API 調用訪問 WooCommerce 的身份驗證。
有關其 API 的更多信息,請訪問 WooCommerce REST API 文檔。 products.jsx文件包含用產品填充商店的代碼。 管理員可以從儀表板添加產品。 只需轉到儀表板→產品→添加新產品並輸入產品詳細信息。
當您單擊某個產品時,您將被帶到product.jsx頁面:
上述頁面的代碼類似於post.jsx :
renderProduct() { return ( <div className="card"> <div className="card-body"> <div className="col-sm-4"><img className="product-image" src={this.state.product.images ? this.state.product.images[0].src : null} alt={this.state.product.images ? this.state.product.images[0].alt : null } /></div> <div className="col-sm-8"> <h4 className="card-title">{this.state.product.name}</h4> <p className="card-text"><strike>${this.state.product.regular_price}</strike> <u>${this.state.product.sale_price}</u></p> <p className="card-text"><small className="text-muted">{this.state.product.stock_quantity} in stock</small></p> <p className="card-text">{jQuery(this.state.product.description).text()}</p> </div> </div> </div> ); }
product.jsx 文件的代碼" alt="product.jsx 文件的代碼
永久鏈接
為了使主題正常工作,我們必須在Dashboard → Settings → Permalinks中設置以下永久鏈接:
在Common Settings → Custom Structure下:
https://localhost/celestial/posts/%postname%/
在產品永久鏈接→自定義基礎下:
/products/
如果您未按上述方式設置永久鏈接,則主題可能無法按預期運行。
WooCommerce 修復
當您導航到localhost/celestial/products時,您可能會得到一個應該加載產品的空白區域。 發生這種情況是因為 WooCommerce 需要經過身份驗證的請求,而我們的 localhost 不是 https。 要解決此問題:
- 訪問 https://localhost/celestial/wp-json/wc/v2/products。 這會給我們一個警告:
- 單擊高級 > 繼續到本地主機(不安全) 。
- 現在,如果您返回產品頁面,商品將正確顯示。
注意:如果您在 Mac 上使用 Valet,則必須在您的站點上運行 Valet Secure 以使用 TLS 證書保護本地站點。 這是解決問題的另一種方法。
什麼是 ScrollMagic?
ScrollMagic 是一個庫,它允許我們在滾動頁面時執行某些操作。 要使用 ScrollMagic,我們將在functions.php中加入 ScrollMagic JavaScript 庫。 我們在這個項目的兩個實例中使用了 ScrollMagic:
- 要在posts.jsx組件中延遲加載帖子:
componentDidMount() { var that = this; window.onbeforeunload = function () { window.scrollTo(0, 0); } // init ScrollMagic Controller that.state.controller = new ScrollMagic.Controller(); // build scene var scene = new ScrollMagic.Scene({ triggerElement: "#colophon", triggerHook: "onEnter" }) .addTo(that.state.controller) .on("enter", function (e) { if (that.state.getPosts && that.getMorePosts !== null) { that.getMorePosts(); } }); }
在 posts.jsx 組件中延遲加載帖子 - 通過分別在posts.jsx和products.jsx組件中滾動帖子和產品來顯示帖子的淡入動畫:
componentDidUpdate() { var FadeInController = new ScrollMagic.Controller(); jQuery('.posts-container .col-md-4.card-outer').each(function () { // build a scene var FadeInScene = new ScrollMagic.Scene({ triggerElement: this.children[0], reverse: false, triggerHook: 1 }) .setClassToggle(this, 'fade-in') .addTo(FadeInController); }); }
為出現滾動的帖子應用淡入動畫
我們現在準備從前端查看我們的主題。 在您的 Web 瀏覽器上導航到localhost/celestial並查看您的主題。
拍拍自己的後背,因為您現在已經成功創建了主題!
其他帶有 JavaScript 庫的 WordPress 主題
如果你覺得這很有幫助,你可以看看使用現代 JavaScript 庫/框架構建的其他很棒的解耦 WordPress 主題:
- Foxhound:第一個進入 WordPress 主題存儲庫的解耦主題。 這個主題由 Kelly Dwan 編寫,使用 React、Redux 和 React Router。
- Anadama React:同一作者的另一個主題,但使用 Flux 代替 Redux,使用 Page 代替 React Router。
- Wallace:由 Kirby 編寫,這個主題使用 Angular 和 WordPress REST API。
- Picard:由 Automattic 自己編寫,用於展示 WordPress REST API 的功能。
- React Verse:我基於 Foxhound 編寫的 React 和 Redux 主題。
下一步
我想向您展示的主要部分現在已經完成。 您可以繼續構建項目以獲取更多知識。 這些是您可以遵循的一些建議:
- 帶有 WooCommerce 插件的成熟商店,包括結賬和購物車;
- 歸檔、標籤、分類等各頁面;
- 使用 Redux 或 Flux 進行狀態管理。
祝你好運,編碼愉快!