react16知识点总结,试图一篇搞定react16

当然,这只是react16的基本使用,里面没说react-hook,和redux,因为说这两个庞然大物又是一篇长文章了

总之一句话总结迭代如此快的前端开发如何轻松面对各种需求,只有足够努力,才能毫不费劲

react16知识点目录:

react的基本概念:

React是Facebook开发出的一款JS库 Facebook认为MVC无法满足他们的扩展需求

特点:
1.react不使用模板
2.react 不是一个MVC框架
3.响应式
4. react是一个轻量级的js库

原理:
虚拟DOM , react把DOM抽象成为一个JS对象
diff算法

1.虚拟dom确保只对界面上真正发生变化的部分进行实际的DOM操作
2.逐层次的来进行节点的比较

react历史:
2013年发布开源

高阶函数:
定义:如果函数接收的参数是函数或者返回值是函数
例子:promise()/then()/定时器/数组遍历相关方法/
好处:更加动态,更加具有扩展性

高阶组件:
定义:参数为组件,返回值为新的组件的函数,高阶组件是特别的高阶函数
例子:WithRouter(组件)/connect()(组件)
作用:react中用于复用组件逻辑的一种高级技巧

使用装饰器优化react中的高阶组件:

@connect(
   state => ({
    
     userInfo: state.userInfo }),
{
    
    deleteUserInfo}
)
class Admin extends Component {
    
    
  constructor(props) {
    
    
    super(props)
  }
}
export default Admin

class原版:

class Admin extends Component {
    
    
  constructor(props) {
    
    
    super(props)
  }
}

export default connect(
  state => ({
    
     userInfo: state.userInfo }),
  {
    
    deleteUserInfo}
)(Admin)

特别的:要想使用装饰器,需要安装babel

npm install @babel/plugin-proposal-decorators

还需要改写根目录的配置文件

纯函数:
1.一类特别的函数:只要是同样的输入(实参),必定得到同样的输出(返回)
2.必须遵守以下一些约束

  • a:不得改写参数数据
  • b:不会产生任何副作用,例如网络请求,输入和输出设备
  • c:不能调用Date.now()或者Math.random()等不纯的方法

3.redux的reducer函数必须是一个纯函数

react开发环境搭建:

  1. react.js 核心文件
  2. react-dom.js 渲染页面中的DOM,当前文件依赖于react核心文件
  3. babel.js ES6转换成es5,JSX转换成javascript,现今浏览器进行代码的兼容

下载:
react核心包 npm i react --save
react-dom npm i react-dom --save
babel npm i babel-standalone --save

jsx的理解:

jsx = javascript + xml 扩展javascript `优点`:
1.执行的效率更快
2.类型更安全,编译的过程能及时的发现错误
3.在使用jsx的时候,编写模板更加的快速

注意:jsx中html标签必须按照w3c的规范来写,标签必须闭合
基本的react环境搭建:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</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.js"></script>
</head>
<body>
    <!-- 创建根节点,这个节点下的内容就会被react管理 -->
    <div id="demo">

        
    </div>
</body>
<script type="text/babel">

    let myDom = <h1>你好世界1</h1>
    ReactDOM.render(myDom,document.getElementById("demo"))

    
</script>
</html>

Render函数:

注意:render里不允许写setState是指在render的return之前不允许写,因为会造成死循环return体里面是可以写的

在jsx中的注释语法:  {
    
    /* xxxxxxxxxx */}

多行标签的使用:想要使用多行标签,必须使用以一个父元素包裹,为了更好的识别,还通过()包裹

<script type="text/babel">
    let myDom = (
        <div>
        <h1>你好世界1</h1>
        <h1>你好世界2</h1>
        <h1>你好世界3</h1>
        {
     
     /*我是注释的内容*/}
        </div>
    )
    ReactDOM.render(myDom,document.getElementById("demo"))
</script>

使用表达式,完成轻量级的数据展示:

<script type="text/babel">
    let text = 'nihao'
    let num = 34
    let obj = {
     
     
        name: "34332"
    }
    let arr = [423, 534, 124]
    let fun = (data) => {
     
     
        return "我是函数:" + data
    }
    let myDom = (
    <div>
        <h1>{
     
     text}-{
     
     num}-{
     
     obj.name}-{
     
     arr}-{
     
     fun("aaaaa")}</h1>
    </div>
    )
    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

引入外部jsx代码文件

<script src="/js/jsx.js" type="text/babel"></script>

Jsx语法的进阶使用:

<script type="text/babel">

    三目运算符
    let phone = 42342
    let myDom = (<div>
        {
     
     phone > 3000 ? "很贵" : "便宜"}
</div>)


    渲染数组
    let arr = [
        <p key="1">1</p>,
        <p key="2">2</p>,
        <p key="3">3</p>,
        <p key="4">4</p>,
    ]

    myDom = (<div>
        {
     
     arr}
    </div>)

    属性设置
    let text = '点我去百度'
    let url = 'https://www.baidu.com/'
    myDom = <a href={
     
     url}>{
     
     text}</a>

    修改样式,注意:必须是一个对象{
     
     }
    let myStyle = {
     
      color: "red", fontSize: "80px", }
    myDom = <p style={
     
     myStyle}>修改样式</p>


    设置class,注意:在jsx中不能直接使用class,因为它是js的关键字,需要使用className
