React笔记2——ReactAjax和React路由
三、ReactAjax
1、脚手架配置
-
首先,安装一个轻量级框架axios yarn add axios
-
在App组件中定义两个简单的按钮分别用来获取学生数据和汽车数据。在测试时,需要打开测试服务器进行监听。 import React, { Component } from 'react'
import axios from 'axios'
export default class App extends Component {
getStudentData = () => {
axios.get('http://localhost:3000/api1/students').then(
response => {console.log('成功了', response.data);},
error => {console.log('失败了', error);}
)
}
getCarData = () => {
axios.get('http://localhost:3000/api2/cars').then(
response => {console.log('成功了', response.data)},
error => {console.log('失败了', error);}
)
}
render() {
return (
<div>
<button onClick={this.getStudentData}>点我获取学生数据</button>
<button onClick={this.getCarData}>点我获取汽车数据</button>
</div>
)
}
}
-
进行脚手架的配置,在src文件夹下创建setupProxy.js文件。因为配置文件一个项目只需要写一次,所以每次只需要把该文件进行复制粘贴即可。配置文件如下:(该项目因为向两个不同的服务器发送了请求,所以设置了两个代理) const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(
proxy('/api1', { //遇见'/api1'前缀的请求,就会触发该代理配置
target: 'http://localhost:5000', //请求转发给谁
changeOrigin: true, //控制服务器收到的响应头中Host字段的值(这条写不写影响不大)
pathRewrite: {'^/api1':''} //重写请求路径(必须的,用于走代理时将'/api1'换成空字符串)
}),
proxy('/api2', {
target: 'http://localhost:5001',
changeOrigin: true,
pathRewrite: {'^/api2':''}
})
)
}
-
代理配置总结
2、github搜索案例
-
将静态页面拆成React的Search、Item、List组件 -
将公共的样式放在App.css中,单独的样式也拆到各自的index.css里(该案例样式较为简单,所以只有List组件中有样式) -
state都定义在App组件中方便进行修改;App组件中定义了一个修改state的方法updateAppState并将其传给了Search组件 import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'
export default class App extends Component {
state = { //初始化状态
users: [], //users初始值为数组
isFirst: true, //是否为第一次打开页面
isLoading: false, //标识是否处于加载中
err: '', //存储请求相关的错误信息
}
updateAppState = (stateObj) => {
this.setState(stateObj)
}
render() {
return (
<div className="container">
<Search updateAppState={this.updateAppState} />
<List {...this.state} />
</div>
)
}
}
-
Search组件
-
导入axios框架用于向github接口发送get请求; -
利用ref核心属性给Search实例对象添加keyWordElement属性,该属性对应着input标签。同时keyWordElement是一个对象,它有一个value属性,该属性对应着input标签中输入的文字。 -
我们将该value属性重新赋值为keyword,并通过axios将关键字作为参数发送出去 import React, { Component } from 'react'
import axios from 'axios'
export default class Search extends Component {
search = () => {
// 获取用户的输入(连续解构赋值+重命名)
const {keyWordElement: {value:keyword}} = this
// 发送请求前通知App更新状态
this.props.updateAppState({isFirst:false, isLoading:true})
// 发送网络请求
// 因为我们端口是3001,也从该端口发送请求,所以可以省略http://localhost:3001
axios.get(`/api1/search/users?q=${keyword}`).then(
response => {
// 请求成功后通知APP更新状态
this.props.updateAppState({isLoading:false, users:response.data.items})
// console.log(response);
},
error => {
// 请求失败后通知APP更新状态
this.props.updateAppState({isLoading: false, err: error.message, users: []})
// console.log('失败了', error);
}
)
}
render() {
return (
<section className="jumbotron">
<h3 className="jumbotron-heading">搜索Github用户</h3>
<div>
<input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键字点击搜索"/>
<button onClick={this.search} >搜索</button>
</div>
</section>
)
}
}
-
List组件
-
App组件通过展开运算符把state所有属性通过props传递给List组件 -
List通过解构赋值拿到必要的属性 -
jsx只能写js表达式,而不能写for/if/switch这类js语句。故条件判断这里使用三元运算符。重点:三元表达式可以进行连续判断,效果相当于if语句 -
利用map对users数组进行处理,每个元素单独返回,并通过props将所有user属性传给Item组件 import React, { Component } from 'react'
import './index.css'
import Item from '../Item'
export default class List extends Component {
render() {
const {isFirst, users, isLoading, err} = this.props
return (
<div className="row">
{
isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :
isLoading ? <h2>加载中...</h2> :
err ? <h2 style={{color:'red'}}>{err}</h2> :
users.map((user) => {
return <Item key={user.id} {...user} />
})
}
</div>
)
}
}
-
Item组件
-
对我们需要的user属性进行解构赋值,包括头像链接、用户仓库链接和用户名 -
将属性通过{}放在需要显示数据的位置 import React, { Component } from 'react'
export default class Item extends Component {
render() {
const {avatar_url, html_url, login} = this.props
return (
<div className="card">
<a rel="noreferrer" href={html_url} target="_blank">
<img alt='avatar' src={avatar_url} style={{width: '100px'}}/>
</a>
<p className="card-text">{login}</p>
</div>
)
}
}
-
代理文件 const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(
proxy('/api1', { //遇见'/api1'前缀的请求,就会触发该代理配置
target: 'http://localhost:5000', //请求转发给谁
changeOrigin: true, //控制服务器收到的响应头中Host字段的值(这条写不写影响不大)
pathRewrite: {'^/api1':''} //重写请求路径(必须的,用于走代理时将'/api1'换成空字符串)
})
)
}
-
总结
- 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。
- ES6小知识点:解构赋值+重命名
let obj = {a:{b:1}} const {a} = obj; //传统解构赋值 const {a:{b}} = obj; //连续解构赋值 const {a:{b:value}} = obj; //连续解构赋值+重命名
3、消息订阅-发布机制
-
使用PubSub
-
安装pubsub-js yarn install pubsub-js
-
使用方法(https://github.com/mroderick/PubSubJS)
- import PubSub from ‘pubsub-js’ //引入
- PubSub.subscribe(‘delete’, function(data){ }); //订阅
- PubSub.publish(‘delete’, data) //发布消息
-
PubSub简化了App组件,因为该方法可以实现兄弟组件间的通信 -
简化的App组件 import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'
export default class App extends Component {
render() {
return (
<div className="container">
<Search />
<List />
</div>
)
}
}
-
Search组件
-
导入PubSub工具库,导入axios框架 -
使用PubSub.publish()方法发布消息,更新兄弟组件List的state import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import axios from 'axios'
export default class Search extends Component {
search = () => {
// 获取用户的输入(连续解构赋值+重命名)
const {keyWordElement: {value:keyword}} = this
// 发送请求前通知List更新状态
this.props.updateAppState({isFirst:false, isLoading:true})
PubSub.publish('atguigu', {isFirst:false, isLoading:true})
// 发送网络请求
// 因为我们端口是3000,也从该端口发送请求,所以可以省略http://localhost:3000
axios.get(`/api1/search/users?q=${keyword}`).then(
response => {
// 请求成功后通知List更新状态
PubSub.publish('atguigu', {isLoading:false, users:response.data.items})
},
error => {
// 请求失败后通知List更新状态
PubSub.publish('atguigu', {isLoading: false, err: error.message, users: []})
}
)
}
render() {
return (
<section className="jumbotron">
<h3 className="jumbotron-heading">搜索Github用户</h3>
<div>
<input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键字点击搜索"/>
<button onClick={this.search} >搜索</button>
</div>
</section>
)
}
}
-
List组件
-
state状态放在List组件中 -
在componentDidMount钩子函数中使用PubSub.subscribe()方法接收消息 -
在componentWillUnmount钩子函数中取消订阅 import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import './index.css'
import Item from '../Item'
export default class List extends Component {
state = { //初始化状态
users: [], //users初始值为数组
isFirst: true, //是否为第一次打开页面
isLoading: false, //标识是否处于加载中
err: '', //存储请求相关的错误信息
}
componentDidMount() {
this.token = PubSub.subscribe('atguigu', (_, stateObj) => {
// console.log('List组件收到数据了', data);
this.setState(stateObj)
})
}
componentWillUnmount() {
PubSub.unsubscribe(this.token)
}
render() {
const {isFirst, users, isLoading, err} = this.state
return (
<div className="row">
{/* jsx只能写js表达式,而不能写for/if/switch这类js语句。故条件判断这里使用三元运算符。三元表达式可以进行连续判断 */}
{
isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :
isLoading ? <h2>加载中...</h2> :
err ? <h2 style={{color:'red'}}>{err}</h2> :
users.map((user) => {
return <Item key={user.id} {...user} />
})
}
</div>
)
}
}
-
Item组件 Item组件还保持跟之前一样。
四、React路由
1.react-router-dom
2.路由的基本使用
-
明确好界面中的导航区、展示区 -
导航区的a标签改为Link标签 <Link to="/xxxxx">Demo</Link>
-
展示区写Route标签进行路径的匹配 <Route path='/xxxx' component={Demo}/>
-
的最外侧包裹了一个或
3.一个简单的路由跳转实例
-
App组件,导入Link和Route import React, { Component } from 'react'
import {Link, Route} from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header"><h2>React Router Demo</h2></div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠<a>跳转不同的页面 */}
{/* <a className="list-group-item" href="./about.html">About</a>
<a className="list-group-item active" href="./home.html">Home</a> */}
{/* 在React中靠路由链接实现切换组件 */}
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Route path="/home" component={Home}/>
<Route path="/about" component={About}/>
</div>
</div>
</div>
</div>
</div>
)
}
}
-
components中的Home和About组件
-
Home组件 import React, { Component } from 'react'
export default class Home extends Component {
render() {
return (
<h3>我是Home的内容</h3>
)
}
}
-
About组件 import React, { Component } from 'react'
export default class About extends Component {
render() {
return (
<h3>我是About的内容</h3>
)
}
}
4.路由组件与一般组件
1.写法不同:
一般组件:<Demo/>
路由组件:<Route path="/demo" component={Demo}/>
2.存放位置不同:
一般组件:components
路由组件:pages
3.接收到的props不同:
一般组件:写组件标签时传递了什么,就能收到什么
路由组件:接收到三个固定的属性
history:
go: ? go(n)
goBack: ? goBack()
goForward: ? goForward()
push: ? push(path, state)
replace: ? replace(path, state)
location:
pathname: "/about"
search: ""
state: undefined
match:
params: {}
path: "/about"
url: "/about"
5.NavLink的使用
? NavLink可以实现路由链接的高亮,通过activeClassName指定样式名。
-
将导入的Link改为NavLink -
若不写activeClassName,则默认对点击的NavLink添加一个active的类。因为这里导入了bootstrap.css样式,里面本身自带有active的样式。 -
若要自定义样式,可在index.html文件中通过style定义样式 <!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>react脚手架</title>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="stylesheet" href="/css/bootstrap.css">
<style>
.leon{
background-color: pink !important;
/* color: white !important; */
}
</style>
</head>
<body>
<div id="root"></div>
</body>
</html>
-
App.jsx组件中的NavLink <NavLink activeClassName="leon" className="list-group-item" to="/home">Home</NavLink>
<NavLink activeClassName="leon" className="list-group-item" to="/about">About</NavLink>
-
自定义MyNavLink标签
-
在components创建MyNavLink文件,在这里对NavLink进行一层封装 -
App组件的属性皆可以通过props传送过来 {/* 在React中靠路由链接实现切换组件 */}
<MyNavLink to="/home">Home</MyNavLink>
<MyNavLink to="/about">About</MyNavLink>
-
MyNavLink标签中的Home、About会作为children属性传送过来,故利用展开运算符,可以将所有属性以简便的方式赋值给NavLink中的属性,如下是MyNavLink组件 import React, { Component } from 'react'
import {NavLink} from 'react-router-dom'
export default class MyNavLink extends Component {
render() {
// console.log(this.props);
return (
<NavLink activeClassName="leon" className="list-group-item" {...this.props}/>
)
}
}
6.Switch的使用
|