这可能是全网最全react入门教程笔记(4W字劝退警告)
- React 的基本概念
- React 开发环境的搭建
- JSX语法基本使用
- JSX语法进阶使用
- React列表
- React遍历对象
- React面向组件编程基础
- React面向组件编程props基础
- props进阶默认值和验证
- React渲染外部数据与练习
- React之state基础
- React之state进阶
- React生命周期
- React之ref转发
- React事件与this
- React条件渲染
- React状态提升
- React之create react app(React脚手架)基本创建
- React脚手架组件使用
- React脚手架props与state
- React组件传值
- React数据请求与json server
- React跨域请求
- React router dom路由基础
- React路由进阶与高阶组件
- 使用State Hook
- React之redux
- React项目实战之移动端
- React项目实战之后台管理系统开发
React 的基本概念
一、React起源于Facebook
React是Facebook开发出的一款JS库,Facebook认为MVC无法满足他们的扩展需求
二、特点
1、react不实用模版
2、react不是一个MVC框架
3、响应式
4、react是一个轻量级的js库
三、原理
- 虚拟DOM——react当中把DOM抽象成一个JS对象,通过这个对象来事实更新真实DOM,并且虚拟DOM确保只对界面上真正发生变化的部分进行实际的DOM操作
- diff算法——进行逐层次的节点比较,只要遍历一次树,则可以更新整个DOM树
四、react历史
- 2013年
React 开发环境的搭建
一、主要文件
- react.js 核心文件
- react-dom.js 渲染页面中的DOM,当前文件依赖于react核心文件
- babel.js ES6转换成ES5,JSX语法转换成JavaScript,方便现代浏览器进行代码兼容
二、引入方式
可以通过下载代码包引入,也可以通过CDN方式引入,通常使用npm安装方式使用
以下介绍通过npm命令安装方式
- react核心包——
npm i react --save
- react-dom——
npm i react-dom --save
- babel——
npm i babel-standalone --save
这三个包的引入顺序为:react->react-dom->babel
三、创建第一个react应用
1、准备项目
npm init
npm i react --save
npm i react-dom --save
npm i babel-standalone --save
2、项目目录
3、项目代码
<!--
* @Author: Coan
* @Date: 2021-05-13 18:01:33
* @LastEditors: Coan
* @LastEditTime: 2021-05-13 22:36:54
* @FilePath: /React/helloReact.html
* @Description:
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HelloReact</title>
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="./node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<!-- 创建根节点 一个页面中需要有一个根节点 这个节点下的内容会被react所管理 -->
<div id="demoReact"></div>
<script type="text/babel">
// jsx语法 == JavaScript XML 的扩展语法
// 优点:1.执行的效率更高;2.jsx是类型安全的,编译的过程中就能及时的发现错误;3.在使用jsx的时候编写模版会更加简单快捷
// 【注意】jsx中HTML标签必须按照w3c的规范来写,标签必须闭合
let myDom = <h1>hello react</h1>;
ReactDOM.render(myDom, document.getElementById('demoReact'));
</script>
</body>
</html>
JSX语法基本使用
定义:jsx语法 == JavaScript XML 的扩展语法
优点:
1、执行的效率更高;
2、jsx是类型安全的,编译的过程中就能及时的发现错误;
3、在使用jsx的时候编写模版会更加简单快捷
【注意】jsx中HTML标签必须按照w3c的规范来写,标签必须闭合
一、注释
{/* 这里是注释内容 */}
let MyDom = <div>hello,我们需要在这里打一个注释,内容是’react‘,开始了{/*react*/}</div>
二、多行标签
需要一个父元素将多行标签包裹起来
let MyDom = (
<div>
<p>hello</p>
<p>我们需要在这里使用多行标签</p>
<p>开始了</p>
</div>
)
JSX语法进阶使用
一、jsx中使用表达式
如果想在jsx中使用表达式,那么我们就需要把表达式放入一对
{}
中
let text = '你好'
let num = 9527
let myDom = (
<div>
<div>{text}</div>
<div>{num}</div>
{/* 计算 */}
<div>{num + 1}</div>
</div>
)
二、jsx中使用函数
jsx中使用函数,可以在
{}
使用
function fun(obj){
return `name is ${obj.name}, and age is {obj.age}`
}
let user = {
name: "coan",
age: 18
}
let myDom = (
<div>{fun(user)}</div>
)
三、将jsx文件单独封装
可以在项目目录中单独封装一个关于jsx的js文件,用于美化代码,区分逻辑
1、项目目录
2、helloReact.html
<!--
* @Author: Coan
* @Date: 2021-05-13 18:01:33
* @LastEditors: Coan
* @LastEditTime: 2021-05-13 22:36:54
* @FilePath: /React/helloReact.html
* @Description:
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HelloReact</title>
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="./node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<div id="demoReact"></div>
<script src="./jsx.js" type="text/babel"></script>
</body>
</html>
3、jsx.js
/*
* @Author: Coan
* @Date: 2021-05-14 10:29:43
* @LastEditors: Coan
* @LastEditTime: 2021-05-14 10:33:48
* @FilePath: /React/jsx.js
* @Description:
*/
let myDom = (<h1>hello react</h1>);
ReactDOM.render(myDom, document.getElementById('demoReact'));
四、在jsx中使用三元(三目)运算符
let answer = "不会"
let myDom = (<div>你会react嘛?{answer}!!!{answer==="不会"?"那还不点赞收藏关注!!":"你说谎!"}</div>)
五、在jsx中渲染数组
let arr = [
<p>arr1</p>,
<p>arr2</p>,
<p>arr3</p>
]
let myDom = (<div>{arr}</div>)
六、如何在jsx中设置html标签属性
let text = '这是一个超链接字面'
let linkUrl = "http://www.baidu.com"
let domStyle = {
marginTop: '50px',
color: 'red',
fontSize: '28px',
}
let myDom = <a href={linkUrl} style={domStyle}>{text}</a>
七、在jsx中设置html标签类属性
【注意】在jsx中不能直接使用class这个属性,因为class是js关键字,必要时候可以使用className代替
let myDom = (<div className="demoStyle">在jsx中设置html标签的类属性</div>)
.demoStyle {
color: red;
background-color: blue
}
React列表
一、map方法遍历
map方法渲染列表,需要在每次遍历的时候绑定一个独一无二的key值,以便react在使用diff算法遍历列表的时候更加方便快捷高效
let arr = ['eat', 'sleep', 'play']
let myDom = arr.map((item, index) => {
return <p key={index}>{item}</p>
})
在遍历过程中retuen后若内容太多,需要换行编写则需要使用
()
包裹起来
let arr = ['eat', 'sleep', 'play']
let myDom = arr.map((item, index) => {
return (
<p key={index}>{item}</p>
)
})
二、for in方法循环遍历
for in方法循环遍历
let arr = [1,2,3]
function fun(){
let newArr = []
for(let index in arr){
newArr.push(<p key={index}>{arr.index}</p>)
}
return newArr
}
ReactDOM.render(fun(),document.getElementById('demoReact'))
三、同理forEach、for循环
同理forEach、for循环也可以遍历循环列表,但map方法更简便
四、案例
在列表渲染中添加点击事件,使点击项变色,并让render函数重新调用
let arr = ['eat', 'sleep', 'play']
let index = -1
function fun(){
let myDom = arr.map((v, i) => {
return <p key={i} style={{color: index == i ? 'red' : ''}} onClick={() => {index = i; render()}}>{v}</p>
})
return myDom
}
function render(){
ReactDOM.render(fun(),document.getElementById('demoReact'))
}
render()
React遍历对象
一、原生JavaScript中对象的取值操作
let obj = {
name: 'coan',
age: 18
}
console.log(obj.name) // 通过点取值
console.log(obj[age]) // 通过[]取值
// 如果对象的key值是一个变量,则无法用点方式取值
console.log(Object.keys(obj)) // 返回数组类型数据,内容为对象的键(key)
console.log(Object.values(obj)) // 返回数组类型数据,内容为对象的值(value)
二、React遍历对象
let obj = {
name: 'coan',
age: 18
}
let myDom = (<div>
{Object.keys(obj).map((v, i) => {
return <p>key:{v}----value:{obj[v]}</p>
})}
</div>)
React面向组件编程基础
React组件是一个非常重要的概念。通过组件可以吧页面中的ui部分切分成独立的、高复用的部件,让每个开发者更加专注于一个个独立的部件。
一、什么是组件
组件与组件化:
组件:就是用于实现页面局部功能的代码集合,简化页面的复杂程度,提高运行效率
组件化:如果当前程序都是使用组件完成的,那么我们就可说这是一个组件化的应用
1、高耦合低内聚
高耦合:把逻辑紧密的内容放在一个组件中
低内聚:把不同组件的依赖关系尽量弱化,每个组件都尽可能的独立
2、组件当中的重要内容
构建方式
组件的属性
生命周期
3、演变过程
传统的组件有几个明显的特点:
1、简单的封装
2、简单的生命周期
3、明显的数据流动
当一个项目比较复杂的时候,传统的组件化根本不能很好的把结构样式和行为结合,让项目很难维护
4、react组件的三大部
1、属性props
2、状态state
3、生命周期
二、组件的创建
1、函数组件/无状态组件
function MyCom(){
return (
<div>这是一个无状态组件</div>
)
}
// 调用组件[组件就是自定义标签]
let com = (
<div>
<MyCom/>
<MyCom></MyCom>
</div>
)
ReactDOM.render(com, document.getElementById('demoReact'))
2、父子组件
// 定义子组件
function MyComA(){
return(
<h1>这是子组件A</h1>
)
}
function MyComB(){
return(
<h1>这是子组件B</h1>
)
}
function MyComC(){
return(
<h1>这是子组件C</h1>
)
}
// 父组件调用
function ComP(){
return (
<div>
<MyComA/>
<MyComB/>
<MyComC/>
</div>
)
}
ReactDOM.render(<Comp/>, document.getElementById('demoReact'))
3、类组件
class MyCom extends React.Component{
render(){
return(
<div>类组件</div>
)
}
}
let com = <MyCom/>
ReactDOM.render(com, document.getElementById('demoReact'))
React面向组件编程props基础
一、props是什么
props是react中一个重要的属性,是组件对外的接口。我们使用props就可以从组件的外部想内部进行数据的传递,也可以完成父组件给自组件的数据传递。
【注意】无论是无状态组件还是类组件,我们都不能修改自身的props
二、无状态组件使用props
1、组件外向组件传递简单数据
function Com(props){
return(
<div>这是一个无状态组件---外部传递数据是:{props.text}</div>
)
}
ReactDOM.render(<Com text='这是外部传递给props的数据text' />,document.getElementById('demoReact'))
2、组件外向组件传递变量
function Com(props){
return(
<div>这是一个无状态组件---外部传递数据是:name:{props.name}---age:{props.age}</div>
)
}
let name = 'coan'
let age = 18
ReactDOM.render(<Com name={name} age={age} />,document.getElementById('demoReact'))
若需要传递很多变量到组件中,可以使用ES6扩展运算符语法使代码看起来更简洁
function Com(props){
return(
<div>这是一个无状态组件---外部传递数据是:name:{props.name}---age:{props.age}</div>
)
}
let propsObj = {
name : 'coan',
age : 18
}
ReactDOM.render(<Com {...propsObj} />,document.getElementById('demoReact'))
三、类组件使用props
1、传递简单数据
calss Com extends React.Component{
render(){
return(
<div>这是类组件---外部传递的数据是:name:{this.props.name}---age:{this.props.age}</div>
)
}
}
ReactDOM.render(<Com name='类组件props数据coan' age='类组件props数据18' />,document.getElementById('demoReact'))
2、传递变量
calss Com extends React.Component{
render(){
return(
<div>这是类组件---外部传递的数据是:name:{this.props.name}---age:{this.props.num}</div>
)
}
}
let propsObj = {
name: 'coan',
aga: 18
}
ReactDOM.render(<Com {...propsObj} />,document.getElementById('demoReact'))
props进阶默认值和验证
props验证:用来验证传递进来的值是否符合我们期望的类型或者要求【注意:上线模式中请取消props验证,因为props验证只会在调试信息中报错但并不影响数据显示】
props默认值:当我们没有数据传递进组件,但还要显示某些东西的时候,我们需要设置props默认值来帮助我们实现这个效果
一、无状态组件props默认值和验证
1、无状态组件props默认值的设定
默认值需要defaultProps设置
function Com(props){
return (
<div>这是一个无状态组件---{props.text}</div>
)
}
Com.defaultProps = {
text: '这是props.text的默认值'
}
// 如果是<15.x版本的react,那么我们可以使用||的方式来实现默认值
// function Com(props){
// return (
// <div>这是一个无状态组件---{props.text || '这是15.x版本之前的默认值定义方式'}</div>
// )
// }
ReactDOM.render(<Com text='这是props.text的传递数据'/>, document.getElementById('demoReact'))
2、无状态组件props验证的使用
- 引用
prop-tyoes
库npm install prop-type --save
Com.propTypes = {
name: PropTypes.string // 验证name这个props传递进来的数据必须是string类型
age: PropTypes.number.isRequired // 验证age这个props传递进来的数据必须是number类型,并且不能为空
}
更多的props验证检查请移步官方文档:使用 PropTypes 进行类型检查【https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html】
二、类组件的props默认值和验证
1、类组件props默认值和验证的传统写法
类组件的props默认值设置以及验证的传统写法和无状态组件使用的方式一毛一样
2、在类组件中使用static
关键字来设置props默认值
class Com extends React.Component{
static defaultProps = {
text: '这是使用static关键字设置的props默认值'
}
render(){
return(
<div>这是类组件---{this.props.text}</div>
)
}
}
React渲染外部数据与练习
一份可以中英转换的CSGO地图名单练习
<!--
* @Author: Coan
* @Date: 2021-05-13 18:01:33
* @LastEditors: Coan
* @LastEditTime: 2021-05-15 17:08:32
* @FilePath: /React/helloReact.html
* @Description: 一份可以中英转换的CSGO地图名单练习
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>HelloReact</title>
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="./node_modules/babel-standalone/babel.min.js"></script>
</head>
<body>
<div id="demoReact"></div>
<script type="text/babel">
let btnV = 'en'; // 用来控制展示中文还是英文的变量
let ComC = (props) => { // 定义子组件用来展示地图名单,props为父组件传来的值
return (
<div>
<div style={{ display: btnV === 'en' ? 'block' : 'none' }}>
{props.arrE.map((v, i) => {
return <p key={i}>{v}</p>;
})}
</div>
<div style={{ display: btnV === 'ch' ? 'block' : 'none' }}>
{props.arrC.map((v, i) => {
return <p key={i}>{v}</p>;
})}
</div>
</div>
);
};
let arrE = [ // 英文地图名单
'de_inferno',
'de_train',
'de_mirage',
'de_nuke',
'de_dust2',
'de_overpass',
'de_vertigo',
];
let arrC = [ // 中文地图名单
'炼狱小镇',
'列车停放站',
'荒漠迷城',
'核子危机',
'炙热沙城Ⅱ',
'死亡游乐园',
'殒命大厦',
];
let ComP = () => { // 定义父组件,写了标题以及控制按钮,并引入了地图列表的子组件
return (
<div>
<h1>CSGO中的地图名</h1>
<button
onClick={() => {
btnV === 'en' ? (btnV = 'ch') : (btnV = 'en');
render();
}}
>
en/ch
</button>
<ComC arrE={arrE} arrC={arrC} />
</div>
);
};
function render() { // 将render方法封装成一个函数,在页面发生变化的时候,我们可以调用这个函数来使react重新渲染,从而改变页面中绑定的数据内容
ReactDOM.render(ComP(), document.getElementById('demoReact'));
}
render();
</script>
</body>
</html>
React之state基础
一、state是什么
state——状态
state和props的本质区别:props是组件对外的接口,state是组件队内的接口
props:组件内可以引用其他组件,组件之间的引用就形成了一个树状的接口,如果下层组件需要使用上层组件的数据,上层组件就可以通过下层组件中的props属性来进行数据的传递,因此,props就是组件对外的接口
state:组件除了使用上层组件传递的数据之外,他自身也可能有需要管理的数据,这个对内管理数据的属性就是state
二、为什么使用state
我们回想一下上一章节中我们做的案例——CSGO地图中英切换的案例,当数据发生改变的时候,我们封装了render函数,从而手动的实现了页面数据的内容修改,这样很不理想,我们想要实现自动化的监听和改变,这也是react设计当中的一大重点。
在拥有了state的react项目中,我们只需要关注数据,当数据改变的时候页面就会自动发生改变,状态就等同于数据,也就是说,状态/数据改变了,则对应页面中的数据绑定内容就会被react自动改变。
这也被称作
声明式渲染
——即一切的数据改变操作我们都不用去关注,只需要我们声明好数据,react就会自动的对于数据进行相应的改变
三、state和props的主要区别
state是可变的
而props相对于当前组件来说,他是只读的,如果我们想要修改props当中的数据,我们就只能修改传递给当前组件数据的父组件的数据
四、state的使用
【注意】如果我们想要使用state,那么我们就不能使用无状态组件,只能使用类组件
class Com extends React.Component{
// 在ES6当中,不管子类写不写constructor,在new实例的时候都会自动的给类加上constructor
// 当然我们可以不写constructor,但是如果我们写了,就必须在其中写上super()
// super()就是指向父类的构造方法
// 如果想在constructor中使用props,那么我们就必须写上props
constructor(props){
super(props)
// 定义state
this.state = {
naem: 'coan'
}
}
render(){
return(
<div>
<p>这是一个类组件---state的值为:{this.state.name}</p>
</div>
)
}
}
五、state数据的修改
不能直接修改,需要调用
this.setState({key:newValue})
方法来修改
this.setState()
方法是一个异步方法,当该方法被调用,react就会自动触发render,从而进行重新渲染页面
于是我们修改一下上面的案例
class Com extends React.Component{
// 在ES6当中,不管子类写不写constructor,在new实例的时候都会自动的给类加上constructor
// 当然我们可以不写constructor,但是如果我们写了,就必须在其中写上super()
// super()就是指向父类的构造方法
// 如果想在constructor中使用props,那么我们就必须写上props
constructor(props){
super(props)
// 定义state
this.state = {
naem: 'coan'
}
}
render(){
return(
<div>
<p>这是一个类组件---state的值为:{this.state.name}</p>
<button onClick={() => {
this.state.name === '科恩'
? this.setState({ name: 'coan' })
: this.setState({ name: '科恩' });
}}
>
科恩/coan
</button>
</div>
)
}
}
React之state进阶
一、state的异步操作
this.setState()
是异步的
class Com extends React.Component{
constructor(props){
super(props)
this.state = {
name: 'coan'
}
}
fun = () => {
this.setState({
name: 'Coan'
})
console.log(this.state.name) // 这里会打印出’coan‘,原因是this.setState是异步的
}
render(){
return (
<div>
<button onClick={this.fun}>修改</button>
{this.state.name}
</div>
)
}
}
如上例所示,我们在点击事件修改后的打印中发现,因为
this.setState()
方法为异步操作,所以之后的打印依旧是初始的数据值,如我国们想要拿到修改之后的值,则可以使用this.setState()
方法的第二个参数,该参数是一个回调函数
// 比如我们将fun()函数中的代码修改为这样
this.setState({
name: 'Coan'
}, () => {
console.log(this.state.name) // 这里打印的结果为修改后的name值:Coan
})
二、使用state插入字符串标签
字符串标签插入的话,需要使用
dangerouslySetInnerHTML = {{__html:你要插入的字符串}}
class Com extends React.Component{
constructor(props){
super(props)
this.state = {
name: 'coan',
newHtmlAge: '<h1>18</h1>'
}
}
render(){
return(
<div>
<div>{this.state.newHtmlAge}</div>
<div dangerouslySetInnerHTML={{__html:this.state.newHtmlAge}}></div>
</div>
)
}
}
React生命周期
组件的生命周期可分成三个状态:
- Mounting:已插入真实 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真实 DOM
生命周期的方法有:
- componentWillMount 在渲染前调用,在客户端也在服务端。
- componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。
- componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
- shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
可以在你确认不需要更新组件时使用。- componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
- componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。
- componentWillUnmount在组件从 DOM 中移除之前立刻被调用。
这些方法的详细说明,可以参考官方文档。
挂载
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
更新
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
卸载
当组件从 DOM 中移除时会调用如下方法:
错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
React之ref转发
一、refs转发是什么
react当中提供了一个ref的数据(不能在无状态组件当中来进行使用,因为无状态组件没有实例)
表示当前组件的真正实例的引用,他就返回绑定当前属性的元素
标识组件内部的元素——方便我们查找
二、ref的使用
1、字符串的方式
2、回调函数(推荐)
3、
React.createRef()
_(react16.3新提供的一种方式)
三、字符串方式使用ref
class Com extends React.Component{
fun = () => {
console.log(this.refs.demoInput.value)
}
render(){
return(
<div>
这是一个类组件
<input type="text" ref="demoInput" placeholder="请输入" />
<button onClick={this.fun}>点击获得输入框的值</button>
</div>
)
}
}
四、回调函数方式使用ref
就是在dom节点上或者组件上挂载函数,函数的入参(行参)是dom节点,其效果和字符串方式是一样的,都是获取值的引用
class Com extends React.Component{
fun = () => {
console.log(this.inputText.value)
}
render(){
return(
<div>
这是一个类组件
<input type="text" ref={(input)=>{this.inputText=input}} placeholder="请输入" />
<button onClick={this.fun}>点击获得输入框的值</button>
</div>
)
}
}
五、React.createRef()
方式使用ref
把值赋值给一个变量,通过ref挂载在节点或者组件上,使用ref的current属性拿到这个节点
class Com extends React.Component{
constructor(props){
super(props)
this.myRef = React.createRef()
}
fun = () => {
console.log(this.myRef.current.value)
}
render(){
return(
<div>
<input type="text" ref={this.myRef} placeholder="请输入" />
<button onClick={this.fun}>点击得到输入框的值</button>
</div>
)
}
}
六、官方建议在项目当中,不要过度使用refs对逻辑进行处理,优先考虑state
React事件与this
一、react事件基础
在react当中进行事件处理,其绑定事件使用小驼峰命名法,在绑定函数的时候不能加
()
——>如果加了()
函数会立即执行
二、修改this指向
1、bind方式原地绑定
2、函数通过剪头函数进行创建
3、constructor中提前绑定
4、把事件的调用写成箭头函数的调用方式
class Com extends React.Component{
constructor(props){
super(props)
this.fun3 = this.fun3.bind(this)
}
fun1(){
console.log(this)
}
fun2 = () => {
console.log(this)
}
fun3(){
console.log(this)
}
fun4(){
console.log(this)
}
render(){
return(
<div>
<button onClick={this.fun1.bind(this)}>bind方式绑定</button>
<button onClick={this.fun2}>箭头函数创建绑定</button>
<button onClick={this.fun3}>constructor提前绑定</button>
<button onClick={()=>{this.fun4()}}>箭头函数调用绑定</button>
</div>
)
}
}
三、react事件传参
1、bind方式传参
2、箭头函数调用传参
class Com extends React.Component{
fun = (arg) => {
console.log(arg)
}
render(){
return(
<div>
<button onClick={this.fun.bind(this,'这是bind传递的参数')}>bind方式传参</button>
<button onClick={()=>{this.fun('这是箭头函数传递的参数')}}>箭头函数调用传参</button>
</div>
)
}
}
3、箭头函数调用传递事件对象
class Com extends React.Component{
fun = (arg, e) => {
console.log(arg)
console.log(e)
}
render(){
return(
<div>
<button onClick={(e)=>{this.fun('这是箭头函数传递的参数',e)}}>箭头函数调用传参</button>
</div>
)
}
}
React条件渲染
一、条件渲染是什么
根据状态的变化只渲染其中的一部分
二、if语句
jsx中不允许有if
class Com extends React.Component{
constructor(props){
super(props)
this.state = {
bool: true
}
}
fun(){
this.setState({
bool: !this.state.bool
})
}
render(){
let text
if(this.state.bool){
text = 'bool is true'
}else{
text = 'bool is false'
}
return(
<div>
<button onClick={this.fun.bind(this)}>点击修改渲染条件</button>{text}
</div>
)
}
}
三、三元运算符
class Com extends React.Component{
constructor(props){
super(props)
this.state = {
bool: true
}
}
fun(){
this.setState({
bool: !this.state.bool
})
}
render(){
return(
<div>
<button onClick={this.fun.bind(this)}>点击修改渲染条件</button>{this.state.bool?'bool is true':'bool is false'}
</div>
)
}
}
四、bind方式
React状态提升
一、状态提升是什么
在react当中,如果多个组件需要反映相同的变化数据,那么我们就需要将他们的状态提升到理他们最近的一个父组件中
即:多个子组件需要利用到对方状态的情况下,我们就需要使用到状态提升
二、状态提升的使用
class Demo1 extends React.Component{
constructor(props){
super(props)
}
render(){
return(
<div>this is demo1---{this.props.text}</div>
)
}
}
class Demo2 extends React.Component{
constructor(props){
super(props)
}
render(){
return(
<div>this is demo2---{this.props.text}</div>
)
}
}
class DemoP extends React.Component{
constructor(props){
super(props)
this.state = {
demoCText: 'this is a text,and all of demoCs can use it'
}
}
fun = () => {
this.setState({
demoCText: 'this is the revused demoCText'
})
}
render(){
return(
<div>
this is demoP
<Demo1 text={this.state.demoCText} />
<Demo2 text={this.state.demoCText} />
<button onClick={this.fun}>click to change the demoCText</button>
</div>
)
}
}
ReactDOM.render(<demoP />, document.getElementById('demoReact'))
React之create react app(React脚手架)基本创建
create-react-app是FaceBook官方推出的一款react脚手架工具
要使用react脚手架工具,必须要依赖node环境
一、安装(npm安装)
npm install create-react-app -g
全局安装react脚手架工具
二、查看脚手架版本
create-react-app --version
【小插曲】(for macOS)
If your terminal spits out this to you:
-bash: create-react-app: command not found
You are able to apply the following solution:
$ npm config set prefix /usr/local
$ sudo npm install -g create-react-app
$ create-react-app my-app
三、创建项目
cd到需要创建项目的目录
create-react-app my-app
(使用脚手架创建项目,react脚手架创建项目默认使用yarn管理项目)npx create-react-app my-app
(npx 来自 npm 5.2+ 或更高版本, 查看 npm 旧版本的说明)npm init react-app my-app
(npm init <initializer>
在 npm 6+ 中可用)yarn create react-app my-app
(yarn create
在 Yarn 0.25+ 中可用)官方说明:
If you use npm 5.1 or earlier, you can’t use
npx
. Instead, installcreate-react-app
globally:npm install -g create-react-app
Now you can run:
create-react-app my-app
【翻译】:如果你使用npm 5.1或更早的版本,你就不能使用
npx
。相反,全局安装create-react-app
:npm install -g create-react-app
现在你可以运行了:
create-react-app my-app
四、启动项目
cd my-app
npm start
(如果你安装了yarn,也可以使用yarn start
)
五、项目目录
1、public 静态资源文件夹
2、src 写代码的地方
App 根组件
index 全局主配置文件
React脚手架组件使用
1、在src下创建文件夹–Components
2、在Components下创建组件–
FirstCom.js
或者FirstCom.jsx
3、然后我们就可以在组件
FirstCom.js
中编写组件内容了(vs codek代码块–rcc
)
import React, { Component } from 'react'
export default class FirstCom extends Component {
render() {
return (
<div>
this is the FirstCom.{parseInt(Math.random() * 10)}
</div>
)
}
}
4、在需要使用到组件的地方引入组件FirstCom,比如App.js
import FirstCom from './components/FirstCom';
5、使用组件
function App() { return ( <div className='App'> <header className='App-header'> <FirstCom /> <img src={logo} className='App-logo' alt='logo' /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a className='App-link' href='https://reactjs.org' target='_blank' rel='noopener noreferrer' > Learn React </a> </header> </div> ); }
6、运行项目效果
7、react项目中图片的引用
在App.js中我们看到react引用图片是采取了先导入再引用的方式,即:
// 导入 import logo from './logo.svg'; // 引用 <img src={logo} className='App-logo' alt='logo' />
这是当图片在src目录下的一种处理方式,此外,我们还可以使用另外一种方式来处理图片,从而使得不必每张图片都进行事先的import:
<img src={require('../assets/logo192.png')} />
当require引用图片不显示时,可以试试以下方法:
<img src={require('../assets/logo192.png').default} />
【require中只能放字符串,不能放变量】
如果图片在public目录下,则可以不带路径直接引用文件名,如:
<img src='logo512.png' />
React脚手架props与state
环境准备:
一个父组件ComP、一个子组件ComC
在父组件中引用子组件
一、React脚手架中使用props
/*
* @Author: Coan
* @Date: 2021-05-18 15:24:39
* @LastEditors: Coan
* @LastEditTime: 2021-05-18 15:34:19
* @FilePath: /React/my-cli-app/src/components/ComP.js
* @Description: ComP
*/
import React, { Component } from 'react';
import ComC from './ComC';
export default class ComP extends Component {
render() {
return (
<div>
ComP
<ComC text='This is a text from ComP to ComC' />
</div>
);
}
}
/*
* @Author: Coan
* @Date: 2021-05-18 15:25:02
* @LastEditors: Coan
* @LastEditTime: 2021-05-18 15:37:55
* @FilePath: /React/my-cli-app/src/components/ComC.js
* @Description: ComC
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class ComC extends Component {
static propTypes = {
text: PropTypes.number,
};
render() {
return <div>ComC---{this.props.text}</div>;
}
}
二、React脚手架中使用state
脚手架环境中使用state与本地模式使用state方式相同
/*
* @Author: Coan
* @Date: 2021-05-18 15:25:02
* @LastEditors: Coan
* @LastEditTime: 2021-05-18 15:48:21
* @FilePath: /React/my-cli-app/src/components/ComC.js
* @Description:
*/
import React, { Component } from 'react';
export default class ComC extends Component {
constructor(props) {
super(props);
this.state = {
name: 'coan',
};
}
render() {
return (
<div>
ComC---{this.props.text}---by:{this.state.name}
<br />
<button
onClick={() => {
this.setState({
name: 'Coan',
});
}}
>
updata_state_to_Coan
</button>
</div>
);
}
}
React组件传值
一、正向传值(父传子)
父组件直接通过自定义属性向子组件进行传递
子组件通过props进行接收
二、逆向传值(子传父)
父组件通过自定义属性向子组件传递一个定义好的自定义方法
子组件通过props接收该方法
触发该方法时将数据传入为参数
这样,父组件就可以在先前的自定义方法中获取到子组件传递的数据
/*
* @Author: Coan
* @Date: 2021-05-18 15:24:39
* @LastEditors: Coan
* @LastEditTime: 2021-05-18 16:13:28
* @FilePath: /React/my-cli-app/src/components/ComP.js
* @Description: ComP
*/
import React, { Component } from 'react';
import ComC from './ComC';
export default class ComP extends Component {
constructor(props) {
super(props);
this.state = {
text: 'This is defaultText of ComP',
};
}
receiveFromComC = (textFromComC) => { // 该方法用来接收和保存子组件传递的数据
console.log(textFromComC);
this.setState({
text: textFromComC,
});
};
render() {
return (
<div>
ComP---{this.state.text}
<ComC
textP='This is a text from ComP to ComC'
sendToComP={this.receiveFromComC}
/>
</div>
);
}
}
/*
* @Author: Coan
* @Date: 2021-05-18 15:25:02
* @LastEditors: Coan
* @LastEditTime: 2021-05-18 15:59:52
* @FilePath: /React/my-cli-app/src/components/ComC.js
* @Description: ComC
*/
import React, { Component } from 'react';
export default class ComC extends Component {
constructor(props) {
super(props);
this.state = {
name: 'coan',
textC: 'This is a text from ComC to ComP',
};
}
render() {
return (
<div>
ComC---{this.props.textP}---by:{this.state.name}
<br />
<button
onClick={() => {
this.setState({
name: 'Coan',
});
}}
>
updata_state_to_Coan
</button>
<button onClick={this.props.sendToComP.bind(this, this.state.textC)}>
send_textC_to_ComP
{/* 这里触发父组件传递过来的自定义方法并逆向传递数据 */}
</button>
</div>
);
}
}
三、同胞传值(兄弟传值)
首当其冲的古老办法:结合正向传值和逆向传值来实现兄弟组件传值
即子组件A传值给父组件P,然后再由父组件P传值给子组件B
子组件B实现渲染
不经过父组件的传值方法
想要实现同胞传值,我们需要使用到
pubsub.js
npm install --save pubsub-js
创建两个子组件ComC和ComC2
在发起传值和接收数据的组件中引用
pubsub-js
import PubSub from 'pubsub-js'
在发起传值的组件ComC中使用
PubSub.publish()
方法进行数据传递PubSub.publish('sendToComC2', this.state.textC2) // 第一个参数为自定义函数名,第二个参数为要传递的数据
在接收数据的组件ComC2中使用
PubSub.subscribe()
方法来进行数据接收PubSub.subscribe('sendToComC2', (eventName, data) => { console.log(eventName); console.log(data); }); // 第一个参数为监听函数(需要与发起传值的函数匹配),第二个参数为回调函数(回调函数中的第一个参数为监听到函数的函数名,我们可以在回调函数的第二个参数中拿到接受的数据)
React数据请求与json server
一、json-server
Json-server 模拟数据
安装:
npm install json-server -g
准备数据:
1、在项目根路径下创建mock文件夹用来存放模拟数据
2、创建模拟数据json文件:
{ "arr": [ {"id":1,"name":"coan1"}, {"id":2,"name":"coan2"}, {"id":3,"name":"coan3"}, {"id":4,"name":"coan4"} ] }
启动json-server
1、cd到mock文件夹下
2、
json-server json数据文件名 -- port 4000
【json-server默认端口是3000,这里修改为4000】成功启动后你将会看到:
此时我们直接在浏览器访问
localhost:4000
时,就会看到本地json-server服务的主页
(这里我们只需要简单引用一下json-server,后期有需要的话我会补充json-server的详细教程记录)
二、axios
数据请求
安装:
npm install axios --save
引用:在需要发起请求的文件中引入axios
例如:我们新建组件Axios用来演示axios请求本地mock数据
使用:我们在组件的钩子函数中使用axios发起请求
/* * @Author: Coan * @Date: 2021-05-21 16:39:21 * @LastEditors: Coan * @LastEditTime: 2021-05-21 17:09:53 * @FilePath: /React/my-cli-app/src/components/Axios.js * @Description: axios使用 */ import React, { Component } from 'react'; import axios from 'axios'; export default class Axios extends Component { constructor(props) { super(props); this.state = { label: 'data这里将存放请求返回数据', data: [], }; } componentDidMount() { // 组件挂载的钩子函数 this.axiosFun(); } axiosFun = () => { axios.get('http://localhost:4000/arr').then((res) => { // axios发起get请求本地mock数据 console.log(res.data); this.setState({ label: 'res is ok,请求已返回数据', data: res.data, }); }); }; render() { return ( <div> <div>{this.state.label}</div> <div> {this.state.data.map((v, i) => { return <p key={v.id}>{v.name}</p>; })} </div> </div> ); } }
(这里我们只需要简单引用并演示一下axios在react项目当中的使用,后期有需要的话我会补充axios的详细教程记录)
React跨域请求
一、正向代理–开发环境
一个位于客户端和目标服务器之间的代理服务器,为了获取到目标服务器的内容,客户端向代理服务器发送一个请求,代理服务器帮助我们去目标服务器里面获取数据并且返回给我们
1、修改package.json
// 最新的create-react-app脚手架2.0版本以上只能配置string类型 "proxy": "http://m.kugo.com",
// create-react-app脚手架低于2.0版本时候,可以使用对象类型,否则会报错 // 后面2项(pathRewrite,secure)可以不写 "proxy":{ "/api":{ target: "http://www.baidu.com", // 目标地址-跨域请求的地址 changeOrigin: true, // 默认false,是否需要改变原始主机头为目标URL "pathRewrite": { "^/api":"/" }, // 重写请求,比如我们源访问的是api,那么请求会被解析为/ "secure": false, // 如果是https接口 需要配置这个参数为true } },
2、修改webpackDevServer.config.js
配置文件目录:node_modules > react-scripts > confuse > webpackDevServer.config.js
// 后面2项(pathRewrite,secure)可以不写 proxy:{ "/api":{ target: "http://www.baidu.com", // 目标地址-跨域请求的地址 changeOrigin: true, // 默认false,是否需要改变原始主机头为目标URL "pathRewrite": { "^/api":"/" }, // 重写请求,比如我们源访问的是api,那么请求会被解析为/ "secure": false, // 如果是https接口 需要配置这个参数为true } },
3、使用middleware中间件(Creact React App脚手架官网推荐方法,可以配置多个代理)
1、安装http-proxy-middleware
npm install http-proxy-middleware --save
2、在src目录下创建 setupProxy.js 文件
3、设置代理(setupProxy.js文件内容)
const proxy = require('http-proxy-middleware') module.exports = function(app) { app.use( proxy.createProxyMiddleware('/api', { target: 'http://localhost:5000', // 目标地址-跨域请求的地址 changeOrigin: true, "pathRewrite": { "^/api":"/" }, // 重写请求,比如我们源访问的是api,那么请求会被解析为/ }) ) }
4、官方资料
https://www.html.cn/create-react-app/docs/proxying-api-requests-in-development/#configuring-the-proxy-manually
二、反向代理–生产环境
可以通过代理服务器来接收网络上的请求链接,然后将这个请求转发给内部的网络服务器上,并且把这个服务器上得到的数据返回给发起网络请求的客户端,这个时候代理服务器对外的表现就是一个反向代理
React router dom路由基础
一、什么是路由
路由系统可以根据url的不同来切换对应的组件,实现spa(单页面应用)在页面切换的时候不刷新,使react项目更加接近原生体验。
- react-router,只实现了路由的核心功能
- react-router-dom,基于react-router,加入了一些浏览器运行环境下的一些功能,例如:Link组件,会渲染一个a标签。BrowserRouter和HashRouter组件,前者使用pushState和popState事件构建路由,后者使用window.location.hash和hashchange事件构建路由。
- react-router-native,基于react-router,类似react-router-dom,加入了react-native运行环境下的一些功能。
传送门:react-router和react-routr-dom的区别详解
二、下载路由(react-router-dom)
npm install --save react-router-dom
三、react-router-dom中的路由模式
hash模式–HashRouter(hash模式,url地址带#号,刷新页面时不会丢失)
browser模式-- BrowserRouter(历史记录模式- history,url不带#号,通过历史记录api来进行路由切换,生产环境刷新会丢失,本地模式中不会丢失)
四、使用路由
1、在全局的index.js文件中引用路由
import { BrowserRouter } from 'react-router-dom'; // browser模式
// 或
import { HashRouter } from 'react-router-dom'; // hash模式
2、路由包裹根组件
ReactDOM.render( // browser模式
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
// 或
ReactDOM.render( // hash模式
<HashRouter>
<App />
</HashRouter>,
document.getElementById('root')
);
3、在跟组件中引用路由
import { Route } from 'react-router-dom';
4、配置route
<Route path='/firstCom' component={FirstCom} />
五、路由导航(声明式导航)
1、Link–<Link to="/firstCom" />Link路由导航</Link>
1、引用
import { Link } from 'react-router-dom';
2、使用
<Link to='/firstCom'>GoToFirstCom</Link>
2、NavLink–<NavLink to="/firstCom" />NavLink路由导航</NavLink>
1、引用
import { NavLink } from 'react-router-dom';
2、使用
<NavLink to='/firstCom'>GoToFirstCom</NavLink>
3、Link和NavLink的区别
Link路由导航只实现了路由的导航跳转
NavLink路由导航在实现路由的导航跳转之外,还为导航的标签自动添加了动态的
active
类。这就使得我们在写导航标签时修改样式变得更加方便
4、精准匹配
在路由组件中使用exact属性即可设置精准匹配规则,即完全符合路由地址才会渲染该路由组件
<Route path='/firstCom' exact component={FirstCom} />
5、唯一渲染
当我们在使用路由组件时不小心写到了重复的路由,此时我们只需要在路由外部使用
<Switch></Switch>
标签将路由包裹起来,这样react的路由系统则会帮我们实现唯一渲染import { Route, Link, NavLink, Switch } from 'react-router-dom'; <Switch> <Route path='/firstCom' exact component={FirstCom} /> <Route path='/firstCom' exact component={FirstCom} /> </Switch>
6、路由重定向
<Redirect></Redirect>
标签可以使当前匹配路径重定向到另外的新路径下import { Route, Link, NavLink, Switch, Redirect } from 'react-router-dom'; <Redirect from='/' to='/firstCom' exact />
7、二级路由
react中使用二级路由用法与一级路由相似,只需要在一级路由组件内使用即可
React路由进阶与高阶组件
一、withRouter
react当中的一个高阶组件,可以让不是路由切换的组件也具有路由切换组件的三个属性(location、match、history)
用途:监控路由变化
*【高阶组件】:*HOC——参数是一个组件,同时返回的也是一个组件,这类组件我们称之为高阶组件
在我们没有使用withRouter时,在App组件中我们打印props输出值为
{}
import { Route, NavLink } from 'react-router-dom'; function App(props) { console.log(props); // 这里输出结果为 {} return ( <div className='App'> <header className='App-header'> <NavLink to='/firstCom'>GoToFirstCom</NavLink> <Route path='/firstCom' exact component={FirstCom} /> </header> </div> ); } export default App;
当我们使用withRouter包裹了App组件后,props就拥有了路由切换组件的三个属性(location、match、history)
import { Route, NavLink, withRouter } from 'react-router-dom'; function App(props) { console.log(props); return ( <div className='App'> <header className='App-header'> <NavLink to='/firstCom'>GoToFirstCom</NavLink> <Route path='/firstCom' exact component={FirstCom} /> </header> </div> ); } export default withRouter(App);
props打印结果:
这里我们使用withRouter使根组件App拥有了props属性值
二、监控路由变化
我们使用App的props属性里的
history.listen()
方法来监听路由的变化props.history.listen((link) => { console.log(link); });
路由变化时打印结果为:
- pathname:跳转后的目标路径
三、编程式导航
props.history.push('/PathName')
<button onClick={() => {props.history.push('./firstCom');}}>编程式导航GoToFirstCom</button>
四、路由传参
1、params传参
1、需要在路由规则中配置传递的接收参数
:xxx
<Route path='/firstCom/:args' exact component={FirstCom} />
2、发送参数 直接在路由跳转路径后拼接参数
<NavLink to='/firstCom/this_is_params_args'>GoToFirstCom</NavLink>
3、接收参数
this.props.match.params.参数名
componentDidMount() { console.log(this.props.match.params.args); }
优点:刷新地址,参数依然存在
缺点:只能传递字符串,并且,参数过多的时候url会变的比较丑陋
2、query传参
1、不需要在路由规则中配置传递的接收参数
2、发送参数 直接发送数据
<Link to={{ pathname: '/firstCom', query: { args: 'this_is_query_args' } }}> GoToFirstCom </Link>
3、接收参数
this.props.location.query.xxx
componentDidMount() { console.log('query:' + this.props.location.query.args); }
使用State Hook
React16.8当中新增的一个特性,主要是用来让无状态组件可以使用状态
在react开发当中,状态的管理是必不可少的,在16.7之前的版本,我们需要使用类组件或者redux来进行状态管理操作
我们可以使用react当中的Hook中的useState来进行实现在无状态组件中的状态使用
useState是来定义一个状态的,与类组件中的状态不同,函数组件的状态可以使对象,也可以是基本数据类型
useState返回的是一个数组,第一个值是当前的状态值,第二个值是一个对象,用于表明更改状态的函数(类似于setState)
*官方的Hook示例
1: import React, { useState } from 'react';
2:
3: function Example() {
4: const [count, setCount] = useState(0);
5:
6: return (
7: <div>
8: <p>You clicked {count} times</p>
9: <button onClick={() => setCount(count + 1)}>
10: Click me
11: </button>
12: </div>
13: );
14: }
- 第一行: 引入 React 中的
useState
Hook。它让我们在函数组件中存储内部 state。- 第四行: 在
Example
组件内部,我们通过调用useState
Hook 声明了一个新的 state 变量。它返回一对值给到我们命名的变量上。我们把变量命名为count
,因为它存储的是点击次数。我们通过传0
作为useState
唯一的参数来将其初始化为0
。第二个返回的值本身就是一个函数。它让我们可以更新count
的值,所以我们叫它setCount
。- 第九行: 当用户点击按钮后,我们传递一个新的值给
setCount
。React 会重新渲染Example
组件,并把最新的count
传给它。
一、引入
import React, { useState } from 'react';
二、初始化
let [val, setVal] = useState(0);
三、使用
<div> useState:val={val} <button onClick={() => {setVal(val + 1)}}> setValOfUseState(++1) </button> </div>
四、多个状态的使用
1、声明对象类型的状态
初始化
let [obj, setObj] = useState({ obj1val:1, obj2val:2 })
使用
useState:obj1val={obj.obj1val}---obj2val={obj.obj2val}
至于修改的话,如果useState有多个状态,需要使用其他方式进行修改
2、多次声明基本数据类型的状态(推荐使用)
let [val1, setVal1] = useState(0); let [val2, setVal2] = useState(1); let [val3, setVal3] = useState(2);
useState:val1={val1}---val2={val2}---val3={val3}