myDom = <p className="demo">修改样式</p>

    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

react列表渲染,遍历数组:

很灵活
 <script type="text/babel">

    //react列表渲染  map(),建议使用map对数组进行循环遍历
    let arr = ["吃饭", "睡觉", "哈哈"]
    let myDom = arr.map((item, index) => {
     
     
        return (
            <p key={
     
     index}>{
     
     item}</p>
        )
    })

    //  for  in  循环遍历数组
    let arrList = () => {
     
     
        let newArr = ["gsdgsd", "sfsd", "dgsdgs"]
        let emptyArr = []
        for (let index in newArr) {
     
     
            emptyArr.push(<p key={
     
     index}>{
     
     newArr[index]}</p>)
        }
        return emptyArr
    }
    myDom = arrList()

    
    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

实例:点击字体颜色变红色

<script type="text/babel">

    let clickIndex = -1

    function fn1() {
     
     
        let arr = ["吃饭", "睡觉", "哈哈"]
        let myDom = arr.map((item, index) => {
     
     
            return (
                <p
                    key={
     
     index}
                    style={
     
     {
     
      color: clickIndex == index ? 'red' : '' }}
                    onClick={
     
     () => {
     
      console.log(index); clickIndex = index; render() }}
                >{
     
     item}</p>
            )
        })
        return myDom
    }

    function render() {
     
     
        ReactDOM.render(fn1(), document.getElementById("demo"))
    }

    render()

</script>

遍历对象:

<script type="text/babel">

    遍历对象,Object.keys(),Object.values(),map()
    let obj = {
     
     
        name: "EFawfaw",
        age: 13
    }

    let myDom
    console.log(Object.keys(obj))
    console.log(Object.values(obj))

    myDom = Object.keys(obj).map((item, index) => {
     
     
        return (<p> {
     
     item} ---{
     
     obj[item]} </p>)
    })

    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

组件化开发:

组件的特点什么?
 	 1.高内聚低耦合
	 2.高内聚就是把逻辑紧密的内容放在一个组件当中
 	 3.低耦合把不同组件的依赖关系尽量弱化,每个组件尽可能的独立

组件当中的重要内容:
 	 1.构建方式
 	 2.组件内的属性
 	 3.生命周期

组件的演变过程:
  1.传统的组件的特点
       +简单的封装
       +简单生命周期的呈现
       +明显的数据流动
    当一个项目比较复杂的时候,传统的组件化根本不能很好的把结构样式和行为结合,
    让项目难以维护
   
  2.react的组件分为三个部分
       +属性
       +状态state
       +生命周期
    react的组件是  一个非常重要的概念,通过组件可以把页面中的ui部分切分成  独立  
    高复用性的部件,让每个开发者更加专注于一个个独立的部件

组件与组件化:
1.组件就是用来实现页面局部功能的代码集合,简化页面的复杂程度,提高运行效率、
2.组件化 当前程序都是使用组件完成的,那么就是一个组件化的应用

组件的创建:两种方式
1.函数式组件/无状态组件(组件首字母必须大写)

<script type="text/babel">

    let myDom
    //无状态组件的创建
    //注意:组件首字母必须大写
    function MyCom() {
     
     
        return (<p>我是一个无状态的组件</p>)
    }

    //组件就是自定义标签
    //调用组件
    myDom = (
        <div>
           //单标签和双标签都可以调用组件
            <MyCom> </MyCom>
            <MyCom />
        </div>
    )

    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

函数式组件/无状态组件 父子组件的使用

<script type="text/babel">
    父子组件的使用

    子组件的创建
    function MyComA() {
     
     
        return (<p>我是第1个组件</p>)
    }

    function MyComB() {
     
     
        return (<p>我是第2个组件</p>)
    }

    function MyComC() {
     
     
        return (<p>我是第3个组件</p>)
    }

    父组件的创建
    function ParentCom() {
     
     
        return (
            <div>
                <MyComA />
                <MyComB />
                <MyComC />
            </div>
        )
    }

    let myDom
    myDom =ParentCom()

    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

2.类组件(组件首字母必须大写)

<script type="text/babel">
    
    //创建类组件
    class MyCom extends React.Component{
     
     
        render(){
     
     
            return (
                <div>类组件</div>
            )
        }
    }

    let myDom
    myDom = <MyCom/>

    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

受控组件和非受控组件:推荐使用受控组件

没交给state管理,而是通过操作dom改变value的值,非受控组件

用户名:<input type="text"  ref={
    
     (input)=>{
    
      this.nameInput= input} }/>

交给state管理,不用操作dom去改变value的值,受控组件

密码:<input type="password"   value={
    
    this.state.pwd} onChange={
    
    this.handleChange } />

Props数据传递:父传子,子传父

