react学习笔记2——基于React脚手架与ajax
使用create-react-app创建react应用
react脚手架
- xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
- 包含了所有需要的配置(语法检查、jsx编译、devServer…)
- 下载好了所有相关的依赖
- 可以直接运行一个简单效果
- react提供了一个用于创建react项目的脚手架库: create-react-app
- 项目的整体技术架构为: react + webpack + es6 + eslint
- 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化
创建项目并启动
第一步,全局安装:npm i -g create-react-app
第二步,切换到想创项目的目录,使用命令:create-react-app hello-react
第三步,进入项目文件夹:cd hello-react
第四步,启动项目:npm start
react脚手架项目结构
public ---- 静态资源文件夹
favicon.icon ------ 网站页签图标
index.html -------- 主页面
logo192.png ------- logo图
logo512.png ------- logo图
manifest.json ----- 应用加壳的配置文件
robots.txt -------- 爬虫协议文件
src ---- 源码文件夹
App.css -------- App组件的样式
App.js --------- App组件
App.test.js ---- 用于给App做测试
index.css ------ 样式
index.js ------- 入口文件
logo.svg ------- logo图
reportWebVitals.js
--- 页面性能分析文件(需要web-vitals库的支持)
setupTests.js
---- 组件单元测试的文件(需要jest-dom库的支持)
功能界面的组件化编码流程(通用)
1. 拆分组件: 拆分界面,抽取组件
2. 实现静态组件: 使用组件实现静态页面效果
3. 实现动态组件
3.1 动态显示初始化数据
3.1.1 数据类型
3.1.2 数据名称
3.1.2 保存在哪个组件?
3.2 交互(从绑定事件监听开始)
组件的组合使用-TodoList
功能: 组件化实现此功能
1. 显示所有todo列表
2. 输入文本, 点击按钮显示到列表的首位, 并清除输入的文本
相关代码:
App.jsx
import React, { Component } from "react";
import Header from "./components/Header";
import List from "./components/List";
import Footer from "./components/Footer";
import "./App.css";
export default class App extends Component {//状态在哪里,操作状态的方法就在哪里//初始化数据state = {todos:[{id:'001',name:'吃饭',done:true},{id:'002',name:'睡觉',done:true},{id:'003',name:'打代码',done:false},{id:'004',name:'逛街',done:true},]}//addTodo用于添加一个todo,接收的参数是一个todo对象addTodo = (todoObj) =>{// console.log('App',todoObj);//获取原todosconst {todos} = this.state//追加一个todoconst newTodos = [todoObj,...todos]//更新状态this.setState({todos:newTodos})}//updateTodo用于更新一个todo,接收的参数是todo对象updateTodo = (id,done) =>{//获取状态中的todosconst {todos} = this.state//加工数据,匹配处理数据const newTodos = todos.map((todoObj)=>{if(todoObj.id === id) return {...todoObj,done}else return todoObj})this.setState({todos:newTodos})}//deleteTodo用于删除一个todo对象deleteTodo = (id) =>{//获取原来的todosconst {todos} = this.state//删除指定的id的todo对象const newTodos = todos.filter((todoObj)=>{return todoObj.id !==id})this.setState({todos:newTodos})}//checkAllTodo用于全选checkAllTodo = (done) =>{//获取去原来的todosconst {todos} = this.state//加工数据const newTodos = todos.map((todoObj)=>{return {...todoObj,done}})//更新状态this.setState({todos: newTodos})}//clearAllDone用于清除所有已完成的clearAllDone = () =>{//获取原来的todosconst {todos} = this.state//过滤数据const newTodos = todos.filter((todoObj)=>{return !todoObj.done})//更新状态this.setState({todos:newTodos})}render() {const {todos} = this.statereturn (<div className="todo-container"><div className="todo-wrap">{/* 通过props将函数传递给子组件,子组件在适当的时机调用这个函数,并将参数交给App,然后App将这个参数放入自己的状态里,就会引起App状态的更改,然后调用App里面的render,由于List是App的子组件,就会引发子组件的重新渲染 */}<Header addTodo={this.addTodo} /><List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo} /><Footer todos={todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone} /></div></div>);}
}
App.css
/*base*/
body {background: #fff;
}.btn {display: inline-block;padding: 4px 12px;margin-bottom: 0;font-size: 14px;line-height: 20px;text-align: center;vertical-align: middle;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);border-radius: 4px;
}.btn-danger {color: #fff;background-color: #da4f49;border: 1px solid #bd362f;
}.btn-danger:hover {color: #fff;background-color: #bd362f;
}.btn:focus {outline: none;
}.todo-container {width: 600px;margin: 0 auto;
}
.todo-container .todo-wrap {padding: 10px;border: 1px solid #ddd;border-radius: 5px;
}
Header文件
index.jsx
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {nanoid} from 'nanoid'
import './index.css'
export default class index extends Component {//对接收的props进行类型和必要性的限制static propTypes = {addTodo: PropTypes.func.isRequired}//键盘事件的回调handleKeyUp = (event) =>{//解构赋值获取keyCode,targetconst {keyCode,target} = event//判断是否是回车按键if(keyCode!=13) return//添加的todo名字不能为空if(target.value.trim() === ''){alert('输入不能为空')return}console.log(target.value);//准备好一个todo对象const todoObj = {id:nanoid(),name:target.value,done:false}//将todoObj传递给Appthis.props.addTodo(todoObj)//清空输入target.value = ''}render() {return (<div className="todo-header"><input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认" /></div>)}
}
index.css
/*header*/
.todo-header input {width: 560px;height: 28px;font-size: 14px;border: 1px solid #ccc;border-radius: 4px;padding: 4px 7px;
}.todo-header input:focus {outline: none;border-color: rgba(82, 168, 236, 0.8);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}
List文件
index.jsx
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'
export default class index extends Component {//对接收的props进行类型和必要性的限制static propTypes = {todos: PropTypes.array.isRequired,updateTodo: PropTypes.func.isRequired,deleteTodo: PropTypes.func.isRequired,}render() {const {todos,updateTodo,deleteTodo} = this.propsreturn (<ul className="todo-main">{todos.map(todo=>{// return <Item key={todo.id} id={todo.id} name={todo.name} done={todo.done} />return <Item key={todo.id} {...todo} updateTodo={updateTodo} deleteTodo={deleteTodo} />})}</ul>)}
}
index.css
/*main*/
.todo-main {margin-left: 0px;border: 1px solid #ddd;border-radius: 2px;padding: 0px;
}.todo-empty {height: 40px;line-height: 40px;border: 1px solid #ddd;border-radius: 2px;padding-left: 5px;margin-top: 10px;
}
Item文件
index.jsx
import React, { Component } from 'react'
import './index.css'
export default class index extends Component {state = {mouse:false} //标识鼠标移入、移出//标识鼠标移入、移出的回调handleMouse=(flag)=>{return ()=>{this.setState({mouse:flag})}}//勾选、取消勾选某一个todo的回调handleCheck = (id) =>{return (event)=>{console.log(id,event.target.checked);this.props.updateTodo(id,event.target.checked)}}//删除一个todo的回调handleDelete = (id) =>{console.log('通知App删除',id);if(window.confirm('确定删除吗?')){this.props.deleteTodo(id)}}render() {const {id,name,done} = this.propsconst {mouse} = this.statereturn (<li style={{backgroundColor: mouse ? '#ddd' : 'white'}} onMouseLeave={this.handleMouse(false)} onMouseEnter={this.handleMouse(true)}><label><input type="checkbox" checked={done} onChange={this.handleCheck(id)} /><span>{name}</span></label><button onClick={()=>this.handleDelete(id)} className="btn btn-danger" style={{ display: mouse ? 'block' : "none" }}>删除</button></li>)}
}
index.css
/*item*/
li {list-style: none;height: 36px;line-height: 36px;padding: 0 5px;border-bottom: 1px solid #ddd;
}li label {float: left;cursor: pointer;
}li label li input {vertical-align: middle;margin-right: 6px;position: relative;top: -1px;
}li button {float: right;display: none;margin-top: 3px;
}li:before {content: initial;
}li:last-child {border-bottom: none;
}
Footer文件
index.jsx
import React, { Component } from 'react'
import './index.css'
export default class index extends Component {//全选checkbox的回调handleCheckedAll=(event)=>{this.props.checkAllTodo(event.target.checked)}//清除已完成任务的回调handleClearAllDone = () => {this.props.clearAllDone()}render() {const {todos} = this.props//已完成的个数//pre 上一次的返回值,0 初始值,current当前的todo对象//第一次调用的时候没有上一次,所以pre初始是0const doneCount = todos.reduce((pre,todo)=>pre + (todo.done ? 1 : 0),0)console.log(doneCount);//总数const total = todos.lengthreturn (<div className="todo-footer"><label>{/* defaultChecked只在第一次生效 */}<input type="checkbox" onChange={this.handleCheckedAll} checked={doneCount === total && total!==0 ? true : false} /></label><span><span>已完成{doneCount}</span> / 全部{total}</span><button onClick={this.handleClearAllDone} className="btn btn-danger">清除已完成任务</button></div>)}
}
index.css
/*footer*/
.todo-footer {height: 40px;line-height: 40px;padding-left: 6px;margin-top: 5px;
}.todo-footer label {display: inline-block;margin-right: 20px;cursor: pointer;
}.todo-footer label input {position: relative;top: -1px;vertical-align: middle;margin-right: 5px;
}.todo-footer button {float: right;margin-top: 5px;
}
React ajax
理解
前置说明
- React本身只关注于界面, 并不包含发送ajax请求的代码
- 前端应用需要通过ajax请求与后台进行交互(json数据)
- react应用中需要集成第三方ajax库(或自己封装)
常用的ajax请求库
- jQuery: 比较重, 如果需要另外引入不建议使用
- axios: 轻量级, 建议使用
-
- 封装XmlHttpRequest对象的ajax
- promise风格
- 可以用在浏览器端和node服务器端
-
App.jsx
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);})}geCarData = () =>{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.geCarData}>点我获取汽车数据</button></div>)}
}
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": "" }, //重写请求路径(必须)
// }),
// proxy("/api2", {
// target: "http://localhost:5001",
// changeOrigin: true,
// pathRewrite: { "^/api2": "" },
// })
// );
// };
const { createProxyMiddleware } = require("http-proxy-middleware");module.exports = function (app) {app.use("/api1",createProxyMiddleware({target: "http://localhost:5000",changeOrigin: true,pathRewrite: { "^/api1": "" },}));app.use("/api2",createProxyMiddleware({target: "http://localhost:5001",changeOrigin: true,pathRewrite: { "^/api2": "" },}));
};
笔记:
# react 脚手架配置代理总结## 方法一> 在 package.json 中追加如下配置```json
"proxy":"http://localhost:5000"
```说明:1. 优点:配置简单,前端请求资源时可以不加任何前缀。
2. 缺点:不能配置多个代理。
3. 工作方式:上述方式配置代理,当请求了 3000 不存在的资源时(先去本地路径获取),那么该请求会转发给 5000 (优先匹配前端资源)## 方法二1. 第一步:创建代理配置文件```在src下创建配置文件:src/setupProxy.js```2. 编写 setupProxy.js 配置具体代理规则:```jsconst proxy = require("http-proxy-middleware");module.exports = function (app) {app.use(proxy("/api1", {//api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)target: "http://localhost:5000", //配置转发目标地址(能返回数据的服务器地址)changeOrigin: true, //控制服务器接收到的请求头中host字段的值/*changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000changeOrigin默认值为false,但我们一般将changeOrigin值设为true*/pathRewrite: { "^/api1": "" }, //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)}),proxy("/api2", {target: "http://localhost:5001",changeOrigin: true,pathRewrite: { "^/api2": "" },}));};```说明:1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
2. 缺点:配置繁琐,前端请求资源时必须加前缀。
axios
文档
GitHub - axios/axios: Promise based HTTP client for the browser and node.js
相关API
1.GET请求
axios.get('/user?ID=12345').then(function (response) {console.log(response.data);}).catch(function (error) {console.log(error);});axios.get('/user', {params: {ID: 12345}}).then(function (response) {console.log(response);}).catch(function (error) {console.log(error);});
2.POST请求
axios.post('/user', {firstName: 'Fred',lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
案例—github用户搜索
请求地址: https://api.github.com/search/users?q=xxxxxx
代码:
github搜索案例_axios:
App.jsx
import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'
import './App.css'
export default class App extends Component {state = { //初始化状态users: [], //users初始值为数组isFirst: true, //是否为第一次打开页面isLoading: false, //标识是否处于加载中err: '',//存储请求相关的错误信息}//更新App的stateupdateAppState = (stateObj) =>{this.setState(stateObj)}render() {return (<div className="container"><Search updateAppState={this.updateAppState}/><List {...this.state}/></div>)}
}
setupProxy.js
//这种写法会导致localhost拒绝访问的问题
// const proxy = require("http-proxy-middleware");
// module.exports = function (app) {
// app.use(
// proxy("/api1", {
// //遇见/api1前缀的请求,就会触发该代理配置
// target: "http://localhost:5000", //请求转发给谁
// changeOrigin: true, //控制服务器收到的请求头中Host字段的值
// pathRewrite: { "^/api1": "" }, //重写请求路径(必须)
// })
// );
// };
const { createProxyMiddleware } = require("http-proxy-middleware");module.exports = function (app) {app.use("/api1",createProxyMiddleware({target: "http://localhost:5000",changeOrigin: true,pathRewrite: { "^/api1": "" },}));
};
components
Search/index.jsx:
import React, { Component } from 'react'
import axios from 'axios'export default class index extends Component {search = () =>{//获取用户输入(连续解构赋值+重命名)// console.log(this.keyWorldElement.value);//keyWorldElement没有被定义,{value:keyword}解构出来之后可以重新赋值const {keyWorldElement:{value:keyword}} = thisconsole.log(keyword);//发送请求前通知App要更新状态this.props.updateAppState({isFirst:false,isLoading:true});//发送网络请求 后端解决跨域:cors// http://localhost:3000可以省略axios.get(`/api1/search/users?q=${keyword}`).then(response=>{// console.log('成功了', response.data);//请求成功后通知App更新状态this.props.updateAppState({isLoading:false,users:response.data.items})},error=>{console.log('失败了', error);//请求失败后通知App更新状态this.props.updateAppState({isLoading:false,err:error.message})})}render() {return (<section className="jumbotron"><h3 className="jumbotron-heading">搜索Github用户</h3><div><input ref={c => this.keyWorldElement = c} type="text" placeholder="输入关键词点击搜索"/> <button onClick={this.search}>搜索</button></div></section>)}
}
List/index.jsx
import React, { Component } from 'react'
import './index.css'
export default class index extends Component {render() {const {users,isFirst,isLoading,err} = this.propsreturn (<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="https://github.com/reactjs" target="_blank"><img alt="head_portrait" src={userObj.avatar_url} style={{width: '100px'}}/></a><p className="card-text">{userObj.login}</p></div>)})}</div>)}
}
List/index.css
.album {min-height: 50rem; /* Can be removed; just added for demo purposes */padding-top: 3rem;padding-bottom: 3rem;background-color: #f7f7f7;
}.card {float: left;width: 33.333%;padding: .75rem;margin-bottom: 2rem;border: 1px solid #efefef;text-align: center;
}.card > img {margin-bottom: .75rem;border-radius: 100px;
}.card-text {font-size: 85%;
}
消息订阅-发布机制
- 工具库: PubSubJS
- 下载: npm install pubsub-js --save
- 使用:
1.import PubSub from 'pubsub-js' //引入
2.PubSub.subscribe('delete', function(data){ }); //订阅
3.PubSub.publish('delete', data) //发布消息
github搜索案例_pubsub:
App.jsx
import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'
import './App.css'
export default class App extends Component {//更新App的stateupdateAppState = (stateObj) =>{this.setState(stateObj)}render() {return (<div className="container"><Search updateAppState={this.updateAppState}/><List {...this.state}/></div>)}
}
Search/index.jsx
import React, { Component } from 'react'
import axios from 'axios'
import PubSub from 'pubsub-js'export default class index extends Component {search = () =>{//发布的消息,就像是经常需要改变的数据,而其他组件需要根据这个会动态改变的数据来进行动态展示console.log('Search组件发布消息了')//获取用户输入(连续解构赋值+}重命名)console.log(this.keyWorldElement.value);//keyWorldElement没有被定义,{value:keyword}解构出来之后可以重新赋值const {keyWorldElement:{value:keyword}} = thisconsole.log(keyword);//发送请求前通知List要更新状态//谁发送数据就在什么地方发送消息PubSub.publish('atguigu',{isFirst:false,isLoading:true})//发送网络请求 后端解决跨域:corsaxios.get(`/api1/search/users?q=${keyword}`).then(response=>{console.log('成功了', response.data);//请求成功后通知List更新状态PubSub.publish('atguigu',{isLoading:false,users:response.data.items})},error=>{console.log('失败了', error);//请求失败后通知App更新状态PubSub.publish('atguigu',{isLoading:false,err:error.message})})}render() {return (<section className="jumbotron"><h3 className="jumbotron-heading">搜索Github用户</h3><div><input ref={c => this.keyWorldElement = c} type="text" placeholder="输入关键词点击搜索"/> <button onClick={this.search}>搜索</button></div></section>)}
}
List/index.jsx
import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import './index.css'
export default class index extends Component {state = { //初始化状态users: [], //users初始值为数组isFirst: true, //是否为第一次打开页面isLoading: false, //标识是否处于加载中err: '',//存储请求相关的错误信息}componentDidMount(){//谁接收数据就在什么地方订阅消息//只要写了这个订阅消息之后,一旦有人发布这个消息就会收到数据this.token = PubSub.subscribe('atguigu',(_, stateObj)=>{// console.log('List组件收到数据了',stateObj);this.setState(stateObj)})}componentWillUnmount(){PubSub.unsubscribe(this.token)}render() {const {users,isFirst,isLoading,err} = this.statereturn (<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="https://github.com/reactjs" target="_blank"><img alt="head_portrait" src={userObj.avatar_url} style={{width: '100px'}}/></a><p className="card-text">{userObj.login}</p></div>)})}</div>)}
}
扩展:Fetch
文档
- https://github.github.io/fetch/
2.https://segmentfault.com/a/1190000003810652
特点
- fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求
- 老版本浏览器可能不支持
相关API
- GET请求
fetch(url).then(function(response) {return response.json()}).then(function(data) {console.log(data)}).catch(function(e) {console.log(e)});
2.POST请求
fetch(url, {method: "POST",body: JSON.stringify(data),}).then(function(data) {console.log(data)}).catch(function(e) {console.log(e)})
github搜索案例_fetch:
App.jsx
import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'
import './App.css'
export default class App extends Component {//更新App的stateupdateAppState = (stateObj) =>{this.setState(stateObj)}render() {return (<div className="container"><Search updateAppState={this.updateAppState}/><List {...this.state}/></div>)}
}
Search/index.jsx
import React, { Component } from "react";
// import axios from 'axios'
import PubSub from "pubsub-js";export default class index extends Component {search = async () => {//发布的消息,就像是经常需要改变的数据,而其他组件需要根据这个会动态改变的数据来进行动态展示console.log("Search组件发布消息了");//获取用户输入(连续解构赋值+}重命名)console.log(this.keyWorldElement.value);//keyWorldElement没有被定义,{value:keyword}解构出来之后可以重新赋值const {keyWorldElement: { value: keyword },} = this;console.log(keyword);//发送请求前通知List要更新状态//谁发送数据就在什么地方发送消息PubSub.publish("atguigu", { isFirst: false, isLoading: true });// #region 发送网络请求————使用axios发送//发送网络请求 后端解决跨域:cors// axios.get(`/api1/search/users2?q=${keyword}`).then(// response=>{// console.log('成功了', response.data);// //请求成功后通知List更新状态// PubSub.publish('atguigu',{isLoading:false,users:response.data.items})// },// error=>{// console.log('失败了', error);// //请求失败后通知App更新状态// PubSub.publish('atguigu',{isLoading:false,err:error.message})// }// )//#endregion//发送网络请求————使用fetch发送(未优化)// fetch(`/api1/search/users2?q=${keyword}`).then(// //能得到状态码是因为服务器告诉的,所以不管路径是否错误,只要服务器告诉状态码了,最后都是连接服务器成功了// //成功请求后,获取数据成功后,这个调用response上面的json()方法,得到一个promise实例// //如果联系服务器成功了,获取数据营业成功了,那么这个promise的状态也就变成了"fulfilled",而且里面保存着需要的数据// //如果连接服务器成功了,但是获取数据失败了,那么这个promise的状态也就是失败的状态,里面保存着失败的原因// response => {// console.log('联系服务器成功了');// return response.json()// },// //断网的时候,出现错误// error=>{// console.log('联系服务器失败了',error);// 如果服务器都没有连接上,最好不好展示获取数据是成功还是失败,而是直接中断promise链// return一个初始化状态的promise// return new Promise(()=>{})// }// ).then(// response=>{// console.log('获取数据成功了', response);// },// error=>{// console.log('获取数据失败了',error);// }// )//发送网络请求————使用fetch发送(优化)// fetch(`/api1/search/users2?q=${keyword}`).then(// response => {// console.log('联系服务器成功了');// return response.json()// },// ).then(// response=>{// console.log('获取数据成功了', response);// },// ).catch(// 统一处理错误// error=>{console.log('请求出错', error);}// )try {const response = await fetch(`/api1/search/users2?q=${keyword}`);const data = await response.json();PubSub.publish('atguigu',{isLoading:false,users:data.items})} catch (error) {console.log("请求出错", error);PubSub.publish('atguigu',{isLoading:false,users:error.message})}};render() {return (<section className="jumbotron"><h3 className="jumbotron-heading">搜索Github用户</h3><div><inputref={(c) => (this.keyWorldElement = c)}type="text"placeholder="输入关键词点击搜索"/> <button onClick={this.search}>搜索</button></div></section>);}
}// const xhr = new XMLHttpRequest()
// xhr.open()
// xhr.send()
// ...
// jQuery将这些方法封装好了
// $(_,_)就可以直接发送请求
// 有一个问题,可能会产生回调地狱,jQuery发送ajax请求都靠回调函数来进行沟通
//成功就调用成功的回调,失败调用失败的回调
//如果是这样的需求,第一次成功了,在发第二次,第二次成了,再发第三次,第三次成了,再发第四次....很容易形成回调地域//axios回调地域能解决,是promise风格的// jQuery与axios都是对xhr的封装,他们都需要下载引入,都是第三方的对xhr的封装// fetch也能发送请求,不用xhr,不是一个库,内置就有,window上面就有,fetch与xhr是并列的,
// 本身就是promise风格的
//优势:不是第三方库,不需要下载,直接使用,有浏览器就能使用
List/index.jsx
import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import './index.css'
export default class index extends Component {state = { //初始化状态users: [], //users初始值为数组isFirst: true, //是否为第一次打开页面isLoading: false, //标识是否处于加载中err: '',//存储请求相关的错误信息}componentDidMount(){//谁接收数据就在什么地方订阅消息//只要写了这个订阅消息之后,一旦有人发布这个消息就会收到数据this.token = PubSub.subscribe('atguigu',(_, stateObj)=>{// console.log('List组件收到数据了',stateObj);this.setState(stateObj)})}componentWillUnmount(){PubSub.unsubscribe(this.token)}render() {const {users,isFirst,isLoading,err} = this.statereturn (<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="https://github.com/reactjs" target="_blank"><img alt="head_portrait" src={userObj.avatar_url} style={{width: '100px'}}/></a><p className="card-text">{userObj.login}</p></div>)})}</div>)}
}
相关文章:
react学习笔记2——基于React脚手架与ajax
使用create-react-app创建react应用 react脚手架 xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目 包含了所有需要的配置(语法检查、jsx编译、devServer…)下载好了所有相关的依赖可以直接运行一个简单效果 react提供了一个用于创建react项…...
nim模块教程
导入一个模块 如果我们想要导入一个模块,并且和它的所有函数,我们要做的是写import <moduleName>在我们的文件里,这通常是在文件顶部进行的,这样我们就可以很容易地看到我们的代码使用了什么。 创建一个模块 first.nim …...
雅马哈SMT贴片机高效精密制造解析
内容概要 作为电子制造领域的核心装备,雅马哈SMT贴片机通过集成高速运动控制、智能视觉识别与模块化供料三大技术体系,构建了精密电子元件贴装的工业化解决方案。其YSM系列设备在5G通讯模组、汽车电子控制器及智能穿戴设备等场景中,实现了每…...
审计专员简历模板
模板信息 简历范文名称:审计专员简历模板,所属行业:其他 | 职位,模板编号:KSJYVR 专业的个人简历模板,逻辑清晰,排版简洁美观,让你的个人简历显得更专业,找到好工作。希…...
npm宿主依赖、宿主环境依赖(peerDependencies)(指由宿主环境提供的依赖)
文章目录 宿主环境依赖详解基本概念工作原理应用场景插件开发UI组件库 与其他依赖类型对比npm不同版本处理差异npm v3-v6npm v7 实际应用示例React插件开发 解决宿主依赖问题 宿主环境依赖详解 基本概念 宿主环境依赖(peerDependencies)是指包声明自身…...
Android Kotlin 项目集成 Firebase Cloud Messaging (FCM) 全攻略
Firebase Cloud Messaging (FCM) 是 Google 提供的跨平台消息推送解决方案。以下是在 Android Kotlin 项目中集成 FCM 的详细步骤。 一、前期准备 1. 创建 Firebase 项目 访问 Firebase 控制台点击"添加项目",按照向导创建新项目项目创建完成后&#x…...
游戏引擎学习第252天:允许编辑调试值
回顾并为今天的工作设定目标 我们处理了调试值(debug value)的编辑功能。我们希望实现可以在调试界面中编辑某些值,为此还需要做一些额外的工作。 我们的问题在于:当某个调试值被编辑时,我们需要把这个“编辑”的操作…...
支持selenium的chrome driver更新到136.0.7103.49
最近chrome释放新版本:136.0.7103.49 如果运行selenium自动化测试出现以下问题,是需要升级chromedriver才可以解决的。 selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only su…...
cPanelWHM 的 AutoSSL
在 cPanel&WHM 的第58版本中,开始增加了AutoSSL,这是一项非常棒的新功能。 什么是 AutoSSL? AutoSSL 是为了解决每个使用 cPanel&WHM 用户的最大难题:SSL 证书的安装和续期。有了 AutoSSL,这个问题就不再是问…...
MySQL数据同步之Canal讲解
文章目录 1 Canal搭建1.1 简介1.1.1 概述1.1.2 优点1.1.3 作用&核心组件 1.2 搭建 Canal1.2.1 准备工作1.2.1.1 检查配置1.2.1.2 MySQL配置 1.2.2 下载并安装 Canal1.2.3 配置 Canal Server1.2.3.1 全局配置1.2.3.2 实例配置1.2.3.3 配置目标系统1.2…...
完整迁移物理机Windows XP到PVE8
计划对2007年部署的windows_xp_professional _service_pack_2_x86系统主机,进行重新部署,由于确实环境包和软件包,无法从头部署,只能考虑带系统环境迁移。原主机年代台久远(1Ghz处理器,1G内存)G…...
量子加密通信:打造未来信息安全的“铜墙铁壁”
在数字化时代,信息安全已成为全球关注的焦点。随着量子计算技术的飞速发展,传统的加密算法面临着前所未有的挑战。量子计算机的强大计算能力能够轻易破解现有的加密体系,这使得信息安全领域急需一种全新的加密技术来应对未来的威胁。量子加密…...
11.多边形的三角剖分 (Triangulation) : 画廊问题
目录 1.Methodology 编辑2. Definition 3. Lower & Upper Bound 4.Hardness 5.Approximation & Classification 6. Necessity of floor(n/3) 1.Methodology 多边形三角剖分 点集三角剖分 2. Definition 假设存在一个艺术馆,里面存在很大艺术品需…...
[蓝桥杯 2023 国 Python B] 划分 Java
import java.util.*;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int[] arr new int[41];int sum 0;for (int i 1; i < 40; i) {arr[i] sc.nextInt();sum arr[i];}sc.close();int target sum / 2; // 最接近的两…...
计算机网络——HTTP/IP 协议通俗入门详解
HTTP/IP 协议通俗入门详解 一、什么是 HTTP 协议?1. 基本定义2. HTTP 是怎么工作的? 二、HTTP 协议的特点三、HTTPS 是什么?它和 HTTP 有啥区别?1. HTTPS 概述2. HTTP vs HTTPS 四、HTTP 的通信过程步骤详解: 五、常见…...
渗透测试中的那些“水洞”:分析与防御
1. Nginx 版本泄露 风险分析: Nginx 默认会在响应头中返回 Server: nginx/x.x.x,攻击者可利用该信息匹配已知漏洞进行攻击。 防御措施: 修改 nginx.conf 配置文件,隐藏版本信息:server_tokens off;使用 WAF 进行信息…...
攻防世界 - Misc - Level 3 | 3-1
🌟 关注这个靶场的其它相关笔记:CTF 靶场笔记 —— 攻防世界(XCTF) 过关思路合集 0x01:考点速览 本题考察的是 Misc 中的流量分析题,想要通过此关,你需要具备以下技术: 会通过 010 …...
安装linux下的idea
1.有可能传不了文件 2.按这个包里的流程装 通过网盘分享的文件:idea旗下所有产品.txt 链接: https://pan.baidu.com/s/1kHHkW3DB3z3a6CG0qnMkWA?pwdgg3f 提取码: gg3f...
【音频】基础知识
1、原始数据 1)音频信号:声音是一种机械波,经过麦克风等设备转化为电信号,再经过模数转换(ADC)变成数字信号,这个数字信号就是音频信号。 2)音频信号的参数: 采样率:一秒钟内对音频的模拟信号采样的个数; 8000Hz:主要用于电话通信 、满足基本的语音通信需求,同时…...
系统思考:企业效率提升关键
最近在辅导一家企业时,我们一起画出了这张图。老板说:“我每天都在救火,员工效率不高,我只能不断加班加点,亲自盯、亲自跑、亲自上阵……” 但图画出来才发现,问题不是出在员工不够努力,也不是老…...
MySQL 查找指定表名的表的主键
原理 SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME 表名 AND CONSTRAINT_NAME PRIMARY方法 public static String getPk(String tableName) {String sql "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TA…...
华为eNSP:IS-IS认证
一、什么是IS-IS认证? 华为eNSP中的IS-IS认证 IS-IS认证是华为eNSP网络中用于保障中间系统到中间系统(IS-IS)协议通信安全性的核心机制,通过身份验证和数据完整性校验防止非法路由信息注入或篡改。其实现方式与关键特性如下&…...
qemu(4) -- qemu-system-arm使用
1. 前言 参考网上的资料,使用qemu中的vexpress_a9板子,跑一下Linux环境。 2. 源码 2.1 u-boot 可以到U-Boot官网下载对应的源码,我下载的是u-boot-2025.04-rc5.tar.gz,大约24MB。 3.2 linux 可以到The Linux Kernel Archive…...
JavaScript基础-递增和递减运算符
在JavaScript编程中,递增()和递减(--)运算符是操作数值变量的快捷方式。它们能够简洁地对变量值进行加一或减一的操作。尽管看似简单,但正确理解这两种运算符的不同使用方式(前缀与后缀)对于编写高效且无误的代码至关重要。本文将…...
解决Win10虚拟机“网络连接不上”,“Ethernet0 网络电缆被拔出”的问题
一、情景引入 今天用Win10虚拟机打开浏览器发现: 很奇怪,平常都没有这个问题。 二、检查网络状态 点击更改适配器选项,发现如下: 三、解决问题 打开任务管理器,点击服务,搜索栏搜索:VM …...
【Redis】String详细介绍及其应用场景
文章目录 String类型存储方式set命令get命令mset命令mget命令setnx命令setex和psetex命令incr和decr命令系列append命令--raw选项让redis尝试将二进制数据翻译 getrange命令setrange命令strlen命令字符串类型命令小结string内部的编码方式string类型的典型应用场景1.RedisMySQL…...
C++负载均衡远程调用学习之消息路分发机制
目录 1.LARV0.5-TCP_server链接管理的功能实现及测试 2.LARV0.6 3.LARV0.6 4.LARV0.6 5.LARV0.6-tcp_server集成 6.LARV0.6-tcp_server集成消息路由分发机制总结 7.LARV0.6回顾 1.LARV0.5-TCP_server链接管理的功能实现及测试 ### 16.2 完成Lars Reactor V0.12开发 ###…...
实现了一个基于寄存器操作STM32F103C8t6的工程, 并实现对PA1,PA2接LED正极的点灯操作
#include "stm32f10x.h"// 基于寄存器开发的项目了 int main(){RCC->APB2ENR 0x00000004; // 开启时钟GPIOA->CRL 0x00003330; // 配置引脚 // 0011 0011 0000GPIOA->ODR 0x0000000E; // 1110while(1){} }...
Python字典(dict)详解:从创建到操作全掌握
前言 字典是可变容器,可存储任意类型对象 字典以键(key)-值(value)对的形式进行映射,键值对用冒号分割,对之间用逗号分割 d {key1 : value1, key2 : value2, key3 : value3 } 字典的数据是无序的 字典的键只能用不可变类型,且…...
UDP数据包和TCP数据包的区别;网络编程套接字;不同协议的回显服务器
目录 一、UDP 数据包与 TCP 数据包的区别: 连接方面: 传输方面: 面向对象: 双工模式: 二、UDP 网络编程套接字;基于 UDP 协议的回显服务器: 1. UDP 数据报套接字核心类 DatagramSocket &…...
Python 应用异常追踪实战:如何集成 Sentry 进行高效错误监控
Python 应用异常追踪实战:如何集成 Sentry 进行高效错误监控 引言 在现代应用开发中,异常处理和错误监控至关重要。一个小的运行时错误可能会导致整个系统崩溃,而难以发现的逻辑漏洞可能长期影响用户体验。为了提升代码的稳定性,我们需要一个高效的异常监控机制,以便能够…...
【数据结构】--- 双向链表的增删查改
前言: 经过了几个月的漫长岁月,回头时年迈的小编发现,数据结构的内容还没有写博客,于是小编赶紧停下手头的活动,补上博客以洗清身上的罪孽 目录 前言: 概念: 双链表的初始化 双链表的判空 双链表…...
【C语言练习】014. 使用数组作为函数参数
014. 使用数组作为函数参数 014. 使用数组作为函数参数示例1:使用数组作为函数参数并修改数组元素函数定义输出结果 示例2:使用数组作为函数参数并计算数组的平均值函数定义输出结果 示例3:使用二维数组作为函数参数函数定义输出结果 示例4&a…...
本地服务器备份网站数据,本地服务器备份网站的操作步骤
本地服务器备份网站数据的完整操作指南 一、明确备份需求与目标 核心备份对象 网站文件: 上传的媒体文件(图片、视频、PDF等) 配置文件(如.htaccess、wp-config.php) 附加内容(根据需求选择ÿ…...
机器学习Day15 LightGBM算法
浅谈LightGBM算法:我们之前讲的集成学习算法分为三要素吧,就是形式,损失函数,优化方法,但是LightGBM算法并没有固定的形式,它主要是针对具体算法给出一些优化,它更像是前向分步算法一样,像一个框…...
算法查找目录
1. 基础数据结构 数组与链表 动态数组 实现与自动扩容机制均摊分析ArrayList/Vector实现 单向链表 基本操作(插入、删除、查找)链表反转环检测(Floyd判圈算法) 双向链表 插入删除操作优化双向遍历优势边界情况处理 循环链表 约瑟夫环问题单向循环链表双向循环链表 跳表 基本原…...
【HarmonyOS】作业三 UI
目录 一. 单选题(共10题,10分) 1. (单选题, 1分)关于Tabs组件页签的位置设置,下面描述错误的是 2. (单选题, 1分)下面哪个组件不能包含子组件? 3. (单选题, 1分)ArkTS语言的实现计数器功能的组件名称是以下哪个? 4. (单选题…...
2025五一杯数学建模B题:矿山数据处理问题,详细问题分析,思路模型
一、尊重原创:详细内容文末名片获取 二、数据文件解读 (一)数据文件 1:矿山监测一维数值样例数据.csv 想象一下,这就像是一本简单的记录册,里面记录着一组一维数值序列,每个数据点如同册子里的…...
ES6-Set-Map对象小记
Set 对象 添加元素 set.add(value)常用方法 方法描述has()判断 Set 对象中特定元素是否存在delete()从 Set 对象中删除指定元素clear()清空 Set 对象 遍历方法 很容易想到使用set.forEach(callBackFn, thisArg)方法来进行遍历,其中callBackFn回调的形式如下&am…...
WGCLOUD使用 - 如何监控RabbitMQ运行参数
WGCLOUD是一款开源免费的运维监控软件,开箱即用,实用轻量,高效简单。 RabbitMQ指标数据的采集工作是由server-backup来做的,所以我们需要部署server-backup,它是一个server的辅助工具,作用相当于agent Rabb…...
FreeSWITCH 发送 sip message 的 lua 程序
-- chat.lualocal from argv[1] local to argv[2] local body argv[3] local profile "internal" -- 改成自己的 sip_profileif not body thenstream:write("-ERR miss ie")return endlocal api freeswitch.API() local domain api:executeString(&q…...
安全学习基础入门5集
前言: 来源于b站小迪安全v2023第5天:基础入门-反弹SHELL&不回显带外&正反向连接&防火墙出入站&文件下载_哔哩哔哩_bilibili 环境准备: 通过网盘分享的文件:netcat-1.11 链接: https://pan.baidu.com/s/1zgyYvPf…...
Python结合QT进行开发
Python结合Qt进行开发指南 1. Qt for Python简介 Qt for Python(PySide/PyQt)是Python的官方Qt绑定,允许使用Python语言开发跨平台的GUI应用程序。PySide是Qt官方支持的Python绑定,而PyQt是Riverbank Computing提供的商业/开源版本。 主要特点: 跨平台支持(Windows/macOS…...
Python与深度学习:自动驾驶中的物体检测,如何让汽车“看懂”世界
Python与深度学习:自动驾驶中的物体检测,如何让汽车“看懂”世界 一、引言:自动驾驶的“眼睛”——物体检测 在自动驾驶技术的浪潮中,如何让汽车像人类一样“看懂”周围的环境,成为了最为关键的一环。汽车需要感知道路上的行人、障碍物、交通标志、其他车辆等信息,做出实…...
深度学习-神经网络参数优化的约束与迭代策略
文章目录 前言一、正则化惩罚1、权重正则化(Weight Regularization)2、结构正则化(Structural Regularization)3、其他正则化方法 二、梯度下降1、基本原理(1)梯度下降的计算(2) 算法…...
PyTorch 与 TensorFlow:深度学习框架的深度剖析与实战对比
PyTorch 与 TensorFlow:深度学习框架的深度剖析与实战对比 摘要 :本文深入对比 PyTorch 与 TensorFlow 两大深度学习框架,从核心架构、优缺点、适用场景等多维度剖析,结合实例讲解,帮助开发者清晰理解两者特性&#x…...
Meta公司于2025年4月29日正式推出了全新Meta AI应用程序的首个版本
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
【数据结构】- 栈
前言: 经过了几个月的漫长岁月,回头时年迈的小编发现,数据结构的内容还没有写博客,于是小编赶紧停下手头的活动,补上博客以洗清身上的罪孽 目录 前言: 栈的应用 括号匹配 逆波兰表达式 数制转换 栈的实…...
MATLAB R2024a安装教程
安装步骤: 软件大小:约12.08G 安装环境:Win10~Win11或更高 下载好安装包,可以在网上找个安装包,比如我用国内镜像matlab地址github.com/futureflsl/matlab-chinese-mirror,这样下载稍微快点 1.开始安装…...
【Linux网络】I/O多路转接技术 - poll
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...