坑梓網(wǎng)站建設流程網(wǎng)絡seo是什么意思
文章目錄
- 簡單了解React和Node
- 搭建開發(fā)環(huán)境
- React框架
- JavaScript客戶端
- ChallengeComponent
- 組件的主要結構
- 渲染
- 與應用程序集成
- 第一次運行前端
- 調試
- 將CORS配置添加到Spring Boot應用
- 使用應用程序
- 部署React應用程序
- 小結
前端代碼可從這里下載: 前端示例
后端使用這里介紹的Spring Boot應用程序:一個測試驅動的Spring Boot應用程序開發(fā)
現(xiàn)實世界中,用戶不會通過REST API使用應用程序,因此,這里為這個服務構造前端應用,便于用戶與應用程序進行交互。
簡單了解React和Node
React是一個構建用戶界面的JavaScript庫,由Facebook開發(fā),很流行,已被廣泛使用。React基于組件構建,編寫一段代碼即可在多處復用,這很有優(yōu)勢??梢詣?chuàng)建像 Thumbnail、LikeButton 和 Video 這樣的組件,然后將它們組合成整個應用程序。
React 組件是 JavaScript 函數(shù),學習 React 就是學習編程??梢栽赗eact中使用 JSX,這是由 React 推廣的 JavaScript 語法擴展,它允許將 JSX 標簽與相關的渲染邏輯放在一起,使得創(chuàng)建、維護和刪除 React 組件變得容易。
React 組件接收數(shù)據(jù)并返回應該出現(xiàn)在屏幕上的內(nèi)容??梢酝ㄟ^響應交互(例如用戶輸入)向它們傳遞新數(shù)據(jù)。然后,React 將更新屏幕以匹配新數(shù)據(jù)。也可以不用 React 去構建整個頁面,而只是將 React 添加到現(xiàn)有的 HTML 頁面中,在任何地方呈現(xiàn)交互式的 React 組件。
React 允許將組件放在一起,而不關注路由和數(shù)據(jù)獲取。要使用 React 構建整個應用程序,建議使用像 Next.js 或 Remix 這樣的全棧 React 框架。
React 也是一種架構。實現(xiàn)它的框架可以在服務端甚至是構建階段使用異步組件來獲取數(shù)據(jù),也可以從文件或數(shù)據(jù)庫讀取數(shù)據(jù),并將其傳遞給交互式組件。
簡單的說 Node.js 就是運行在服務端的 JavaScript。Node.js 是一個基于 Chrome JavaScript 運行時建立的一個平臺。Node.js 是一個事件驅動 I/O 服務端 JavaScript 環(huán)境,基于 Google 的 V8 引擎,V8 引擎執(zhí)行 Javascript 的速度非???#xff0c;性能非常好。
如果你是一個前端程序員,想創(chuàng)建自己的服務,那么 Node.js 是一個非常好的選擇。如果你熟悉 Javascript,那么將會很容易的學會 Node.js。當然,如果你是后端程序員,想部署一些高性能的服務,那么學習 Node.js 也是一個非常好的選擇。
搭建開發(fā)環(huán)境
首先,需要到nodejs.org站點上下載應用程序包來安裝Node.js,可以下載免安裝的zip版本,配置相應的環(huán)境變量即可。安裝后可以在控制臺輸入下列命令,查看Node.js和npm的版本:
> node -v
v16.14.2
> npm -v
9.8.1
現(xiàn)在,最新的長期支持的Node版本是20.9.0。
有了Node,就可以使用npx來創(chuàng)建React項目了,命令如下:
> npx create-react-app multiplication-frontend
下載并安裝依賴后,會看到如下輸出:
> npx create-react-app multiplication-frontendCreating a new React app in Z:\_Learn\multiplication-frontend.Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts with cra-template...added 1463 packages in 42sInitialized a git repository.Installing template dependencies using npm...added 69 packages, and changed 1 package in 5s
Removing template package using npm...removed 1 package in 2mCreated git commit.Success! Created multiplication-frontend at Z:\_Learn\multiplication-frontend
Inside that directory, you can run several commands:npm startStarts the development server.npm run buildBundles the app into static files for production.npm testStarts the test runner.npm run ejectRemoves this tool and copies build dependencies, configuration filesand scripts into the app directory. If you do this, you can’t go back!We suggest that you begin by typing:cd multiplication-frontendnpm startHappy hacking!
進入項目目錄,執(zhí)行npm start
命令,Node服務器就會啟動并打開一個瀏覽器窗口,訪問localhost:3000
地址,顯示剛剛生成的應用程序。
> cd .\multiplication-frontend\
> npm start
默認頁面如圖所示:
React框架
在IDEA中,加載項目,create-react-app工具已經(jīng)創(chuàng)建了許多文件,如圖所示:
- package.json和package-lock.json是npm文件,包含關于項目的基本信息,列出相關依賴,依賴項存儲在node_modules文件夾中。
- public存儲所有創(chuàng)建后不再變動的靜態(tài)文件。唯一的例外是index.html,應對其處理以包含生成的JavaScript代碼。
- src存儲所有的React源文件及其相關資源。這里可以找到入口點文件index.js和一個React組件App,該示例組件附帶自己的樣式表App.css和一個測試文件App.test.js。
這里,從index.html開始,刪除注釋后,內(nèi)容如下:
<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8" /><link rel="icon" href="%PUBLIC_URL%/favicon.ico" /><meta name="viewport" content="width=device-width, initial-scale=1" /><meta name="theme-color" content="#000000" /><metaname="description"content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /><link rel="manifest" href="%PUBLIC_URL%/manifest.json" /><title>React App</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body>
</html>
body很簡單。index.js定義了渲染內(nèi)容的入口,代碼如下:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<React.StrictMode><App /></React.StrictMode>
);reportWebVitals();
DOM(文檔對象模型)是HTML元素的樹結構表達,這段代碼將元素React.StrictMode
及其子元素App組件渲染到HTML中。具體地說,將渲染到ID為root的元素中,即插入index.html中的div標簽。由于App是一個組件,可能包含其他組件,因此,最終會處理并渲染整個React應用程序。
JavaScript客戶端
在創(chuàng)建第一個組件前,要確??梢栽L問前面創(chuàng)建的REST API接口,這需要使用JavaScript類。
JavaScript中的類與Java類類似,如下所示:
class ApiClient {static SERVER_URL = 'http://localhost:8080';static GET_CHALLENGE = '/challenges/random';static POST_RESULT = '/attempts';static challenge(): Promise<Response> {return fetch(ApiClient.SERVER_URL + ApiClient.GET_CHALLENGE);}static sendGuess(user: string,a: number,b: number,guess: number): Promise<Response> {return fetch(ApiClient.SERVER_URL + ApiClient.POST_RESULT, {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({userAlias: user,factorA: a,factorB: b,guess: guess})});}
}
export default ApiClient;
兩個方法都返回Promise,JavaScript中的Promise與Java中的Future類相似:表示異步操作的結果。fetch函數(shù)用來與HTTP服務進行交互。
challenge()方法調用fetch函數(shù)的基本形式,默認對URL執(zhí)行GET操作,返回Response對象。
sendGuess()方法接收請求所需的參數(shù),與第二個參數(shù)一起使用,定義了HTTP方法(POST)的對象、請求體的內(nèi)容類型(JSON)和請求體。
最后,為使類可以公開訪問,添加了export default ApiClient,就可以將完整的類引入其他組件和類中了。
ChallengeComponent
下面,創(chuàng)建第一個React組件,來處理Challenge域,包括:
- 將后端檢索到的數(shù)據(jù)渲染到ChallengeComponent
- 顯示表單供用戶發(fā)送猜測
下面是ChallengeComponent的代碼:
import ApiClient from "../services/ApiClient";
import React from "react";// 類從React.Component繼承,這就是React創(chuàng)建組件的方式。
// 唯一要實現(xiàn)的方法是render(),該方法必須返回DOM元素才能在瀏覽器中顯示。
class ChallengeComponent extends React.Component {// 構造函數(shù),初始化屬性及組件的state(如果需要的話),// 這里創(chuàng)建一個state來保持檢索到的挑戰(zhàn),以及用戶為解決嘗試而輸入的數(shù)據(jù)。constructor(props) {super(props);this.state = {a: '',b: '',user: '',message: '',guess: '',};// 兩個綁定方法。如果想要在事件處理程序中使用,這是必要的,需要實現(xiàn)這些方法來處理用戶輸入的數(shù)據(jù)。this.handleSubmitResult = this.handleSubmitResult.bind(this);this.handleChange = this.handleChange.bind(this);}// 這是一個生命周期方法,用于首次渲染組件后立即執(zhí)行邏輯。componentDidMount(): void {// 使用ApiClient實用程序類來調用服務,檢索挑戰(zhàn)。// 考慮到函數(shù)返回一個promise,使用then()來指定獲取響應時的操作。ApiClient.challenge().then(res => {if (res.ok) {// 使用then()解析promise,將REST API響應中預期的factorA和factorB傳遞給setState()。res.json().then(json => {this.setState({a: json.factorA,b: json.factorB});});} else {this.updateMessage("Can't reach the server");}});}handleChange(event) {const name = event.target.name;this.setState({[name]: event.target.value});}handleSubmitResult(event) {event.preventDefault();ApiClient.sendGuess(this.state.user,this.state.a,this.state.b,this.state.guess).then(res => {if (res.ok) {res.json().then(json => {if (json.correct) {this.updateMessage("Congratulations! Your guess is correct");} else {this.updateMessage("Oops! Your guess " + json.reaultAttempt + " is" +" wrong, but keep playing!");}});} else {this.updateMessage("Error: server error or not available");}});}updateMessage(m: string) {this.setState({message: m});}render() {return (<div><div><h3>Your new challenge is</h3><h1>{this.state.a} x {this.state.b}</h1></div><form onSubmit={this.handleSubmitResult}><label>Your alias:<input type="text" maxLength="12" name="user"value={this.state.user} onChange={this.handleChange}/></label><br/><label>Your guess:<input type="number" min="0" name="guess"value={this.state.guess} onChange={this.handleChange}/></label><br/><input type="submit" value="Submit"/></form><h4>{this.state.message}</h4></div>);}
}
export default ChallengeComponent;
組件的主要結構
import ApiClient from "../services/ApiClient";
import React from "react";// 類從React.Component繼承,這就是React創(chuàng)建組件的方式。
// 唯一要實現(xiàn)的方法是render(),該方法必須返回DOM元素才能在瀏覽器中顯示。
class ChallengeComponent extends React.Component {// 構造函數(shù),初始化屬性及組件的state(如果需要的話),// 這里創(chuàng)建一個state來保持檢索到的挑戰(zhàn),以及用戶為解決嘗試而輸入的數(shù)據(jù)。constructor(props) {super(props);this.state = {a: '',b: '',user: '',message: '',guess: '',};// 兩個綁定方法。如果想要在事件處理程序中使用,這是必要的,需要實現(xiàn)這些方法來處理用戶輸入的數(shù)據(jù)。this.handleSubmitResult = this.handleSubmitResult.bind(this);this.handleChange = this.handleChange.bind(this);}// 這是一個生命周期方法,用于首次渲染組件后立即執(zhí)行邏輯。componentDidMount(): void {// ... Component initialization}render() {return (// ... HTML as JSX ...)}
}
在React中,setState函數(shù)重新加載部分DOM。這意味著瀏覽器將再次渲染HTML被更改的部分,因此收到服務器響應后,會在頁面上看到乘法因子。
渲染
JSX可以混合使用HTML和JavaScript。
render() {return (<div><div><h3>Your new challenge is</h3><h1>{this.state.a} x {this.state.b}</h1></div><form onSubmit={this.handleSubmitResult}><label>Your alias:<input type="text" maxLength="12" name="user"value={this.state.user} onChange={this.handleChange}/></label><br/><label>Your guess:<input type="number" min="0" name="guess"value={this.state.guess} onChange={this.handleChange}/></label><br/><input type="submit" value="Submit"/></form><h4>{this.state.message}</h4></div>);}
ChallengeComponent有一個根div元素,包含3個主要代碼塊。第一個代碼塊通過state中的兩個參數(shù)來顯示挑戰(zhàn)。最后一個代碼塊,展示message狀態(tài)屬性。第二個代碼塊創(chuàng)建了一個表單,可以讓用戶輸入自己的猜測。在表單中,涉及相關的處理,用于處理用戶輸入。
handleChange(event) {const name = event.target.name;this.setState({[name]: event.target.value});}handleSubmitResult(event) {event.preventDefault();ApiClient.sendGuess(this.state.user,this.state.a,this.state.b,this.state.guess).then(res => {if (res.ok) {res.json().then(json => {if (json.correct) {this.updateMessage("Congratulations! Your guess is correct");} else {this.updateMessage("Oops! Your guess " + json.reaultAttempt + " is" +" wrong, but keep playing!");}});} else {this.updateMessage("Error: server error or not available");}});}
表單提交時,調用服務器的API來發(fā)送猜測。當獲取響應時,檢查是否正常,解析JSON,然后,更新狀態(tài)中的message屬性,最后,相應部分的HTML DOM對象會被再次渲染。
與應用程序集成
現(xiàn)在,以及完成了組件的代碼,就可以在應用程序中使用了。修改App.js,在其中添加創(chuàng)建的組件。
import React from "react";
import './App.css';
import ChallengeComponent from './components/ChallengeComponent';function App() {return (<div className="App"><header className="App-header"><ChallengeComponent/></header></div>);
}export default App;
框架應用程序在index.js中使用此App組件,構建代碼時,生成的腳本包含在index.html文件中。
還需要調整App.test.js中包含的測試代碼或直接刪除。這里不會探討React測試的細節(jié),現(xiàn)在可以將其刪除。
第一次運行前端
確保運行了Spring Boot應用程序,啟動控制臺,進入前端應用程序的文件夾,執(zhí)行npm start
命令來啟動React前端。
成功編譯后,會打開默認瀏覽器并顯示位于localhost:3000的頁面,顯示如下:
這里出問題了,應該去后端獲取參數(shù)的,但現(xiàn)在的參數(shù)區(qū)域空白,下面看看如何進行調試。
調試
大多數(shù)瀏覽器都為開發(fā)者提供了功能強大的工具,打開開發(fā)者模式,刷新瀏覽器,就可以查看前端是否與后端正確交互。
上面的網(wǎng)頁刷新,在開發(fā)者模式下,單擊“網(wǎng)絡”選項卡,會看到對http://localhost:8080/challenges/random的HTTP請求失敗,如圖所示:
控制臺還顯示一條描述性消息:已攔截跨源請求:同源策略禁止讀取位于 http://localhost:8080/challenges/random 的遠程資源。(原因:CORS 頭缺少 'Access-Control-Allow-Origin')。狀態(tài)碼:200。
默認情況下,瀏覽器會阻止那些嘗試訪問前端所在域以外的域中的資源的請求,以避免瀏覽器中的惡意頁面訪問其他頁面中的數(shù)據(jù),這稱為“同源策略”。本例中,雖然在本地主機中同時運行前端和后端,但是不同的端口,因此被認為是不同源的。
有多種方法可以解決該問題。這里,將啟用跨域資源共享(CORS),這是一種可在服務器端啟用的安全策略,允許前端使用來自不同源的REST API。
將CORS配置添加到Spring Boot應用
回到后端代碼庫,添加一個Spring Boot @Configuration類,配置CORS,代碼如下:
package cn.zhangjuli.multiplication.configuration;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @author Juli Zhang, <a href="mailto:zhjl@lut.edu.cn">Contact me</a> <br>*/
@Configuration
public class WebConfiguration implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("http://localhost:3000");}
}
這里通過CorsRegistry 實例完成工作,添加一個映射,允許前端的源訪問任何路徑(用/**表示)。也可以省略allowedOrigins部分,這樣,所有的源域都可以訪問。
注意:當前端和后端部署到不同的主機時,應該提供有選擇的CORS配置策略,避免為所有的源域添加完全訪問權限。
使用應用程序
現(xiàn)在,前后端可以協(xié)同工作了,重新啟動前后端應用程序,刷新瀏覽器,如圖所示:
這是一個測試,可以完成猜測游戲了。
部署React應用程序
目前為止,前端一直使用的是開發(fā)模式,這不是生產(chǎn)環(huán)境的工作方式。
要想部署React應用程序,首先需要構建它,使用npm run build
命令來構建用于生產(chǎn)部署的React應用程序。執(zhí)行如下:
> npm run build> multiplication-frontend@0.1.0 build
> react-scripts buildCreating an optimized production build...
One of your dependencies, babel-preset-react-app, is importing the
"@babel/plugin-proposal-private-property-in-object" package without
declaring it in its dependencies. This is currently working because
"@babel/plugin-proposal-private-property-in-object" is already in your
node_modules folder for unrelated reasons, but it may break at any time.babel-preset-react-app is part of the create-react-app project, which
is not maintianed anymore. It is thus unlikely that this bug will
ever be fixed. Add "@babel/plugin-proposal-private-property-in-object" to
your devDependencies to work around this error. This will make this message
go away.Compiled successfully.File sizes after gzip:47.14 kB build\static\js\main.b3d22150.js1.79 kB build\static\js\787.444a2e11.chunk.js515 B build\static\css\main.f855e6bc.cssThe project was built assuming it is hosted at /.
You can control this with the homepage field in your package.json.The build folder is ready to be deployed.
You may serve it with a static server:serve -s buildFind out more about deployment here:https://cra.link/deployment
該命令在build文件夾下生成了所有腳本和文件,還可以在其中找到放在public文件夾中的文件的副本。這些日志還說明了如何使用npm安裝靜態(tài)Web服務器。
另外,Spring Boot應用程序已經(jīng)嵌入了Web服務器Tomcat,也可以直接使用。最簡單的方式是,將整個應用程序(前端和后端)打包在同一個可部署單元中:Spring Boot生成的胖JAR文件。需要做的就是,將前端build文件夾中的所有文件復制到Multiplication代碼庫的src/main/resources/static文件夾中。Spring Boot的默認服務器配置為靜態(tài)Web文件添加了一些預定義的位置,static文件夾就是其中之一。如圖所示:
可根據(jù)需要配置這些資源位置及其映射。其中一個需要微調的就是WebMvcConfigurer接口實現(xiàn)。這里不需要改動,因為前端和后端共享同一個源域http://localhost:8080,當然,也可以刪除它。
小結
文章介紹了如何基于React創(chuàng)建一個前端Web應用程序的過程。首先,使用create-react-app工具創(chuàng)建React應用程序框架,然后創(chuàng)建一個ApiClient類來實現(xiàn)與后端API服務的訪問,并創(chuàng)建一個使用該服務并顯示結果的React組件。為了使前后端能夠協(xié)同,在后端增加了CORS配置。最后,介紹了如何構建用于生產(chǎn)環(huán)境的React應用程序,以及如何在Spring Boot的嵌入式Tomcat中集成。