props 是react中一个重要的属性,是组件对外的接口,通过props就可以从组件的外部向组件的内部进行数据的传递,也可以完成父组件给子组件数据的传递
注意:无论是函数式组件还是类组件,我们都不能修改自身的props
父传子:
1.函数式组件/无状态组件 的props数据传递:

  <script type="text/babel">

    function Com(props) {
     
     
        console.log(props)
        return (
            <div>我是函数式/无状态组件,外部传递的数据是--{
     
     props.text}</div>
        )
    }
  


    let myDom
    	传递一个数据,直接写在组件中
    let demoVar = "我是数据"

    	传递多个参数,展开运算符
    let obj ={
     
     
        text:"我是text",
        demoVar: "我是数据"
}


      myDom = <Com  text="我是text" demoVar={
     
     demoVar}  />
           //等效于
      myDom = <Com    {
     
     ...obj}     />
     
    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

2.类组件 的props数据传递:

<script type="text/babel">

     class MyCom extends React.Component{
     
     
        render(){
     
     
            return (
                <div>
         我是类组件--{
     
     this.props.text} -- {
     
     this.props.demoVar}
                </div>
            )
        }
     }
  

    let myDom
    传递一个数据,直接写在组件中
    let demoVar = "我是数据"

    传递多个参数,展开运算符
    let obj ={
     
     
        text:"我是text",
        demoVar: "我是数据"
    }

     myDom = <MyCom  text="gesgse" demoVar={
     
     demoVar}  />
    	     //等价于
     myDom = <MyCom  {
     
     ...obj} />
    
     
    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

子传父:
本质上也是父传子,只不过是传入的function,通过function巧妙的实现子传父的效果

<script type="text/babel">
    class Child extends React.Component {
     
     
        constructor(props) {
     
      super(props) }

        render() {
     
     
            return (
                <div>
                    <button onClick={
     
     this.props.fun.bind(this, "aaaaa")}>传递数据给父组件</button>
                    我是child
                </div>
            )
        }
    }

    class MyCom extends React.Component {
     
     
        constructor(props) {
     
      super(props) }

        func = (data) => {
     
     
            console.log(data)
        }

        render() {
     
     
            return (
                <div>
                    <Child fun={
     
     this.func} />
                </div>
            )
        }
    }

    let myDom
    myDom = <MyCom />

    ReactDOM.render(myDom, document.getElementById("demo"))

</script>

props验证和默认值:

1.函数式组件/无状态组件的props验证和默认值
引用prop-types库: npm install --save prop-types
注意: 上线模式中取消props验证

<script type="text/babel">

    function MyCom(props) {
     
     
 // react 15x版本设置默认值方法: ||的方式完成  ,  16之后不允许修改props了
  props.name = props.name || "我是name的默认值"
        return (
            <div>我是无状态组件--{
     
     props.name}--{
     
     props.age}</div>
        )
    }

  //  react 16版本之后的设置默认值方法 
    MyCom.defaultProps = {
     
     
        name: "我是name的默认值",
        age:12
    }

    
    MyCom.propTypes={
     
     
        name:PropTypes.string,  //验证name这个props,传递进来的必须是number类型
        age:PropTypes.number.isRequired   //是数字类型,且不能为空
    }

    let myDom
    myDom = <MyCom />

    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

2.类组件的props验证和默认值

<script type="text/babel">

  class Mycom extends React.Component{
    
    

    //类中设置props默认值 (推荐)
    static defaultProps={
    
    
        name:"类中设置默认值"
}

//类组件的props验证(方式1)
static	propTypes={
    
    
    name:PropTypes.string
  }


      render(){
    
    
         return (
             <div>我是类组件--{
    
    this.props.name}</div>
         )
      }
  }
   
  //类组件中  传统的设置默认值
//    Mycom.defaultProps ={
    
    
//       name:"我是name的默认值"
//   }
   
   //类组件的props验证(方式2)
  Mycom.propTypes={
    
    
    name:PropTypes.string
  }
  
    let myDom
    myDom = <Mycom />

    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

案例:点击显示隐藏

<script type="text/babel">

    let bool = true //用来保存内容显示隐藏的状态
    let MyCom = (props) => {
     
     
        return (
            <div style={
     
     {
     
      display: bool ? "block" : "none" }}>
                {
     
     props.arr.map((item, index) => {
     
     
                    return (<p key={
     
     index}>{
     
     item}</p>)
                })}
            </div>
        )
    }

    let dataArr = ["gesgse", "gsergse", "Stse"]

    let handleClick = () => {
     
     
        bool = !bool
        render()    //刷新页面
    }

    let ParentCom = (props) => {
     
     
        return (
            <div>
                <h1 onClick={
     
     handleClick}>我是父组件</h1>
                <MyCom arr={
     
     dataArr} />
            </div>
        )
    }

    function render() {
     
     
        ReactDOM.render(<ParentCom />, document.getElementById("demo"))
    }

    render()

</script>

state状态:

注意:如果用使用state状态,不能使用函数式/无状态组件

state和props的主要区别:

  • state是可以修改的

  • props对于当前页面的组件来说,是只读的,如果想要修改props,只能修改父组件中的数据

  • props是组件对外的接口,state是组件对内的接口

