如今咱們有了一個屬於文章的API,能夠添加、修改、刪除、查看文章,可是對於咱們的網站來講,還須要一個用戶界面才行。如今開始探索一下ReactJS
吧。css
常常聽到有前端三大框架Angular、React、Vue
的說法,不過React官網對本身的介紹倒是這樣的:html
A JavaScript library for building user interfaces
一個用來構建用戶界面的JavaScript庫。首先咱們須要配置一下環境,以便使用React
。前端
首先咱們要安裝一個Node.js
,目前咱們還不用深刻了解Node
,只須要知道它能幫助你在非瀏覽器環境下運行JS代碼就好了。官網爲node.org
,直接下載安裝就行,Linux用戶推薦用各自的包管理器安裝。node
Node
自帶包管理器npm
,有點相似Python的pip
,不過這裏咱們使用yarn這個包管理器。按照官網說明安裝完以後,打開終端,並進入咱們的項目目錄react_drf
,爲接下來的工做作準備。react
從零開始構建一個React
項目涉及到的東西是比較多的,這時候就有一些方便的腳手架能夠選擇,腳手架能夠幫咱們省掉不少麻煩的配置。在這裏先選用Facebook
官方提供的create-react-app
。npm
$ yarn create react-app frontend
使用上述命令後,你將會看到react_drf
目錄下多了個frontend
文件夾:編程
$ cd frontend $ yarn start
這下你會看到瀏覽器跳轉到localhost:3000
,而且應該看到一個轉動的React logo
的頁面,這說明你安裝成功了,接下來瀏覽一下frontend
這個目錄下的文件,但還不要作任何改動。json
如今讓咱們一塊兒來看一下frontend/src
這個目錄,基本上目前咱們只要關心這個目錄下的內容就能夠了。首先來看一下這裏的frontend/src/index.js
:後端
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; ReactDOM.render( <React.StrictMode> <App /> </React.StrictMode>, document.getElementById('root') ); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister();
可能你已經注意到了代碼中的<App />
這一部分,彷佛HTML
中並無這一標籤,而且爲何JS
文件中有這樣相似HTML
的東西呢?可能你的心中已經佈滿了疑問,不要緊,讓咱們先動手改一下這段代碼:瀏覽器
... <React.StrictMode> <div>hello world</div> </React.StrictMode>, ...
我想,上述寫法應該能讓你看出應該在哪裏修改代碼。如今,打開瀏覽器,看看localhost:3000
這個頁面,應該能看到頁面發生了改變並顯示了hello world。若是你以前不當心關閉了終端,請記得從新打開,並在frontend
目錄下運行yarn start
。
這裏我不打算詳細介紹React
的基礎知識了,目前我尚未見過比React官方文檔更好的學習資料,哪怕你以爲本身的英語不好勁,官方的中文文檔也值得一看,這裏我僅僅粗略介紹一下接下來要接觸的知識點。
React
中能夠把HTML
、CSS
、JavaScript
混合在一塊兒,一個本來在HTML
中的元素能夠被賦值給變量,如const element = <h1>Hello, world!</h1>;
,JSX
裏面也能夠插入JS,如const name = 'Josh Perez'; // 使用JS變量 const element = <h1>Hello, {name}</h1>;
React
認爲UI
應該是組件化的,就像搭積木同樣,由一個一個組件搭起來,這樣將一個大頁面的工做拆分爲不少個小組件,便於複用,也方便多人協做開發。以前看到的<App />
就是一個組件。在frontend/src
目錄下建立新文件ArticleList.js
,正如名字所示,這是一個文章列表的組件。
import React, {Component} from "react"; class ArticleList extends Component { constructor(props) { super(props); } }
如今咱們定義了一個類組件,它繼承自React
的Component
類,這裏咱們寫了它的第一個生命週期函數,也就是constructor
,若是你熟悉過任何一門面向對象的語言,這裏應該不難理解。constructor
並非React
獨有的,而是JavaScript
原生的寫法,不過對於類組件來講,它固然也能夠算生命週期的一部分。
如今來看第二個生命週期函數render
:
class ArticleList extends Component { constructor(props) { ...... } render() { return <div>第一個組件</div>; } }
render
函數是類組件中惟一一個必須被實現的函數,組件將根據這個函數的返回值渲染內容。這裏的<div>第一個組件</div>
就是JSX
,若是有多行嵌套能夠用括號括起來:
return ( <div className="ArticleList"> <div>文章一</div> <div>文章二</div> </div> );
那怎麼讓這個組件被渲染呢?聰明的你可能已經把App.js
的代碼看了一遍,咱們在ArticleList.js
的最後面添加一行代碼
export default ArticleList;
同時修改index.js
:
import React from 'react'; ...... // 原先引入App的那行能夠刪掉 import ArticleList from './ArticleList' ReactDOM.render( <React.StrictMode> <ArticleList /> </React.StrictMode>, document.getElementById('root') ); ......
如今運行yarn start
,你將在看到瀏覽器啓動並顯示你在render
函數中返回的內容。固然事實上若是你以前沒有中止終端的運行,那麼就沒必要要從新運行yarn start
,代碼作了更改,會自動從新渲染,之後再也不提醒如何查看咱們的成果了。
接下來開始實現文章列表界面,可是先不急着從API獲取文章數據,先讓咱們模擬一下文章數據:
const articleList = [ { "id": 2, "title": "React", "body": "React is good", "created": "2020-03-21T21:19:31.732703", "updated": "2020-03-21T21:19:31.732728" }, { "id": 1, "title": "React", "body": "React is good", "created": "2020-03-21T21:10:53.922033", "updated": "2020-03-21T21:10:53.922128" } ]; class ArticleList extends Component { constructor(props) { super(props); this.state = { articleList: articleList, } } ...... }
注意到這裏添加了一個articleList
列表,在構造函數
裏多了一個this.state
,併爲其設置了articleList
屬性。如今來修改render
函數:
render() { return ( <div className="ArticleList"> {this.state.articleList.map(item => <div key={item.id}> <h4>{item.title}</h4> <p> <strong>{item.body}</strong> <br/> <em>建立時間:{item.created}</em> <em>更新時間:{item.updated}</em> </p> </div> )} </div> ); }
如今來大體講解一下上面的代碼,首先咱們看到最外層的div
標籤,它擁有一個className
屬性,這個實際上就是HTML
的class
屬性,這麼寫的緣由也很簡單,JSX
容許JS
和HTML
混合在一塊兒,但在不少編程語言(包括JS)裏class
都是用於建立類的關鍵字,因此給它改個名字便於區分。
接着咱們看到了在JSX
中如何使用JavaScript
,咱們在大括號裏使用了map
方法,使用箭頭函數,讓不一樣標籤裏包含了文章標題、正文等內容。
注意到包含<div key={item.id}>
這一行,如今若是你已經使用了yarn start
命令,你將會在瀏覽器看到一個簡陋的文章列表,若是你刪除這個key={item.id}
,在瀏覽器按下F12
,你將會在控制檯看到警告信息。
總之記住React
要求這類列表元素,必需要要有一個惟一的key
標識來讓React
能識別哪些元素被改變了。在後臺的真實數據中,id
這個字段是主鍵,也就是惟一的,恰好能夠利用。
好了,咱們已經使用虛假的數據嘗試了一把,以前說過先後端分離開發的一個好處是前端與後端約定好接口後,能夠各自分開並行開發,那麼實質上就會有一些工具來幫助生成「假的API」或者虛假的前端請求之類來幫助測試,有興趣的能夠去搜索搜索。
固然這裏咱們是爲了學習,開發只有本身一我的而已,那如今讓咱們來試試使用真實的API吧。
在正式開始以前,咱們還要先了解一下瀏覽器的同源策略。想象一下,若是你在a.com
登陸瀏覽了一段時間,再跑去b.com
逛逛,結果b.com
直接取到了你在a.com
的cookie
,用於在a.com
登陸你的帳號,那實在是太可怕了,尤爲是當a.com
是銀行或購物網站的時候。基於此,瀏覽器使用同源策略來作一個基本的安全保障。簡單來講,就是域名、端口、協議只要有一個不同,就會受到訪問限制:
咱們能夠簡單嘗試一下,修改ArticleList.js
:
constructor(props) { ...... } componentDidMount() { fetch('http://127.0.0.1:8000/articles/') .then(response => response.json()) .then(result => this.setState({articleList: result})) .catch(e => e); } ......
這裏咱們又見到了一個新的生命週期函數,componentDidMount
將在render
以後執行。這裏調用了原生的fetch
函數,直接簡單粗暴的請求API,在瀏覽器中按下F12
,你會看到以下報錯:
雖然域名(IP)、協議都相同,可是端口號卻不一樣,Django
後臺在8000
,React
卻在3000
,因此發生了錯誤。
這裏給出一個在開發時能夠用的方案,幫助咱們解決這個問題,固然,在實際部署時不能這麼作,部署時的具體狀況之後再講,這裏咱們先找到frontend/package.json
這個文件,在其中添加一行:
{ ......., "proxy": "http://127.0.0.1:8000" }
省略號表明以前的內容,若是你插入在文件最後面可別忘了給前面加一個逗號。
接着對源代碼作一處修改:
componentDidMount() { fetch('/articles/') ...... }
這樣開發服務器就能識別你的請求將其代理到http://127.0.0.1:8000
也就是Django服務所在的地址了,如今從新運行yarn start
,能夠在瀏覽器看到你在後端添加的數據啦。
此次就講到這裏了,下一章要講講將此次的代碼在細節上優化一下。這一章有不少React的基礎知識並無講解,若是讀者對React
目前尚未一點了解,那麼建議如今去React官網看一下,至少把基礎教程看完。
歡迎關注個人公衆號「公子政的宅平常」,原創技術文章第一時間推送。