写在前面
在最近看了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/
- 个人文章: segmentfault.com/a/119000000…
- 服务器响应请求和联系服务器是否成功是两回事。
- 联系服务器失败
由于一些不可抗力因素,比如断网
- 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方法。