声明式编程/渲染:
              react中我们只需要关心的是数据,当数据改变的时候会自动发生变化,通过state管理的数据,不再需要手动的render()刷新页面,state状态等同于页面中的数据,状态/数据 改变–页面中对应的数据绑定内容就会被react自动的进行改变,这种方式也称之为 声明式渲染—-一切的数据改变都不用关心,只需要我们声明好数据,react会自动管理更新数据

类组件中state状态的使用: 只有类组件才能使用state进行数据管理

  • this.setState({key:newValue},()=>{})

  • this.state.xxxx

<script type="text/babel">
   class MyCom extends React.Component{
     
     

    //es6中的写法,不管子类写不写constructor,new实例的时候,都会补上constructor
    //我们可以不写,但如果写了constructor后,必须在其中写上super(),super就是指向父类constructor的构造方法
        constructor(props){
     
     
     //如果想在constructor中使用props,那么super必须写上props
            super(props)

            //定义state
            this.state={
     
     
                name:"xixixixixixixi"
            }
        }

        render(){
     
     
            return(
                <div>
                    {
     
     /*  
修改state数据,一定不能直接修改,需要调用this.setState({key:newValue}), this.setState({key:newValue}) 是异步的,react会自动的触发render进行数据渲染
                    */}
              <button onClick={
     
     ()=>this.setState({
     
     name:"aaaaaaaaaaaaaa"})}>
点我改变state数据</button>
                我是一个组件--{
     
     this.state.name}
                    </div>
            )
        }
   }
    let myDom
	myDom = <MyCom />

    ReactDOM.render(myDom,document.getElementById("demo"))

</script>

this.setState异步回调转同步:

import React, {
    
     Component } from 'react'
class App extends Component {
    
    
  state = {
    
    
    count: 0
  }
btnClick = async () => {
    
    
    await this.setStateAsync({
    
     count: this.state.count + 1 })
    console.log(this.state.count)
  }
  setStateAsync = (state) => {
    
    
    return new Promise((reslove) => {
    
    
      this.setState(state, reslove)
    })
  }
  render() {
    
    
    return (
      <div>
        <p>{
    
    this.state.count}</p>
        <button onClick={
    
    this.btnClick}>按钮</button>
      </div>
    )
  }
}
export default App;

ref引用:

表示当前组件实例的引用,它会返回绑定当前ref属性的元素
作用:标记组件内部的元素–方便我们查找
注意:不能在无状态组件当中进行使用,因为无状态组件没有实例

react给我们提供了三种方式进行ref的使用:

  1. 字符串的方式

  2. 回调函数( 推荐 )
              就是在dom节点上或者组件上挂载函数,函数的形参是dom节点,它达到的效果和字符串方式是一样的,都是获取值的引用

  3. React.createRef() (react 16.3新提供的一种方式)
               把值赋给一个变量,通过ref挂载在节点或者组件上,使用ref的current属性拿到这个节点

官方建议:不要过度使用refs对逻辑进行处理,需要优先考虑state

<script type="text/babel">

    class MyCom extends React.Component {
    
    
        constructor(props) {
    
    
            super(props)

            this.myRef = React.createRef()
        }

        fun = () => {
    
    

            //1.字符串的方式
            //需要注意的是,这里不能使用function 定义函数,因为this指向问题
            // console.log(this.refs.demoInput.value)

            //2.回调函数方式
            // console.log(this.textinput.value)

            //3.React.createRef()方式
            console.log(this.myRef)
 			 console.log(this.myRef.current)
        }

        //1.字符串的方式
        render(){
    
    
            return (
            <div>
         			 我是组件
        <input type="text" ref="demoInput"  placeholder="请输入" />    
        <button  onClick={
    
     this.fun }>点击我得到输入框的值</button>
            </div>
            )
        }

        //2.回调函数的方法(推荐)
        render(){
    
    
            return (
            <div>
               我是组件
   <input  type="text"  ref={
    
    (input)=>{
    
     this.textinput = input }}   
placeholder="请输入"  />    
   <button  onClick={
    
     this.fun }>点击我得到输入框的值</button>
            </div>
            )
        }

        //3.React.createRef() 方式
        render() {
    
    
            return (
                <div>
                    我是组件
        <input type="text" ref={
    
    this.myRef} placeholder="请输入" />
        <button onClick={
    
    this.fun}>点击我得到输入框的值</button>
                </div>
            )
        }
    }

    let myDom
    myDom = <MyCom />

    ReactDOM.render(myDom, document.getElementById("demo"))

</script>

react事件处理:this指向,传递参数,传递事件对象

 注意:react绑定事件使用的是小驼峰命名法,在绑定函数的时候不能加(),
           否则函数会立即执行

 修改this指向四种方式:
         1.bind方式原地绑定
         2.函数通过箭头函数创建 (推荐)
         3.constructor中提前绑定
         4.把事件的调用写成箭头函数的调用方式

 函数实参传递两种方式:
		1.bind 		onClick={this.fune.bind(this, "我是参数1")}
        2.箭头函数   onClick={() => { this.fune("我是参数2") }}

 传递事件对象event两种方式:
		1. onClick={(e) => { this.fune("我是参数3", e) }}
        2. onClick={this.fune}  //什么都不传,默认参数为event

