4.1 脚手架配置代理
概要总结
1、使用axios发送异步请求
2、使用node搭建一个服务器
3、安装FeHelper浏览器插件
4、跨域与代理
5、两种配置代理方式
(1)package.json
(2)setupProxy.js
6、http-proxy-middleware属性
7、两种代理的优缺点
一、使用axios发送异步请求
1、安装axios依赖
npm i axios
2、使用axios发送请求
getStudentData = () => { axios.get('').then( response => {console.log('成功了', response.data);}, error => {console.log('失败了', error);} ) } render() { return (
二、使用node搭建一个服务器
1、编写异步请求服务
server1.js:
const express = require("express")
const app = express()
app.use((request, response, next) => {
console.log('有人请求服务器1');
next()
})
app.get('/students', (request, response) => {
const students = [
{id: '001', name: 'tom', age: 18},
{id: '002', name: 'jerry', age: 19},
{id: '003', name: 'tony', age: 20}
]
response.send(students)
})
app.listen(5000, (err) => {
if (!err){
console.log("服务器1启动成功了,请求学生信息地址为:http://localhost:5000/students");
}
else console.log(err);
})
2、启动服务器
启动node服务器只需要用node来执行对应的js文件即可:
3、测试发请求
三、安装FeHelper浏览器插件
该插件主要是可以帮我们把网页的请求数据进行格式化。
四、跨域与代理
1、前端向node发请求
getStudentData = () => {
axios.get('http://localhost:5000/students').then(
response => {console.log('成功了', response.data);},
error => {console.log('失败了', error);}
)
}
2、跨域原理
由于前端工程的端口号是3000,而node服务器的端口号是5000,3000向5000发请求,根据同源策略而言,端口不同就是非同源,就是跨域。
跨域是请求可以发送,但是数据无法返回。
3、代理
客户端端口是3000,服务器是5000。请求发出去的时候,一定会经过ajax引擎,在发送的时候它是放行的,但数据返回的时候,它发现是跨域就直接把数据截住不让返回。
所谓的代理就是一台微小的服务器,它作为中间人跟客户端的端口是一样的,也是3000。现在客户端向代理发请求,大家都是3000端口。代理把它转发到5000服务器,数据返回的时候,虽然端口不一样,但由于代理没有ajax引擎,同源策略没有限制它,所以数据能正常返回到代理上。代理再把数据返回给客户端的时候,由于它们俩的端口号是相同的都是3000,因此放行,数据就能正常返回到客户端上。
五、两种配置代理方式
1、第一种方式:package.json
package.json:
{
"name": "react_staging",
"version": "0.1.0",
"private": true,
"dependencies": { ...... },
"scripts": { ...... },
"eslintConfig": { ...... },
"browserslist": { ...... },
"proxy": "http://localhost:5000"
}
在package.json添加"proxy"代理属性,配置了"http://localhost:5000", 意思是在端口3000的请求会代理到5000那里去,那么在请求的端口号就得改成3000:
App.js:
getStudentData = () => {
axios.get('http://localhost:3000/students').then(
response => {console.log('成功了', response.data);},
error => {console.log('失败了', error);}
)
}
注意:配置了代理,并不代表所有的请求都往代理服务器去发请求,它会先去public目录下寻找是否存在该资源,找不到再把请求发送给代理服务器。例如:请求index.html
App.js:
getStudentData = () => {
axios.get('http://localhost:3000/index.html').then(
response => {console.log('成功了', response.data);},
error => {console.log('失败了', error);}
)
}
2、第二种方式:setupProxy.js
由于package.json的proxy只能配置一个代理,如果有多台服务器需要代理的时候,package.json的方式显然不满足需求。
我们可以通过新建一个setupProxy.js来实现配置多个代理。这个js文件不能用ES6的语法,要用CommonJS的语法来写,因为这个js不是给前端代码执行的,是React脚手架把这个文件加到webpack配置里,而webpack里用的都是CJS语法。
setupProxy:
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(
proxy('/api1',
{
target: 'http://localhost:5000',
changeOrigin: true,
pathRewrite: {'^/api1': ''}
}
),
proxy('/api2',
{
target: 'http://localhost:5001',
changeOrigin: true,
pathRewrite: {'^/api2': ''}
}
)
)
}
App.js:
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>
);
}
注意:旧版的http-proxy-middleware的引入方式是const proxy = require('http-proxy-middleware'),在新版这样引入会导致本地工程无法启动,要换成新版的引入方式:const { createProxyMiddleware } = require('http-proxy-middleware')。
六、http-proxy-middleware属性
1、target:转发的目标地址
2、changeOrigin:控制服务器收到的请求头中Host字段的值
Host字段标识着本次请求从哪发出的,服务器在接收一次请求的时候,它收到很多信息,其中一个最为重要的就是Host字段。
服务器server1.js:
app.use((request, response, next) => {
console.log('有人请求服务器1');
console.log('请求来自于', request.get('Host'));
next()
})
(1)changeOrigin为false
服务器实际上是知道发送请求的源头的ip端口是什么,虽然你做了代理,但如果它针对请求源头做一些严格的处理,那么在changeOrigin为false的情况下,它还是会把发请求的源头ip端口给暴露出来,它就可以做一些限制和拦截。
setupProxy.js:
module.exports = function(app) {
app.use(
createProxyMiddleware('/api1', { // 遇见/api1前缀的请求,就会触发该代理配置
target: 'http://localhost:5000', // 请求转发给谁
pathRewrite: {'^/api1': ''}
})
)
}
(2)changeOrigin为true
changeOrigin属性为true的时候,当服务器想获取发请求的源头ip端口的时候,它告诉服务器发请求的ip端口跟服务器一致,服务器就不知道请求的真实ip端口是什么,这样它就不能做限制,也就可以完美的欺骗服务器。
setupProxy.js:
module.exports = function(app) {
app.use(
createProxyMiddleware('/api1', { // 遇见/api1前缀的请求,就会触发该代理配置
target: 'http://localhost:5000', // 请求转发给谁
changeOrigin: true, // 控制服务器收到的请求头中Host的值
pathRewrite: {'^/api1': ''}
})
)
}
3、pathRewrite:重写请求路径
因为代理它要求请求路径必须带上/api1才能进入代理,但真实的请求路径是没有/api1的,因此进入代理之后,要把这个/api1标识去掉,否则会导致url错误。
服务器server1.js:
const { createProxyMiddleware } = require('http-proxy-middleware')
module.exports = function(app) {
app.use(
createProxyMiddleware('/api1', { // 遇见/api1前缀的请求,就会触发该代理配置
target: 'http://localhost:5000', // 请求转发给谁
changeOrigin: true, // 控制服务器收到的请求头中Host的值
// pathRewrite: {'^/api1': ''} // 重写请求路径
})
)
}
现在确实是已经代理了,但是由于没有去掉/api1,因此url报错404,所以需要借助pathRewrite去掉/api1即可,例如pathRewrite: {'^/api1': ''}。
七、两种代理的优缺点
1、package.json
优点:配置简单,前端请求资源时可以不加任何前缀
缺点:不能配置多个代理
工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000(优先匹配前端资源)
2、setupProxy
优点:可以配置多个代理,可以灵活的控制请求是否走代理
缺点:配置繁琐,前端请求资源时必须加前缀
4.2 github搜索案例_静态组件
概要总结
1、github搜索案例需求
2、编写github静态组件
需求:
1、根据名字搜索,请求接口返回数据列表
一、拆分组件
二、编写静态组件
在拆分组件之前,可以把所有的代码全部写在App.js和App.css,先看效果,然后再一步一步建立组件拆分。
1、Search组件
import React, {Component} from 'react';
export default class Search extends Component {
render() {
return (
<section className="jumbotron">
<h3 className="jumbotron-heading">Search Github Users</h3>
<div>
<input type="text" placeholder="enter the name you search"/>
<button>Search</button>
</div>
</section>
);
}
}
2、List组件
import React, {Component} from 'react';
import './index.css'
export default class List extends Component {
render() {
return (
<div className="row">
<div className="card">
<a href="https://github.com/reactjs" target="_blank">
<img src="https://avatars.githubusercontent.com/u/6412038?v=3" style={{width: '100px'}}/>
</a>
<p className="card-text">reactjs</p>
</div>
<div className="card">
<a href="https://github.com/reactjs" target="_blank">
<img src="https://avatars.githubusercontent.com/u/6412038?v=3" style={{width: '100px'}}/>
</a>
<p className="card-text">reactjs</p>
</div>
<div className="card">
<a href="https://github.com/reactjs" target="_blank">
<img src="https://avatars.githubusercontent.com/u/6412038?v=3" style={{width: '100px'}}/>
</a>
<p className="card-text">reactjs</p>
</div>
<div className="card">
<a href="https://github.com/reactjs" target="_blank">
<img src="https://avatars.githubusercontent.com/u/6412038?v=3" style={{width: '100px'}}/>
</a>
<p className="card-text">reactjs</p>
</div>
<div className="card">
<a href="https://github.com/reactjs" target="_blank">
<img src="https://avatars.githubusercontent.com/u/6412038?v=3" style={{width: '100px'}}/>
</a>
<p className="card-text">reactjs</p>
</div>
</div>
);
}
}
5、App.js统一引入
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>
);
}
}
三、引入第三方库
针对第三方库的引入,一般放在public目录下,在index.html引入。
4.3 github搜索案例_axios发送请求
概要总结
1、绑定搜索点击事件
2、使用axios发送请求
3、搭建node服务器
4、配置代理
一、绑定搜索点击事件
Search.jsx:
search = () => {
// 获取用户的输入(连续解构赋值+重命名)
const {keyWordElement: {value: keyWord}} = this
console.log(keyWord)
// 发送网络请求
}
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}>Search</button>
</div>
</section>
);
}
二、使用axios发送请求
在github中提供了一个搜索的接口:api.github.com/search/user…。
Search.jsx:
search = () => {
// 获取用户的输入(连续解构赋值+重命名)
const {keyWordElement: {value: keyWord}} = this
// 发送网络请求
axios.get(`https://api.github.com/search/users?q=${keyWord}`).then(
response => {console.log('成功了', response.data)},
error => {console.log('失败了', error)}
)
}
三、搭建node服务器
如果频繁向github发请求,它可能会拒绝返回数据。
我们可以在3000与github之间搭建一个node的5000端口服务器,3000请求5000,5000请求github。在5000设定两个接口,一个是真正请求github数据,另一个是假设github不返回数据,它可以伪造一些数据返回。
server.js:
const express = require("express")
const axios = require("axios")
const app = express()
app.get("/search/users", function (req, res) {
const {q} = req.query
axios({
url: 'https://api.github.com/search/users',
params: {q}
}).then(response => {
res.json(response.data)
})
})
app.get("/search/users2", function (req, res) {
res.json({
items: [
{
login: "yyx990803",
html_url: "https://github.com/yyx990803",
avatar_url: "https://avatars3.githubusercontent.com/u/499550?s=460&u=de41ec9325e8a92e281b96a1514a0fd1cd81ad4a&v=4",
id: 1
},
{
login: "ruanyf",
html_url: "https://github.com/ruanyf",
avatar_url: "https://avatars2.githubusercontent.com/u/905434?s=460&v=4",
id: 2
},
......
]
});
});
app.listen(5000, "localhost", (err) => {
if (!err){
console.log("服务器启动成功")
console.log("请求github真实数据请访问:http://localhost:5000/search/users")
console.log("请求本地模拟数据请访问:http://localhost:5000/search/users2")
}
else console.log(err);
})
四、配置代理
Search.jsx:
search = () => {
// 获取用户的输入(连续解构赋值+重命名)
const {keyWordElement: {value: keyWord}} = this
// 发送网络请求
axios.get(`http://localhost:5000/search/users?q=${keyWord}`).then(
response => {console.log('成功了', response.data)},
error => {console.log('失败了', error)}
)
}
由于请求的服务器是node的5000端口服务器,因此必须配置代理。
setupProxy.js:
const { createProxyMiddleware } = require('http-proxy-middleware')
module.exports = function(app) {
app.use(
createProxyMiddleware('/api1', { // 遇见/api1前缀的请求,就会触发该代理配置
target: 'http://localhost:5000', // 请求转发给谁
changeOrigin: true, // 控制服务器收到的请求头中Host的值
pathRewrite: {'^/api1': ''} // 重写请求路径
})
)
}
配置了代理后,请求的url也要改成3000端口,并加上/api1标识符。
Search.jsx:
search = () => {
// 获取用户的输入(连续解构赋值+重命名)
const {keyWordElement: {value: keyWord}} = this
// 发送网络请求
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {console.log('成功了', response.data)},
error => {console.log('失败了', error)}
)
}
4.4 github搜索案例_展示数据
概要总结
1、公共父组件App.js定义状态和方法
2、父组件把状态和方法通过props传给子组件使用
一、公共组件App.js定义状态和方法
在兄弟组件之间如果不用订阅发布机制,那就只能靠它们的公共父组件进行传递通信。因此先在父组件App.js定义好数据状态以及操作状态的方法。
App.js
import React, {Component} from 'react';
import Search from './components/Search';
import List from './components/List';
export default class App extends Component {
state = {users: []} // 初始化状态,users初始值为数组
saveUsers = (users) => {
this.setState({users})
}
render() {
const {users} = this.state
return (
<div className="container">
<Search saveUsers={this.saveUsers} />
<List users={users}/>
</div>
);
}
}
二、父组件把状态和方法通过props传给子组件使用
父组件只需要把数据状态和操作方法分别传给对应的子组件,它们通过props接收之后即可使用。
Search.jsx
import React, {Component} from 'react'
import axios from 'axios'
export default class Search extends Component {
search = () => {
// 获取用户的输入(连续解构赋值+重命名)
const {keyWordElement: {value: keyWord}} = this
// 发送网络请求
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {this.props.saveUsers(response.data.items)},
error => {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}>Search</button>
</div>
</section>
);
}
}
List.jsx
import React, {Component} from 'react';
import './index.css'
export default class List extends Component {
render() {
return (
<div className="row">
{
this.props.users.map((userObj) => {
return (
<div key={userObj.id} className="card">
<a href={userObj.html_url} target="_blank">
<img src={userObj.avatar_url} style={{width: '100px'}}/>
</a>
<p className="card-text">{userObj.login}</p>
</div>
)
})
}
</div>
);
}
}
4.5 github搜索案例_完成案例
概要总结
1、初始页面展示欢迎文本
2、请求前添加loading图标
3、请求失败显示错误信息
一、定义初始化、是否加载、错误信息3个状态
1、初始化状态
在刚加载页面的时候,初始化应该展示欢迎的文本,在搜索之后就隐藏。定义一个isFirst状态来标识是否初始化。
2、是否加载状态
在请求前显示加载图标,请求完成后取消显示。
3、错误信息状态
在请求失败的时候,显示错误信息。
App.js:
export default class App extends Component {
state = { // 初始化状态
users: [], // users初始值为数组
isFirst: true, // 是否为第一次打开页面
isLoading: false, // 标识是否处于加载中
err: '' // 存储请求相关的错误信息
}
// 更新App的state
updateAppState = (stateObj) => {
this.setState(stateObj)
}
render() {
const {users} = this.state
return (
<div className="container">
<Search updateAppState={this.updateAppState} />
<List users={users}/>
</div>
);
}
}
Search.jsx:
search = () => {
// 获取用户的输入(连续解构赋值+重命名)
const {keyWordElement: {value: keyWord}} = this
// 发送请求前通知App更新状态
this.props.updateAppState({isFirst: false, isLoading: true})
// 发送网络请求
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {
// 请求成功后通知App更新状态
this.props.updateAppState({users: response.data.items, isLoading: false})
},
error => {
// 请求失败后通知App更新状态
this.props.updateAppState({isLoading: false, err: error.message})
}
)
}
二、List组件根据状态进行展示
List组件根据这些状态,分别在初始化、请求前、请求成功和请求失败的状态分别对应展示内容。
List.jsx:
render() {
const {users, isFirst, isLoading, err} = this.props
return (
<div className="row">
{
isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :
isLoading ? <h2>Loading...</h2> :
err ? <h2 style={{color: 'red'}}>{err}</h2> :
users.map((userObj) => {
return (
<div key={userObj.id} className="card">
<a href={userObj.html_url} target="_blank">
<img src={userObj.avatar_url} style={{width: '100px'}}/>
</a>
<p className="card-text">{userObj.login}</p>
</div>
)
})
}
</div>
);
}
1、初始化状态
2、请求前状态
3、请求成功状态
4、请求失败状态
4.6 消息订阅与发布机制_pubsub
概要总结
1、PubSub消息订阅与发布机制
2、使用PubSub进行组件之间的通信
一、PubSub使用案例
github地址:github.com/mroderick/P…
PubSub通过subscribe进行订阅,第一个参数是MY TOPIC,它是一个订阅名;第二个参数是一个回调函数,如果有人发布了MY TOPIC的消息,订阅者会调用这个函数。这个回调函数有两个参数,第一个是订阅名msg,第二个参数是发布的数据data。订阅者可以在这个回调函数里拿到数据进行相关逻辑处理。
PubSub通过publish发布消息,第一个参数也是订阅名,第二个参数就是携带的数据。如果其它组件订阅了这个订阅名,那么它就可以拿到数据。
开启订阅之后,它会返回一个订阅的token,相当于setTimeout开启了之后会返回一个定时器id。当我们不想订阅的时候,例如组件要卸载,我们可以通过这个token取消订阅。PubSub通过unsubscribe进行取消订阅,把token传进去即可。
二、改造状态结构
在没有消息订阅机制之前,兄弟组件的数据传递必须借助父组件通过props进行传递,数据状态也只能全部定义在父组件统一控制。有了消息订阅之后,兄弟组件可以直接通信,而状态可以直接定义在具体使用的组件里。
1、父组件App.js
有了发布订阅,父组件的所有数据状态可以全部去掉。
export default class App extends Component {
render() {
return (
<div className="container">
<Search updateAppState={this.updateAppState} />
<List/>
</div>
);
}
}
2、Search组件
本来Search组件需要调用父组件App的方法来改变状态,现在可以使用发布订阅的模式通知List组件改状态。
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
// 发送请求前通知App更新状态
PubSub.publish('atguigu', {isFirst: false, isLoading: true})
// 发送网络请求
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {
// 请求成功后通知List更新状态
PubSub.publish('atguigu', {users: response.data.items, isLoading: false})
},
error => {
// 请求失败后通知List更新状态
PubSub.publish('atguigu', {isLoading: false, err: error.message})
}
)
}
}
3、List组件
List组件首先要把状态定义在这里,其次要在组件初始化的时候就开始订阅。
(1)初始化状态
export default class List extends Component {
state = { // 初始化状态
users: [], // users初始值为数组
isFirst: true, // 是否为第一次打开页面
isLoading: false, // 标识是否处于加载中
err: '' // 存储请求相关的错误信息
}
}
(2)开启订阅
import React, {Component} from 'react';
import PubSub from 'pubsub-js'
export default class List extends Component {
state = { // 初始化状态
users: [], // users初始值为数组
isFirst: true, // 是否为第一次打开页面
isLoading: false, // 标识是否处于加载中
err: '' // 存储请求相关的错误信息
}
componentDidMount() {
PubSub.subscribe('atguigu', (msg, stateObj) => {
this.setState(stateObj)
})
}
}
(3)取消订阅
在组件销毁的时候,需要手动取消订阅,可以在componentWillUnmount钩子操作。
import React, {Component} from 'react';
import PubSub from 'pubsub-js'
export default class List extends Component {
state = { // 初始化状态
users: [], // users初始值为数组
isFirst: true, // 是否为第一次打开页面
isLoading: false, // 标识是否处于加载中
err: '' // 存储请求相关的错误信息
}
componentDidMount() {
PubSub.subscribe('atguigu', (msg, stateObj) => {
this.setState(stateObj)
})
}
componentWillUnmount() {
PubSub.unsubscribe(this.token)
}
}
4.7 fetch发送请求
概要总结
1、fetch简介
2、fetch代替axios请求
3、优化Promise
一、fetch简介
1、文档说明
github地址:github.github.io/fetch/
segmentfault地址:segmentfault.com/a/119000000…
2、与xhr比较
原始的请求发送都是借助于xhr,无论是jQuery还是axios都是基于xhr进行封装。而fetch是一个不需要借助xhr而能发出请求的api,而且它返回的也是Promise。
xhr:
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';
xhr.onload = function() {
console.log(xhr.response);
};
xhr.onerror = function() {
console.log("Oops, error");
};
xhr.send();
fetch:
fetch(url).then(function(response) {
return response.json();
}).then(function(data) {
console.log(data);
}).catch(function(e) {
console.log("Oops, error");
});
二、fetch替换axios请求
1、关注分离思想
Search.jsx:
search = () => { // 获取用户的输入(连续解构赋值+重命名) const {keyWordElement: {value: keyWord}} = this // 发送请求前通知App更新状态 PubSub.publish('atguigu', {isFirst: false, isLoading: true}) // 发送网络请求---使用fetch发送 fetch(/api1/search/users2?q=${keyWord}).then( response => {console.log('成功了', response)}, error => {console.log('失败了', error)} ) }
它确实是发出请求而且是成功返回的,但是这里面并没有我们所想要的数据。
fetch发送请求,并不是单纯的发送然后返回数据,而是它会把这个过程拆解,首先先连接服务器,然后再进行下一步获取数据。
Search.jsx:
fetch(`/api1/search/users2?q=${keyWord}`).then(
response => {console.log('联系服务器成功了', response)},
error => {console.log('联系服务器失败了', error)}
)
(1)连接服务器成功
在上面的案例请求当然是属于成功的,但如果请求是失败的情况下,例如接口请求的url不对、接口名不对、或者参数不对等等,它并不是直接进入error的,它还是会进入response当作成功来处理。因为fetch请求的第一步,只是连接服务器是否成功,只要连上了就认为是成功,至于接口返回报错哪怕是404还是500,它会在response给你返回请求信息。
// 发送网络请求---使用fetch发送
fetch(`/api1/search/users123?q=${keyWord}`).then(
response => {console.log('联系服务器成功了', response)},
error => {console.log('联系服务器失败了', error)}
)
(2)连接服务器失败
error函数是在一些不可抗力的因素才会执行,例如服务器未启动、网络断网了等等。可以在控制台的network模拟断网:
2、获取数据response.json()
在fetch连接成功的返回值里,它的原型上有一个json()方法,调用它会返回一个Promise对象。
// 发送网络请求---使用fetch发送
fetch(`/api1/search/users2?q=${keyWord}`).then(
response => {console.log('联系服务器成功了', response)},
error => {console.log('联系服务器失败了', error)}
)
由于response.json()返回的又是一个Promise,因此我们可以使用Promise的链式调用:
// 发送网络请求---使用fetch发送
fetch(`/api1/search/users2?q=${keyWord}`).then(
response => {
console.log('联系服务器成功了')
return response.json()
},
error => {console.log('联系服务器失败了', error)}
).then(
response => {console.log('获取数据成功了', response)},
error => {console.log('获取数据失败了', error)}
)
三、优化Promise
1、中断链式Promise
如果连接服务器失败,就应该执行完第一个then的error回调函数就结束了,没有必要再往下执行第二个then回调。终止Promise链式可以返回一个全新的Promise对象。
// 发送网络请求---使用fetch发送
fetch(`/api1/search/users2?q=${keyWord}`).then(
response => {
console.log('联系服务器成功了')
return response.json()
},
error => {
console.log('联系服务器失败了', error)
return new Promise(() => {})
}
).then(
response => {console.log('获取数据成功了', response)},
error => {console.log('获取数据失败了', error)}
)
2、统一处理Promise异常
无论是连接服务器失败还是获取数据失败,其实可以统一在catch处理。
// 发送网络请求---使用fetch发送(优化)
fetch(`/api1/search/users2?q=${keyWord}`).then(
response => {
console.log('联系服务器成功了')
return response.json()
}
).then(
response => {console.log('获取数据成功了', response)}
).catch(
error => {console.log('请求出错', error)}
)
3、使用async/await
// 发送网络请求---使用fetch发送(优化)
try {
const response = await fetch(`/api1/search/users2?q=${keyWord}`)
const data = await response.json()
console.log(data)
} catch (error) {
console.log('请求出错', error)
}
4.8 总结github搜索案例
一、github搜索案例相关知识点
1、设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。
2、ES6小知识点:解构赋值+重命名
let obj = {a: {b: 1}}
const {a} = obj; // 传统解构赋值
const {a: {b}} = obj; // 连续解构赋值
const {a: {b: value}} = obj; // 连续解构赋值+重命名
3、消息订阅与发布机制
(1)先订阅,再发布(理解:有一种隔空对话的感觉)
(2)适用于任意组件间通信
(3)要在组件的componentWillUnmount中取消订阅
4、fetch发送请求(关注分离的设计思想)
try {
const response = await fetch(`/api1/search/user2?q=${keyword}`)
const data = await response.json()
console.log(data)
} catch (error) {
console.log('请求出错', error)
}