持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
写在前面
在最近看了React之后,一直觉得学的懵懵然,虽然很多大佬的手写笔记,写的都很不错,但是我一直没有我想要的那种细无巨细,比如类式组件this指向问题的追根溯源,又比如三大实例属性简写的由来,总之我还是决定做一份事无巨细的笔记。
那就让我们开始吧!
github搜索案例_静态组件
拆分组件
App组件、Search组件、List组件
静态页面拆分
- 将静态页面全部复制到App组件,进行详细拆分。
对class、fontsize、style等关键字进行替换为className、fontSize、style={{}}。
-
创建index.css文件,将全部css样式引入,在List组件中引入样式文件。实现样式加工。
-
将对应的html拆分到子组件内部,在List组件中引入然后使用。
-
引入bootstrap
①在public文件夹下面创建一个css文件添加bootstrap.css
②在index.html中引入bootstrap.css
<link rel="stylesheet" href="/css/bootstrap.css">
- 创建component文件夹
在component文件夹下面创建不同的子组件文件夹。
添加rel="noreferrer"
1.超链接 target="_blank" 要增加 rel="nofollow noopener noreferrer" 来堵住钓鱼安全漏洞。如果你在链接上使用 target="_blank"属性,并且不加上rel="noopener"属性,那么你就让用户暴露在一个非常简单的钓鱼攻击之下。
// a标签下添加 *rel* = "noreferrer"
<a *href*="https://github.com/reactjs" *target*="_blank" *rel* = "noreferrer">
img标签
- img图片 alt属性值最后不要使用photo,picture,img等等。脚手架检查会觉得不规范。
github搜索案例_axios发送请求
修改Search组件
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>
)
}
书写点击事件,获取input输入值
- 使用ref、onclick
search = ()=>{
// 获取用户输入的值
console.log("this.keyWordElement.value")
}
<input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索"/>
<button onClick={this.search}>搜索</button>
- 解构赋值连续写法
//获取用户的输入(连续解构赋值+重命名)
const {keyWordElement:{value:keyWord}} = *this*
console.log(value) // === console.log(keyWord)
console.log(keyWordElement) // Error
Axios请求
- 引入Axios
import axios from 'axios'
- 使用ref,onClick事件函数绑定input标签和button标签。
github 多次访问失效
- 解决方式
开启一个端口号为5000的服务器。对github发送请求的时候,会通过该服务器进行请求。
在多次访问之后无法从github获取数据时,服务器会直接返回本身的数据到客户端。
- 跨域
后端使用cors解决了跨域问题
- 对端口号为5000的服务器配置代理
const proxy = require('http-proxy-middleware')
module.exports = function(app){
app.use(
proxy('/api1',{ //遇见/api1前缀的请求,就会触发该代理配置
target:'http://localhost:5000', //请求转发给谁
changeOrigin:true,//控制服务器收到的请求头中Host的值
pathRewrite:{'^/api1':''} //重写请求路径(必须)
})
)
}
请求地址为:http://localhost:3000/api1/search/users?q=${keyWord}
github搜索案例_展示数据
父组件初始化状态
- 父组件App.js
使用父组件写方法和初始化status,子组件使用方法更新status的手段将获得的用户信息反馈到界面。
// 父组件App
state = {
users:[]
}
saveUsers = (users)=> {
this.setState({users})
}
- 子组件Search组件
调用函数改变状态
<Search updateAppState={this.updateAppState} />
search = ()=>{
//获取用户的输入(连续解构赋值+重命名)
const {keyWordElement:{value:keyWord}} = this
//发送网络请求
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {
//请求成功后通知App更新状态
this.props.saveUsers(response.data.items);
},
error => {
//请求失败后通知App更新状态
this.props.updateAppState({isLoading:false,err:error.message})
}
)
}
子组件渲染
- List组件
<List {...this.state}/>
return (
<div className="row">
{
users.map((userObj)=>{
return (
<div key={userObj.id} className="card">
<a rel="noreferrer" href={userObj.html_url} target="_blank">
<img alt="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/>
</a>
<p className="card-text">{userObj.login}</p>
</div>
)
})
}
</div>
)
github搜索案例_完成案例
为了完善功能,第一次请求,加载中,加载失败等等更新status。
子组件Search调用了2次updateAppState方法,子组件List使用了嵌套三元符分别展示不同页面
初始化状态
- 添加加载中、初次打开页面状态
state = { //初始化状态
users:[], //users初始值为数组
isFirst:true, //是否为第一次打开页面
isLoading:false,//标识是否处于加载中
err:'',//存储请求相关的错误信息
}
- 书写更新状态的函数 updateAppState
//更新App的state
updateAppState = (stateObj)=>{
this.setState(stateObj)
}
- 向Search组件传递 updateAppState
<Search updateAppState={this.updateAppState} />
- 向List组件传递状态属性。
<List {...this.state}/>
- 子组件Search发送请求成功后调用updateAppState函数
//发送请求前通知App更新状态
this.props.updateAppState({isFirst:false,isLoading:true}) //一旦请求就开始改变
- 请求成功后通知App更新状态
//子组件Search
//发送网络请求
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {
//请求成功后通知App更新状态
this.props.updateAppState({isLoading:false,users:response.data.items})
},
error => {
//请求失败后通知App更新状态
this.props.updateAppState({isLoading:false,err:error.message}) // 不能传递对象
}
)
- 不能传递对象
this.props.updateAppState({isLoading:false,err:error.message}) // 不能传递对象
-
根据不同状态时刻,展示不同页面
1.初次打开页面
2.加载中
3.加载失败
4.加载成功
<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 rel="noreferrer" href={userObj.html_url} target="_blank">
<img alt="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/>
</a>
<p className="card-text">{userObj.login}</p>
</div>
)
})
}
</div>
消息订阅与发布技_pubsub
github搜索案例 修改升级
工具库: PubSubJS
下载: npm install pubsub-js --save
使用:
1) import PubSub from 'pubsub-js' //引入
2) PubSub.subscribe('delete', function(data){ }); //订阅
3) PubSub.publish('delete', data) //发布消息
1.将App组件的状态更改到List组件
state = { //初始化状态
users:[], //users初始值为数组
isFirst:true, //是否为第一次打开页面
isLoading:false,//标识是否处于加载中
err:'',//存储请求相关的错误信息
}
const {users,isFirst,isLoading,err} = this.state
2.将App的函数,状态全部清除
export default class App extends Component {
render() {
return (
<div className="container">
<Search/>
<List/>
</div>
)
}
}
3. 引入pubsub并且挂载组件之后订阅消息
- List组件订阅消息
import PubSub from 'pubsub-js'
componentDidMount(){
this.token = PubSub.subscribe('atguigu',(_,stateObj)=>{
this.setState(stateObj)
})
}
4.引入pubsub并且挂载组件之后发布消息
import PubSub from 'pubsub-js'
search = ()=>{
//获取用户的输入(连续解构赋值+重命名)
const {keyWordElement:{value:keyWord}} = this
//发送请求前通知List更新状态
PubSub.publish('atguigu',{isFirst:false,isLoading:true})
//发送网络请求
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {
//请求成功后通知List更新状态
PubSub.publish('atguigu',{isLoading:false,users:response.data.items})
},
error => {
//请求失败后通知App更新状态
PubSub.publish('atguigu',{isLoading:false,err:error.message})
}
)
}
组件将要卸载取消订阅消息
componentWillUnmount(){
PubSub.unsubscribe(this.token)
}
fetch发送请求
发送AJAX请求方式
- xhr对象
2.jQuery(对象xhr封装,使用回调)
传送门:jquery.com/
优点:封装了请求
缺点:产生回调地狱
3.Axios(,对象xhr封装,promise风格)
Fetch
1.window对象自带
2.不用下载,浏览器中直接使用
3.promise风格
-
官方文档: github.github.io/fetch/
-
服务器响应请求和联系服务器是否成功是两回事。
-
联系服务器失败
由于一些不可抗力因素,比如断网
- response.json
返回一个Promise实例对象。
1.获取数据成功
这个Promise对象保存获取的数据
- 获取数据失败
这个Promise对象保存了失败的原因
.then 链式调用
- .then 的返回值就是一个Promise实例对象,可以继续调用 .then。
//发送网络请求---使用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);}
- 发送网络请求---使用fetch发送(优化)
//发送网络请求---使用fetch发送(优化)
try {
const response= await fetch(`/api1/search/users2?q=${keyWord}`)
const data = await response.json()
console.log(data);
PubSub.publish('atguigu',{isLoading:false,users:data.items})
} catch (error) {
console.log('请求出错',error);
PubSub.publish('atguigu',{isLoading:false,err:error.message})
}
fetch特点
1. fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求
2. 老版本浏览器可能不支持
3. 关注分类,只要联系上了服务器就会显示访问成功,而不管是404还是什么(路径有问题)。断网等等不可抗力因数会到时联系服务器失败。
4. Respone原生对象上又一个json方法。