注意: 不能使用onClick={this.changeTitle(“xxx”)} 传递参数

<script type="text/babel">

    class MyCom extends React.Component {
    
    
        constructor(props) {
    
    
            super(props)

            3.constructor中提前绑定
            this.func = this.func.bind(this)
        }

        1.bind方式原地绑定 
        funa() {
    
    
            console.log(this)
        }

         2.函数通过箭头函数创建      (推荐)
        fund = () => {
    
    
            console.log(this)
        }

        3.constructor中提前绑定
        func() {
    
    
            console.log(this)
        }

        4.把事件的调用写成箭头函数的调用方式
        fun() {
    
    
            console.log(this)
        }

        传递事件参数event  和  传递普通参数
        fune = (str, e) => {
    
    
            console.log(str)
            console.log(e)
        }

        render() {
    
    
            return (
                <div>
                    {
    
    /*    1.bind方式原地绑定            */}
  <button onClick={
    
    this.funa.bind(this)}>bind的方式绑定</button>

                    {
    
    /*    2.函数通过箭头函数创建          */}
  <button onClick={
    
    this.fund}>函数通过箭头函数创建</button>

                    {
    
    /*    3.constructor中提前绑定         */}
  <button onClick={
    
    this.func}>constructor中提前绑定 </button>

                    {
    
    /*    4.把事件的调用写成箭头函数的调用方式      */}
<button onClick={
    
    () => {
    
     this.fun() }}>把事件的调用写成箭头函数的调用方式 </button>



                    <h1>函数实参传递   和事件对象event传递  </h1>
                    {
    
    /*       传递参数方式1:bind          */}
<button onClick={
    
    this.fune.bind(this, "我是参数1")}>点我传递实参</button>

                    {
    
    /*       传递参数方式2:箭头函数          */}
<button onClick={
    
    () => {
    
     this.fune("我是参数2") }}>点我传递实参</button>

                    {
    
    /*       传递事件对象event方式一:   */}
<button onClick={
    
    (e) => {
    
     this.fune("我是参数3", e) }}>点我传递实参</button>

  			{
    
    /* 传递事件对象event方式二:默认就是接收的参数就是事件对象e */}
<button onClick={
    
    this.fune}>点我传递实参</button>


                </div>
            )
        }
    }

    let myDom
    myDom = <MyCom />
    ReactDOM.render(myDom, document.getElementById("demo"))
</script>

React条件渲染:

条件渲染是什么?
           根据状态的变化只渲染其中的一部分
条件渲染的方式:
                1.if,else语句 jsx中不允许有if
                2. 三目运算符

<script type="text/babel">

    class MyCom extends React.Component {
     
     
        constructor(props) {
     
     
            super(props)

            this.state = {
     
     
                bool: true
            }
        }

        click() {
     
     
            this.setState({
     
     
                bool: !this.state.bool
            })
        }

        render() {
     
     
            let text
            if (this.state.bool) {
     
     
                text = "ni hao"
            } else {
     
     
                text = 'xixixixixi'
            }

            return (
                <div>
     <button onClick={
     
     this.click.bind(this)}>点我改变</button>
     <p> ifelse{
     
     text}</p>
     <p>三元运算符:{
     
     this.state.bool ? "哈哈" : "呵呵"}</p>
                </div>
            )
        }
    }

    let myDom
    myDom = <MyCom />

    ReactDOM.render(myDom, document.getElementById("demo"))

</script>

React状态提升:

状态提升:
          多个组件需要反映相同的变化数据,提升到他们共同的最近的一个父组件中

应用场景:
          多个子组件需要利用到对方状态的情况下,那么这个时候就需要使用到状态提升

<script type="text/babel">

    class Child1 extends React.Component {
     
     
        constructor(props) {
     
      super(props) }

        render() {
     
     
            return (
                <div>
                    我是child1--{
     
     this.props.text}
                </div>
            )
        }
    }


    class Child2 extends React.Component {
     
     
        constructor(props) {
     
      super(props) }

        render() {
     
     
            return (
                <div>
                    我是child2--{
     
     this.props.text}
                </div>
            )
        }
    }


    class MyCom extends React.Component {
     
     
        constructor(props) {
     
     
            super(props)

            this.state = {
     
     
                context: "我是两个子组件都想使用的数据"
            }
        }

         func = () => {
     
     
             this.setState({
     
      context: "我要改变了" }) 
        }

        render() {
     
     
            return (
                <div>
  <button onClick={
     
     this.func}>点我修改两个子组件共用的数据</button>
     <Child1 text={
     
     this.state.context} />
     <Child2 text={
     
     this.state.context} />
                </div>
            )
        }
    }

    let myDom
    myDom = <MyCom />
    ReactDOM.render(myDom, document.getElementById("demo"))

</script>

React脚手架的安装:

create-react-app 是 facebook官方推出的一款react的脚手架

npm install -g create-react-app 全局安装脚手架环境
create-react-app --version 查看版本
create-react-app 项目名 创建项目
npm start 启动项目

