这可能是全网最全react入门教程笔记(4W字劝退警告)

这可能是全网最全react入门教程笔记(4W字劝退警告)

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-tyoesnpm 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-appnpx 来自 npm 5.2+ 或更高版本, 查看 npm 旧版本的说明
  • npm init react-app my-appnpm init <initializer> 在 npm 6+ 中可用
  • yarn create react-app my-appyarn create 在 Yarn 0.25+ 中可用

官方说明:

If you use npm 5.1 or earlier, you can’t use npx. Instead, install create-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}

React之redux

React项目实战之移动端

React项目实战之后台管理系统开发

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值