如今咱们有了一个属于文章的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官网看一下,至少把基础教程看完。
欢迎关注个人公众号“公子政的宅平常”,原创技术文章第一时间推送。