空标签:
在react中,空标签<></>或者Fragment标签都可以实现,不在dom中显示的效果

		 <>
            你好我是组件{
    
    parseInt(Math.random()*10)}
        </>
		 <Fragment>
            你好我是组件{parseInt(Math.random()*10)}
        </Fragment>

img图片的引入:

  1. 在public中可以直接引入
  2. 不在public中,需要导入后,通过变量引入 或者 通过require引入
import React, {
    
     Component,Fragment } from 'react';

import a1  from '../assets/a1.png'

class Home extends Component {
    
    
    render() {
    
    
        return (
            <Fragment>
                你好我是组件{
    
    parseInt(Math.random()*10)}public中直接引入,自动会找到
                <img   src="logo512.png" />
                <img   src={
    
    a1}  />
<img   src={
    
     require("../assets/a2.png") } />
            </Fragment>
        );
    }
}

export default Home;

实例应用:

 this.headerList = Array.from(new Array(20)).map((_val, i) => ({
    
    
    icon: require(`./images/头像${
      
      i+1}.png`),    //不能使用import
     text: `头像${
      
      i+1}`,
}));

React常用的代码段:

 rcc: 基本的类结构
 rccp:基本的类结构  +  propTypes类型验证

react中同级传值/兄弟传值:

`方式一:` 安装发布订阅库:npm i --save pubsub-js
import Pubsub  from 'pubsub-js'
class Phone extends Component {
    
    

    fun = () => {
    
    
        Pubsub.publish('evt',"aaaaaa")
    }

    render() {
    
    
        return (
            <div>  我是phone
                <button onClick={
    
    this.fun}>进行同级传递数据</button>
            </div>
        );
    }
}

import Pubsub  from 'pubsub-js'
class News extends Component {
    
    
    constructor(props){
    
    
        super(props)

        Pubsub.subscribe('evt',(msg,data)=>{
    
    
                   msg是触发事件的事件名称
            console.log("phone传递过来数据:"+ data)
        })
}

    render() {
    
    
        return (
            <div>我是news</div>
        );
    }
}

方式二:可以通过react内置的包导入事件中间:

import {
    
    EventEmitter} from 'events'
const myEmitter = new EventEmitter();
export default myEmitter

event.on('event', (e) => {
    
    
    console.log('触发事件');
    console.log(e)  //gfseg
  });
event.emit('event',"gfseg");

React中跨域:

1.正向代理—开发环境 (前端可以做到)
        一个位于客户端和目标服务器之间的代理服务器,为了获取到目标服务器的内容,客户端向代理服务器发送一个请求,代理服务器帮助客户端去目标服务器中获取数据,并且返回给客户端

2.反向代理—上线环境 (需要后端配置)
         可以通过代理服务器来接收网络的请求,然后将这个请求转发给内部的网络服务器上,并且把这个服务器上得到的数据返回给网络请求的客户端,这个时候代理服务器对外的表现就是一个反向代理(掩盖后端服务器真正的地址)

正向代理:
方案一:
找到文件 项目\node_modules\react-scripts\config\ webpackDevServer.config.js

 proxy:{
    
    
      "/api":{
    
    
        target:"http://www.weather.com.cn/data/cityinfo",
        changeOrigin:true,
        "pathRewrite":{
    
    
          "^/api":"/"
        }
      }
    },

方案二:(推荐)

 比如,我请求"http://localhost:3000/news"的时候跨域了
fetch("http://localhost:3000/news")package.json文件中配置跨域的地址
  "proxy":"http://localhost:3000"
然后代码改为fetch("/news")

方案三: 这种方式可以配置多个跨域
安装http-proxy-middleware

npm i http-proxy-middleware

在src/下新建setupProxy.js

const proxy = require("http-proxy-middleware");

module.exports = function(app) {
    
    
    app.use(proxy("/api", {
    
    
        target: "http://localhost:3333" , //配置你要请求的服务器地址
        changeOrigin: true,
    }))
    app.use(proxy("/manage/api", {
    
    
        target: "http://admintest.happymmall.com:7000" ,
        changeOrigin: true,
    }))
};

React路由:

        路由 ----- 根据url的不同来切换对应的组件,实现spa,在页面切换的时候不会刷新整个页面,更接近原生体验

路由有两个库:

react-router:			只提供了一些核心的api
react-router-dom:		提供了更多的一些选项(使用这个居多)

下载:npm install --save react-router-dom

路由模式:
1.hash (HashRouter)
             哈希模式,使用url中的hash值来切换的,带#,刷新时页面不会丢失
2.browser (BrowserRouter )
             历史记录模式,通过h5历史记录api来切换路由,刷新会丢失页面,本地模式不会

如果是HashRouter模式,可以通过 <a href="#/list"> 来跳转
index.js中引入使用: 路由模式包裹使用

import {
    
     BrowserRouter, HashRouter } from 'react-router-dom'
ReactDOM.render(
  <BrowserRouter>
     		 <App />
  </BrowserRouter>
  ,
  document.getElementById('root')
);

配置使用路由: Route标签展示组件

import {
    
    Route}  from 'react-router-dom'
import News from './News'
import Phone from './Phone'
class Home extends Component {
    
    
    constructor(props) {
    
     super(props) }

    render() {
    
    
        return (
            <div>
                <Route path="/news" component={
    
    News} ></Route>
                <Route path="/phone" component={
    
    Phone} ></Route>
            </div>
        );
    }
}

路由导航: 切换路由组件

1. Link标签,切换路由时不会动态的加上class=”active”

 <Link  to="/news">跳转到news</Link>
 <Link  to="/phone">跳转到phone</Link>

2. NavLink标签,切换路由时会动态的加上class=”active”

 <NavLink  to="/news">跳转到news</NavLink>
 <NavLink  to="/phone">跳转到phone</NavLink>

自定义路由的激活class,class=”active”:

<NavLink  to="/CommentAdd"  activeClassName="xixixi">跳转Add</NavLink>
<NavLink  to="/CommentItem" activeClassName="xixixi">跳转Item</NavLink>

实例应用封装: 将外部传入的所有属性传递给NavLink

import React, {
    
     Component } from 'react'
import {
    
    Route,NavLink}  from 'react-router-dom'
export default class MyNvaLink extends Component {
    
    
    render() {
    
    
         将外部传入的所有属性传递给NavLink(很灵活)
        return (
          <NavLink  {
    
    ...this.props} activeClassName="xixi" />
        )
    }
}

exact精准匹配:

				<Route path="/" exact  component={
    
    HOME} ></Route>
                <Route path="/news" component={
    
    News} ></Route>
                <Route path="/phone" component={
    
    Phone} ></Route>

Route 标签加上 exact 表示,只有url http://localhost:3000/ 才匹配到HOME,否则http://localhost:3000/news 也会匹配到 HOME 路由

strict更加精准的匹配: strict存在的时候,exact必须也存在才有效

<Route path="/news" component={
    
    News} ></Route>
<Route  strict exact path="/phone" component={
    
    Phone} ></Route>

http://localhost:3000/phone 可以匹配到phone组件,但是http://localhost:3000/phone/ 不行

Switch标签: 避免组件多次渲染,Switch下从上到下只会匹配一个路由组件

import {
    
     Route,Link,NavLink ,Switch } from 'react-router-dom'
  <Switch>
          <Route path="/news" component={
    
    News} ></Route>
          <Route path="/news" component={
    
    News} ></Route>
   </Switch>

渲染的时候只会出现一次News组件

实例应用:404页面

import {
    
     Route,Link,NavLink ,Switch } from 'react-router-dom'
  <Switch>
//此时/news可以匹配到home,从而不再继续渲染其他路由,exact就是来填这个坑的
       <Route path="/" component={
    
    Home} ></Route>
          <Route path="/news" component={
    
    News} ></Route>
          <Route path="/Nine" component={
    
    Nine} ></Route>
<Route  component={
    
    Nofound404} ></Route>
  </Switch>

News和Nine匹配不到了就会显示404页面

路由重定向:Redirect

import {
    
     Route, Switch ,Redirect} from 'react-router-dom'
<Redirect   from="/" to="/home" exact  />

二级路由/嵌套路由:

  在一级路由的组件中,再嵌套路由,并且重定向标签改为
<Redirect   from="/" to="/home/homeChild" exact  />

withRouter:是一个高阶组件
       让不是路由切换的组件也具有路由切换组件的三个属性( location,match,history ),监控路由的变化

加上withRouter后,props中会多出location,match,history三个对象

import {
    
     Route,Redirect,withRouter} from 'react-router-dom'
class Home extends Component {
    
    
    constructor(props) {
    
    
         super(props)
         props.history.listen((link)=>{
    
    
            console.log(link)
         })
     }
    render() {
    
    
        return (
            <div>
            <Route path="/news" component={
    
    News} ></Route>
            <Route path="/phone" component={
    
    Phone} ></Route>
            <Redirect   from="/" to="/news" exact  />
        );
    }
}
export default withRouter(Home);

History:监控路由的变化(前提是需要包裹在withRouter中)

 constructor(props) {
    
    
         super(props)
         props.history.listen((link)=>{
    
    
            console.log(link)
         })
     }

this.props.history还具有以下方法:

push(location)
replace(location)
go(n)
goBack()
goForward()

编程式导航: 通过history.push(‘/xxxx’),history.replace(‘/xxxx’) 实现
前进,可以后退回来

<button onClick={
    
    ()=>{
    
     this.props.history.push('/phone') }}>编程式导航</button>

替换,后退不回来

<button onClick={
    
    ()=>{
    
     this.props.history.replace('/phone') }}>编程式导航</button>

路由传参:

  1. params方式进行传参:match
    1.需要在路由规则中设置传递的接受参数 :xxx
    2.发送参数,直接在跳转路径后进行编写
    3.接受props.match.params.参数名

优点: 刷新地址,参数依然存在
缺点: 只能传递字符串,并且参数过多的时候url会变得丑陋

<NavLink to="/phone/我是参数121321">跳转到phone</NavLink>
<Route path="/phone/:id" component={
    
    Phone} ></Route>
class Phone extends Component {
    
    

    componentDidMount(){
    
    
         console.log(this.props.match.params)
         console.log(this.props.match.params.id)
    }
   
    render() {
    
    	 return (	<div>我是Phone</div>	); }
}
  1. query方式:location (推荐)
    1.不需要在路由规则中进行传递参数的配置
    2.直接发送数据
    3.使用this.props.location.queury.xxx
<NavLink to={
    
    {
    
    pathname:"/phone",query:{
    
    name:"小明"}}}>
跳转到phone
</NavLink>
<Route path="/phone" component={
    
    Phone} ></Route>
class Phone extends Component {
    
    

    componentDidMount(){
    
     
         console.log(this.props.location.query)
    }
   
    render() {
    
    	 return (	<div>我是Phone</div>	); }
}

生命周期:

`react生命周期的理解:`
  1. 组件对象从创建到死亡它会经历特定的生命周期阶段
  2. React组件对象包含一系列的勾子函数(生命周期回调函数), 在生命周期特定时刻回调
  3. 我们在定义组件时, 可以重写特定的生命周期回调函数, 做特定的工作

生命周期流程图:
在这里插入图片描述

生命周期详述:

  1. 组件的三个生命周期状态:
    Mount:插入真实 DOM
    Update:被重新渲染
    Unmount:被移出真实 DOM

2) React 为每个状态都提供了勾子(hook)函数
componentWillMount() 组件将要被挂载在dom上
componentDidMount() 组件已经被挂载在dom上
componentWillUpdate() 组件将要被更新
componentDidUpdate() 组件已经被更新
componentWillUnmount() 组件将要被卸载

生命周期的流程:
第一次初始化渲染显示的流程:

第一次初始化渲染显示: ReactDOM.render() 调用
   constructor(): 创建对象初始化state
   componentWillMount() : 将要插入回调
   render() : 用于插入虚拟DOM回调
   componentDidMount() : 已经插入回调

每次更新state的流程:

每次更新state: this.setSate() 调用
componentWillUpdate() : 将要更新回调
render() : 更新(重新渲染)
componentDidUpdate() : 已经更新回调

移除组件的流程:

移除组件: ReactDOM.unmountComponentAtNode(containerDom)
componentWillUnmount() : 组件将要被移除回调

重要的勾子:

1)  render(): 初始化渲染或更新渲染调用
2)  componentDidMount(): 开启监听, 发送ajax请求
3)  componentWillUnmount(): 做一些收尾工作,: 清理定时器
4)  componentWillReceiveProps(): 监视接收到新的props,第一次不触发

特殊的勾子:

  1. shouldComponentUpdate(): 重写此方法return false,true可以控制页面是否更新
  2. this.forceUpdate(): 调用此方法,就算state,接收的props没有改变,也可以强制刷新一次页面

即将废弃的钩子: 现在使用会出现警告,下个版本需要加上UNSAFE_前缀

componentWillMount()
componentWillReceiveProps()
componentWillUpdate()   

注意:
      componentsDidMount只会在组件加载完后执行一次,之后更新state、props都不会执行,除非重新加载组件。componentWillReceiveProps在组件传进来的props被更改时,将被调用。所以可以在componentWillReceiveProps函数里改变state来重新获取数据。

新的生命周期图:

在这里插入图片描述

新钩子:

getDerivedStateFromProps(props,state):钩子用于取代 componentWillMount、 
											  	  componentWillUpdate  
                                                  componentWillReceiveProps
getSnapshotBeforeUpdate(props,state):介于componentDidUpdate和render之间
componentDidUpdate(props,state,arg)可以接收到getSnapshotBeforeUpdate返回的参数

实例:

 static getDerivedStateFromProps(props,state){
    
    
      console.log('--getDerivedStateFromProps--',props,state);
      //return {data:props.name}  
      return props  //会把props里的对象追加到this.state中
    } 

注意:
     想使用getSnapshotBeforeUpdate,必须和componentDidUpdate一起使用,
componentDidUpdate第三个参数接收getSnapshotBeforeUpdate的返回值

getSnapshotBeforeUpdate(props,state){
    
    
      console.log('--getSnapshotBeforeUpdate--',props,state);
      return 'peiqi'
}

  componentDidUpdate(props,state,data){
    
       //data是'peiqi'
      console.log('--componentDidUpdate--',props,state,data);
}

生命周期的流程:
1.【初始化】

 触发条件:ReactDOM.render(<MyComponent/>)
      constructor()
      static getDerivedStateFromProps(Props, prevState) ----- 【新增】
      render():提供虚拟DOM,可能会调用多次(1+n)componentDidMount():启动定时器、发送Ajax请求、只执行一次。

2.【更新】

触发条件:this.setState({
    
    })
  static getDerivedStateFromProps(Props, prevState)
  shouldComponentUpdate()
  render()
  getSnapshotBeforeUpdate() ---- 【新增】
  componentDidUpdate()

3.【卸载】

触发条件:ReactDOM.unmountComponentAtNode()
 componentWillUnmount():做收尾工作,例如:清除定时器等,该回调只会执行一次。

玩react有段时间了,有需求就做一次总结吧,为同学,为友人,为自己,也为前端爱好者,更为大环境,如有错误还望指出

猜你喜欢

转载自blog.csdn.net/fesfsefgs/article/details/107570471