• react


    目录

    React简介

    • React起源于Facebook 2013年6月发布
    • React是一个用于构建用户界面的JavaScript库
    • React拥有较高的性能,代码逻辑非常简单,越来越多的人已经开始关注和使用它

    除去ie8以下版本,其余浏览器都可以很好的支持)

    React开发背景

    Facebook需要解决的问题:构建数据不断变化的大型应用。
    
    					大量的DOM操作				<==		自动DOM操作 
    数据变化
    					DOM操作逻辑极其复杂		<==		状态对应内容
    

    虚拟DOM----减少更新次数,减少更新区域

    	虚拟dom相当于在js和真实dom中间加了一个缓存。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都首先重新构建整个DOM树(减少页面更新次数),然后React将当前整个DOM树和上一次的DOM树进行对比(DOM Diff算法-最小化页面重绘),得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新
    

    为什么虚拟dom会提高性能

    1. 虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。
    2. 用JavaScript对象结构表示DOM树的结构,然后用这个树构建一个真正的DOM树,插到文档当中当状态变更时,重新构建一颗新的对象树。然后用心的树和旧的树进行对比,记录两棵树的差异把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了
    

    diff算法的作用

    计算出虚拟DOM中真正变化的部分,并值针对该部分进行原生DOM操作,而非重新渲染整个页面
    

    React的diff算法

    什么是调和?
    	将Virtual(虚拟)DOM树转换成actual(真实)DOM树的最少操作的过程称为调和
    什么是React diff算法?
    	diff算法是调和的具体实现
    

    React特点

    * 声明式设计--React采用声明范式,可以轻松描述应用。(开发者只需要声明显示内容,react就会自动完成)
    * 高效--React通过对DOM的模拟,最大限度地减少与DOM的交互
    * 灵活--React可以与已知的库或框架很好地配合。
    * 组件--通过React构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。(把页面的功能拆分成小模块-每个小模块就是组件)
    * 单向数据流--React是单向数据流,数据主要从父节点传递到子节点(通过props).如果顶层(父级)的某个props改变了,React会重新渲染所有的子节点 
    

    React组件特性

    React的核心就是组件:组件的设计目的是提高代码复用率,降低测试难度和代码的复杂程度。
    提高代码复用率:组件将数据和逻辑进行封装。
    降低测试难度:组件高内聚低耦合(各个元素高集成度低关联性),很容易对单个组件进行测试。
    代码的复杂程度:直观的语法,可以极大提高可读性。
    

    React依赖包下载

    react核心包 : npm install react --save
    react-dom: npm install react-dom --save
    babel包 : npm install bable-standalone --save
    
    注意:下载好的文件在node_modules目录。
    1.找到react目录,找到这个目录下的umd目录下react.development.js
    2.在react-dom/umd目录下找到react-dom.development.js
    3.node_modules目录下找到babel-standalone目录下的babel.js
    

    React开发环境搭建

    react.js文件是创建React元素和组件的核心文件,react-dom.js文件用来把React组件渲染为DOM,此文件依赖于react.js文件,需在其后被引入。
    Babel的主要用途是将ES6转成ES5 同时可以把JSX 语法转换新标准的JavaScript代码让现今浏览器兼容的代码
    <script type="text/javascript" src="js/react.min.js"></script>
    <script type="text/javascript" src="js/react-dom.min.js"></script>
    <script type="text/javascript" src="js/babel.min.js"></script>
    

    React--根DOM节点

    页面中需要有一个div容器,容器中的内容都会被React DOM所管理。
    这个容器叫做根DOM节点
    
    注意:通常React开发应用时一般只会定义一个根节点
    

    React--JSX

    JSX=JavaScript XML就是JavaScript和XML结合的一种格式。是JavaScript的语法扩展。React腿甲你在React中使用JSX来描述用户界面。当遇到<,JSX就当HTML解析,遇到{就当JavaScript解析.
    

    JSX优点

    1.JSX执行更快,因为他在编译为JavaScript代码后进行了优化
    2.他是类型安全的,在编译过程总就能发现错误
    3.使用JSX编写模板更加快速
    

    ReactDOM.render()

    ReactDOM.render是React的最基本方法,用于将模板转为HTML语言,并插入页面指定的根DOM节点
    <div id="demodiv"></div>
    <script type="text/babel">
    	//创建虚拟DOM元素对象
    	var myDom=<h1>hello React</h1>
    	//把虚拟DOM渲染到真实DOM中
    	ReactDOM.render(myDom,document.getElementById("demodiv"))
    </script>
    

    JSX--注释

    注释/*内容*/ hrml标签内注释{/*最外层有花括号*/}
    <script type="text/babel">
    	var myDom = <div>
      								{/*jsx注释这样玩*/}
      hello world
      						</div>
      ReactDOM。render(myDom,document.getElementById("demodiv"))
    </script>
    
    多个 HTML 标签,需要使用一个 父 元素包裹
    使用圆括号包裹养成好习惯
    
    <script type="text/babel">
     	var myDom = (
      						<div>
        						<h1>第一行</h1>
        						<h1>第二行</h1>
        				 </div>	
      ) 
    ReactDOM。render(myDom,document.getElementById("demodiv"))
     </script>
    

    jsx独立文件使用

    React JSX 代码可以放在一个独立文件上创建一个 demoreact.js 文件
    页面中引用注意script中type属性
    <div id="demo"></div>
    <script type="text/babel" src="demoreact.js"></script>
    

    JSX绑定属性

    jsx中也可以使用大括号来定义以JavaScript表达式为值得属性:
    	<img src={user.url} />
      切记使用了大括号包裹的 JavaScript 表达式时就不要再到外面套引号了。JSX 会将引号当中的内容识别为字符串而不是表达式。
    

    定义样式

    定义演示对象,以style属性引用一个对象
    样式名以驼峰命名法表示,入text-align需写成textAlign
    默认像素单位是px
    var _style = {200,textAlign:"center",border:"1px solid #f0f"};
    ReactDOM.render(
    	<form style={_style}>
    		age:<input placeholder="请输入你的年龄"/>
    	</form>
    	document.getElementById("demo")
    );
    

    引用外部样式

    引用外部样式时, 不要使用class作为属性名, 因为class是js的保留关键字。JSX 语法上更接近 JavaScript 而不是 HTML,所以 React DOM 使用 className(小驼峰命名)来定义属性的名称,而不使用 HTML 属性名称的命名约定。
    React.render(
    	<form className="redtxt">
    		age:<input placeholder="请输入你的年龄"/>
    	</form>
    	document.getElementById("demo")
    )
    

    jsx规则扩展

    1. html的value属性要写成:defaultValue
    2. html的checked属性要写成:defaultChecked
    3. style 里面写对象
    4. class 写className

    React列表渲染

    React中使用的map()进行列表的渲染
    <script type="text/babel">
     	let arr=[11,22,33,44,55];
    	let newhtml=arr.map((item,index)=>{
        	return <p>{index}----{item}</p>
      })
      ReactDOM.render(newhtml,document.getElementById("demo"))
     </script>
    

    React列表渲染--keys

    Keys 可以在 DOM 中的某些元素被增加或删除的时候帮助 React 识别哪些元素发生了变化。因此要给数组中的每一个元素赋予一个确定的标识。
    一个元素的key最好是这个元素在列表中拥有的一个独一无二的字符串
    let arr=[111,222,333,444,555,666,777,888,999];
        let htmlarr=[]
        for(var i=0;i<arr.length;i++){
              htmlarr.push(<li key={i}>{arr[i]}</li>)
        }
      let com=(
          <ul>
              {
    
                   htmlarr   
              }
          </ul>
    
      )
      ReactDOM.render(com,document.querySelector("#demodiv"))
    

    如何让render重新渲染

    <!--不要忘记在每次点击的时候调用控制渲染的函数-->
    <div id="demodiv"></div>
    <script type="text/babel">
        // 点击变色
        let arr=[
            {name:"xixi1",age:181},
            {name:"xixi2",age:182},
            {name:"xixi3",age:183},
            {name:"xixi4",age:184},
            {name:"xixi5",age:185}
        ]
    
        let num=-1;
    function el(){
      
        return (
            <table border="1">
                <tbody>
                    {
                        arr.map((v,i)=>{
                            return (
                                <tr key={i} onClick={()=>{num=i;render(),console.log(num)}} style={{backgroundColor:i==num?'red':''}}>
                                    <td>{v.name}</td>    
                                    <td>{v.age}</td>    
                                </tr>
                            )
                        })  
                    }
                </tbody>    
            </table>
        )
    } 
     function render(){
        ReactDOM.render(el(),document.querySelector("#demodiv"))
     }
     render()
    

    对象取值

    object.keys()返回一个数组类型 值是方法中对象的键(key)
    // 是吧对象转换数组  数组里面的值是 原始对象的key
    
    Object.values()返回一个数组类型 值是方法中对象的值(value)
    // 是吧对象转换数组  数组里面的值是 原始对象的val
    
    Object.entries()返回一个数组类型 值是方法中对象的键和值
    

    React遍历对象

    <div id="demodiv"></div>
    <script type="text/babel">
    // 遍历对象
    var obj={
        name:"xixi",
        age:18,
        sex:"男",
        love:"女"
    }
    function el(){
               return Object.values(obj).map((v,i)=>{
                    return (
                        <h1 key={i}>{v}</h1>
                    )
                })
            }
      ReactDOM.render(el(),document.querySelector("#demodiv"))
    

    模块与模块化

    模块:向外提供特定功能的js文件,提高js的复用率简化编写提高循行效率
    模块化:当应用的js都是用js模块编写的,这个应用就是模块化应用
    

    组件与组件化

    组件:用来实现页面局部功能效果的代码合集(html/css/js)简化复杂页面的编码,提高运行效率
    组件化:当应用多使用组件的方式来完成,这个应用就是一个组件化应用
    

    组件的概念

    组件是React中非常重要的概念--是可以将UI切分成一些独立的、可复用的部件,这样你就只需专注于构建每一个单独的部件
    

    定义自定义组件--函数组件/无状态组件

    组件 首字母大写 首字母大写 首字母大写并且其后每个单词首字母大写
    <div id="demodiv"></div>
    <script type="text/babel">
    //    无状态组件  函数组件 工厂方法组件
    function MyCom(){
        return (
            <h1>我是一个组件</h1>
        )
    }
    function Fu(){
        return(
            <div>
                <MyCom/>
            </div>
        )
    }
    ReactDOM.render(<Fu/>,document.querySelector("#demodiv"))
    </script>
    

    创建类组件

    需要注意类名的首字母必须大写
    一个组件类必须必须必须要实现一个 render 方法。这个方法必须要返回一个jsx元素。必须要用一个外层的jsx元素把所有的内容包裹起来,返回并列的多个元素需要有个父元素包裹
    语法:
    class MyCom extends React.Component{
                  render(){
                      return <div>我是组件</div>
                  }
              }
    
    <div id="demodiv"></div>
    <script type="text/babel">
    //    创建类组件
    class MyCom extends React.Component{
        render(){
            return(
                <div>
                    <h1>我是类组件</h1>    
                </div>
            )
        }
    }   
    class Fu extends React.Component{
        render(){
            return (
                <div>
                    <MyCom/>    
                </div>
            )
        }
    }
    ReactDOM.render(<Fu/>,document.querySelector("#demodiv"))
    </script>
    

    定义父子组件

    通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离
    <div id="demodiv"></div>
    <script type="text/babel">
    //    创建类组件
    class MyCom extends React.Component{
        render(){
            return(
                <div>
                    <h1>我是类组件</h1>    
                </div>
            )
        }
    }   
    class Fu extends React.Component{
        render(){
            return (
                <div>
                    <MyCom/>    
                </div>
            )
        }
    }
    ReactDOM.render(<Fu/>,document.querySelector("#demodiv"))
    </script>
    

    props

    props 是组件对外的接口。使用props就可以从外部向组件内部进行数据传递 完成父组件传值给子组件
    注意:Props对于使用它的组件来说,是只读的。一旦赋值不能修改。也就是说props的值是不可变的只能在渲染的时候传入无法动态赋值。
    
    组件无论是使用无状态组件还是通过 类组件声明,都决不能修改自身的 props。
    
    <div id="demodiv"></div>
    <script type="text/babel">
    //    props传值 函数组件
    let arr=[
        {title:"jdbc1",content:"我是内容哦1"},
        {title:"jdbc2",content:"我是内容哦2"},
        {title:"jdbc3",content:"我是内容哦3"}]
    function MyCom(props){
        return(
            <div className="item">
                <span>{props.title}</span> 
                <br/>   
                <span>{props.content}</span>    
            </div>
        )
    }
    function Fu(){
        let el=arr.map((v,i)=>{
            return (
                <MyCom title={v.title} content={v.content}/>
            )
        })
        return(
            <div className="con">
                {el}
            </div>
        )
    }
        ReactDOM.render(<Fu/>,document.querySelector("#demodiv"))
    </script>
    
    
    <div id="demodiv"></div>
    <script type="text/babel">
    //    props传值 函数组件
    let arr=[
        {title:"jdbc1",content:"我是内容哦1"},
        {title:"jdbc2",content:"我是内容哦2"},
        {title:"jdbc3",content:"我是内容哦3"},]
    function MyCom(props){
        let {title,content}=props.data
        return(
            <div className="item">
                <span>{title}</span> 
                <br/>   
                <span>{content}</span>    
            </div>
        )
    }
    function Fu(){
        let el=arr.map((v,i)=>{
            return (
                <MyCom data={v}/>
            )
        })
        return(
            <div className="con">
                {el}
            </div>
        )
    }
    ReactDOM.render(<Fu/>,document.querySelector("#demodiv"))
    </script>
    
    在参数较多的时候可以把Object类型进行传递
    <div id="demodiv"></div>
    <script type="text/babel">
    //    props传值 函数组件
    function MyCom(props){
        return(
            <div className="item">
                <span>{props.name}</span> 
                <br/>   
                <span>{props.age}</span>    
                <br/>   
                <span>{props.sex}</span>    
                <br/>   
                <span>{props.love}</span>    
            </div>
        )
    }
    function Fu(){
          let obj={
              name:"我是name",
              age:"我是age",
              sex:"我是sex",
              love:"我是love"
          }
              return (
                  <div>
                      <MyCom {...obj}/>  
                  </div>
    
              )
      }
        ReactDOM.render(<Fu/>,document.querySelector("#demodiv"))
    </script>
    

    逆向传值

    import React,{Component}from 'react'
    export default class list extends Component{
      constructor(props){
        super(props)
      }
      fun=()=>{
        this.props.myClick("我是传递给父组件的数据")
        //使用props调用父组件传递过来的函数 并传入参数
      }
      render(){
        return(
        	<div>
            <p>我是子组件</p>
            {/* 1.子组件创建事件调用函数*/}
          	<button onClick={this.fun}>点击逆向传值</button>	
          </div>
        )
      }
    }
    import React,{Component}from 'react'
    import List from "./list"
    export default class home extends Component{
      constructor(props){
        super(props)
      }
      fun=(val)=>{
        console.log(val)
        //1.父组件创建函数并且定义形参接受传递的数据
      }
      render(){
        return (
        	<div>
          	父组件
            <List myClick={this.fun}/>
            {/*传递给子组件*/}
          </div>
        )
      }
    }
    

    同级传值---pubsub-js

    下载 npm install --save pubsub-js
    在第一个组件中进行数据抛出PubSub.publish("事件名","数据")
    import React,{Component}from "react"
    import PbuSub from "pubsbu-js"
    export default class demoa extends Component{
    	fun=()=>{
        //第一个参数是抛出的事件名,第二个是抛出参数
    		PubSub.publish("zipao","hello world")
      }
    }
    render(){
      	return(
    
        <div>
         	demoa
            <button onClick={this,fun.bind(this)}>点我传递给demob</button>
         </div>
    
       )
    }
    
    在第二组件接受
    PubSub.subscribe("监听的时间",(事件,数据)=>{})
    import React, { Component } from 'react'
    import PubSub from "pubsub-js"
    export default class demob extends Component {
        componentDidMount() {
            // 接受zib这个同胞传递过来的数据
            PubSub.subscribe("zibpao",(a,b)=>{
                console.log(`接受的事件名${a}`)
                console.log(`接受的数据${b}`)
            })
        }
        
        render() {
            return (
    
                <div>
                    {/* 我要接受zib这个同胞的传值 */}
                    demob
                </div>
    
           )
      }
    }
    

    this.props.children

    this.props对象的属性与组件的属性是一一对应的,但是有一个例外就是this.props.children属性,他表示组件的所有子节点
    
    this.props.chilren的值有三种可能:
    1、如果当前组件没有子节点,他就是undefined;
    2、如果有一个子节点,数据类型是Object;
    3、如果有多个子节点,数据类型就是array。
    

    context上下文对象--跨组件传值

    创建Index.js文件
    import React,{Component,createContext}from 'react'
    let context=createContext()
    let {Provider,Consumer}=context
    class Index extends Component{
    	render(){
    		return (
          <div>
          	<Provider value={{name:"cwl",age:18}}>
            	{this.props.children}
            </Provider>
          </div>
    		)
    	}
    }
    export {Index,Consumer}
    
    在根目录index.js中
    import {Index} from "./context/Index.js"
    ReactDOM.render(
    	<Index>
      	<App/>
      </Index>
    )
    
    在需要的组建中进行使用
    import React,{Component} from 'react'
    import {Consumer} from "../myprovider/index"
    class home extends Component{
      render(){
        return(
        	<div>
          	{/*使用context的数据*/}
            <Consumer>
            	{
                (val)=>{
                  return(
                  	<div>{val.name}---{val.age}</div>
                  )
                }
              }
            </Consumer>
          </div>
        )
      }
    }
    export default home
    

    状态机

    在react中开发者只需关心数据。数据改变页面就会发生改变
    数据等同于状态,状态改变-页面绑定的数据就由react进行改变
    组件被称之为"状态机",视图与状态)——对应
    
    初始化状态    	this.state={}
    class	MyCom extends React.Component{
    	constructor(props){
    		super(props)
    		this.state={
    			key1:"value1",
    			key2:"value2"
    		}
    	}
    	render(){
    		return (<div>我是组件</div>)
    	}
    }
    
    
    读取状态 this.state.key1
    render(){
      //读取状态
      return <div>{this.state.key1}</div>
    }
    
    更新状态 this.setState({key1:"newvalue"})
    setState()是异步的会自动触发render函数的重新渲染
    fun = ()=>{
    	//更新state
    	this.setState({
    		key1:"newval"
    	})
    }
    

    constructor--super

    ES6的继承规则得知,不管子类写不写constructor,在new实例的过程都会给补上constructor。
    可以不写constructor,一旦写了constructor,就必须在此函数中写super()super调用父类的构造方法,此时组件才有自己的this,在组件的全局中都可以使用this关键字,
    否则如果只是constructor 而不执行 super() 那么以后的this都是错的!!!super()继承父组件的 this
    
    当想在constructor中使用this.props的时候,super需要加入(props),
    
    此时用props也行,用this.props也行,他俩都是一个东西。(不过props可以是任意参数,this.props是固定写法)
    

    为什么不用工厂方式创建组件

    再有状态的前提下 不能使用工厂方式创建组件
    

    字符串类型标签解析

    dangerouslySetInnerHTML={{__html:this.state.text}}
    class App extends React.Component{
      constructor(props){
        super(props)
        this.state={text:"<p>我是一个p标签</p>"}
      }
      render(){
        return(
        	<div dangerouslySetInnerHTML={{__html:this.state.text}}></div>
        )
      }
    }
    

    Refs转发

    React提供的这个ref属性(不能再无状态组件上使用ref属性,因为他们没有实例)表示为对组件真正实例的引用其实就是ReactDOM.rebder()返回的组件实例
    
    ReactDOM.render()渲染组件是返回的是组件实例;而渲染dom元素时,返回是具体的dom节点
    
    标识组件内部的元素
    

    ref三种用法

    1. 字符串

      class MyCom extends React.Component{
        fun=()=>{
          //字符串获取
          alert(this.refs.elem.value)
        }
        render(){
          return(
          	<div>
            	<input type="text" ref="elem" />
              <button @onClick={this.fun}>点我得到值</button>
            </div>
          )
        }
      }
      
    2. 回调函数

      回调函数就是在dom节点或组件上挂在函数,函数的实参是dom节点,达到的效果与字符串形式是一样的,都是获取其引用
      
      class MyCom extends React.Component{
        constructor(props){
          super(prpos)
        }
        fun=()=>{
          //回调获取
          alert(this.textinput.value)
        }
        render(){
          return(
          	<div>
            	<input type="text" ref={(input)=>{this.textinput=input}} />
              <button @onClick={this.fun}>点我得到值</button>
            </div>
          )
        }
      }
      
    3. React.createRef()

      在React 16.3版本后,使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上该ref的current属性将能拿到dom节点或组件的实例
      
      class MyCom extends React.Component{
        constructor(props){
          super(props)
          //创建ref
          this.myRef=React.createRef();
        }
        fun=()=>{
          alert(this.myRef.current.value)
        }
        render(){
          return(
          	<div>
            	<input type="text" ref={this.myRef}/>
              <button @onClick={this.fun}>点我得到值</button>
            </div>	
          )
        }
      }
      

    React受控组件

    React负责渲染表单的组件。同时仍然控制用户后续输入时所发生的变化。值是来自于state控制的 输入表单元素称为"受控组件"
    

    React组件生命周期

    每个类组件都包含"生命周期方法",可以重写这些方法,以便于在运行过程中特定的阶段执行这些方法。
    
    react生命周期分为三种状态:
    挂载阶段、更新阶段、卸载阶段
    

    生命周期--挂载阶段

    constructor()中完成了React数据的初始化
    
    componentWillMount()
    一般用的比较少,它更多的是在服务端渲染时使用。它代表的过程是组件已经经历了constructor()初始化数据后,但是还未渲染DOM时。
    
    componentDidMount()
    组件第一次渲染完成,此时dom阶段已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染
    
    componentWillUNmount在组件从DOM中移除之前立刻被调用
    
    ReactDOM.unmountComponentAtNode(document.getElementById("demodiv"));//卸载组件
    

    生命周期--更新阶段

    componentWillReceiveProps(nextProps)
    在组件接收到一个新的props时被调用
    
    shouldComponentUpdate()
    判定组件是否要更新html主要用于性能优化(不分更新)唯一用于控制组件重新渲染的生命周期,由于react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以组织组件的更新
    
    componentWillUpdate()
    组件即将更新html时候调用shouldComponentUpdate返回true以后,组件进入重新渲染的流程
    
    componentDidUpdate()在组建完成更新后立即调用
    
    render()
    函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过diff算法比较更新前后的恶心就DOm树,比较以后,找到最小的有差异的DOM节点,并重新渲染。
    

    生命周期的方法

    componentWillMount 组件渲染之前调用
    componentDidMount  组件渲染之后调用在第一次渲染后调用
    componentWillReceiveProps在组件接收到一个新的prop时被调用。这个方法在初始化render时不会被调用。
    shouldComponentUpdate 判定组件是否要更新html
    componentWillUpdate组件即将更新html时候调用
    componentDidUpdate 在组件完成更新后立即调用。componentWillUnmount在组件从 DOM 中移除之前立刻被调用。            ReactDOM.unmountComponentAtNode(document.getElementById("demodiv"));//卸载组件
    

    React事件处理

    React事件绑定属性的命名采用小驼峰式写法
    绑定函数的过程中不加()否则函数会立即执行
    <button onClick={this.fun}>点我暂停</button>
    
    React中阻止默认行为使用preventDefault();
    

    事件处理--传递参数

    1.通过bind的方式进行传递
    <button onClick={this.fun.bind(this,"参数1","参数2","参数n")}>点我</button>
    
    2.通过箭头函数传递。(注意使用箭头函数调用事件对象必须显示的进行传递)
    <button onClick={(e)=>{this.fun("参数1","参数2",e)}}>点我</button>
    

    事件处理--修改this指向

    1. 通过bind方法进行原地绑定,从而改变this指向

    2. 通过创建箭头函数

    3. 在constructor中提前对事件进行绑定

    4. 将事件调用的写法改为箭头函数的形式

      class MyCom extends React.Component{
          constructor(props){
              super(props)
              this.state={
                  text:"memeda"
              }
          // 提前给函数绑定this
              this.fund=this.fund.bind(this)
          }
          // 方式2:通过创建箭头函数
          fun=()=>{
              this.setState({
                  text:"heheda"
              })
          }
         // 方式1:通过bind方法进行原地绑定,从而改变this指向
          funb(){
              this.setState({
                  text:"heheda1"
              })
          }
          // 方式4:将事件调用的写法改为箭头函数的形式
          func(){
              this.setState({
                  text:"heheda2"
              })
          }
          // 方式3:在constructor中提前对事件进行绑定
          fund(){
              this.setState({
                  text:"heheda3"
              })
          }
      
          render(){
              return(
                  <div>
                     <h1>this指向</h1>
                     <p>{this.state.text}</p>
                     <button onClick={this.funb.bind(this)}>方式1:通过bind方法进行原地绑定,从而改变this指向</button>
                     <button onClick={this.fun}>方式2:通过创建箭头函数</button>
                     <button onClick={this.fund}>方式3:在constructor中提前对事件进行绑定</button>
                     <button onClick={()=>{this.func()}}>方式4:将事件调用的写法改为箭头函数的形式</button>
                  </div>
              )
          }
      }
      

      React条件渲染

      开发中,创建不同的组件来封装各中你需要的行为。然后还可以根据应用的状态变化只渲染其中的一部分。
      
      React中的条件渲染和JavaScript中的一致,使用JavaScript操作符if或条件运算符来创建表示当前状态的元素,然后让React根据他们来跟新UI
      

      条件渲染--if语句

      在React中使用if语句条件渲染是最简单的,但是注意jsx中不允许有if
      
      render(){
        let con
        if(this.state.bool){
          con=<MyComa/>
        }else{
          con=<MyComb/>
        }
        return(
        	<div>
          	<button onClick={this.fun}>点我切换组件</button>
          </div>
        )
      }
      

      条件渲染--三目运算符

      render(){
        return(
          <div>
      			<button onClick={this.fun}>点击</button>
            {this.state.bool?<MyCona/>:</MyConb>}
          </div>
        )
      }
      

      条件渲染-&&

      return(
      	<div>
        	<button onClick={this.fun}>点击</button>
          {this.state.bool==true&&<MyConA/>}
        </div>
      )
      

    React脚手架使用

    create-react-app安装
    
    npm install -g create-react-app 安装脚手架
    
    create-react-app --version 查看版本
     
    create-react-app 项目名  创建项目
    
    cd 项目名 切换到创建好的项目中
    
    npm start 启动运行
    
    

    切换npm 镜像路径

    如果创建项目过慢 可以尝试把npm下载地址切换成淘宝镜像
    查看镜像地址 npm get registry
    切换淘宝镜像 npm config set registry http://registry.npm.taobao.org/
    切换回原始的 npm config set registry http://registry.npmjs.org/
    

    React脚手架使用目录结构

    public静态资源文件夹:里面的index.html是整个react项目最终打包的index入口页面的项目模板,但是和我们的代码编写无关,和最终的页面展示是相关的。
    
    静态资源:可以直接在项目中访问
    比如pulic文件夹的内容想访问直接在浏览器中输入
    http://localhost:3000/logo192.png(文件名)即可查看
    
    src文件夹:使我们编写代码的地方
    
    src/index.js:是整个项目的入口js文件
    src/app.js:是被index引入的一个组件,也用一个js表示
    src/index.css:index.js中的样式文件
    src/app.css:是app.js的样式文件
    logo.svg:是一个svg的图片文件。用于界面展示使用
    

    React脚手架使用目录结构:

    但是原生的结构很不适合开发所有需要对各市进行修改
    创建assets静态资源文件夹
    components组件文件夹
    pages页面文件夹
    styles css样式文件夹
    

    image-20201113192209431

    创建组件

    ES6 class 来定义一个组件
    import React from "react"
    class Home extends React.Component{
      render(){
        return(
        	<div>我是组件</div>
        )
      }
    }
    export default Home
    
    在使用的地方先引用 在使用
    import Home from "./pages/home"
    <Home/>
    

    多行标签

    可以在外层加一个父元素
    方式一 传统标签(这种方式会在页面中新增dom节点)
    render(){
    	return(
    		<div>
    			<p>内容1</p>
    			<p>内容2</p>
    		</div>
    	)
    }
    方式二 Fragment不在dom中增加额外节点
    			或者使用<></>空标记
    import React, { Component } from 'react'
    export default class bottomzi extends Component {
        render() {
            return (
                <>
              		<p>111111</p>
              		<p>2222222</p>
              	</>
            )
        }
    }
    import React, { Component,Fragment } from 'react'
    export default class bottomzi extends Component {
        render() {
            return (
                <Fragment>
              		<p>111111</p>
              		<p>2222222</p>
              	</Fragment>
            )
        }
    }
    

    使用图片

    1.把图片放到public文件夹中 直接使用图片名使用
    	<img src="b.jpg"/>
    2.不在public文件夹中使用 导入图片路径:
    import Imga from "../assets/a.jpg"
    <img src={Imga}/>
    

    react脚手架props验证

    static propTypes = {
    	name:PropTypes.string
    }
    

    styled-components

    styledcomponents是一个常用的JavaScript里写css类库。和所有同类型的类库一样,通过js赋能解决了原生css所不能具备的能力,比如变量、循环、函数等。
    
    npm install --save styled-components 下载
    

    styled-components基本使用

    在需要时用的组建文件夹中穿件styled文件夹并在其中创建js文件
    注意组件首字母必须大写不然无法识别
    //创建样式
    import styled from "styled-components"
    export const DemoStyle = styled.div`
    	//样式以及子元素样式
    color:red;
    font-size:100px;
    p{
    	font-size:20px;
    	color:pink;
    }
    `
    //引用并使用
    import React,{Compponrnt} from "react"
    import {DemoStyle} from "./styleHome.js"
    export default class home extends Component{
      render(){
        return(
        	<div>
          	<DemoStyle>
            	我是p标签外面的文字
              <p>我是p标签里面的文字</p>
            </DemoStyle>
          </div>
        )
      }
    }
    

    强制刷新forceUpdate()

    forceUpdate()就是重新调用render渲染,有些变量不在state上,但你又想这个变量更新的时候,刷新render
    export default class demo extends Component{
    	constructor(props){
    		super(props)
    		this.name="cwl"
    	}
    	fun(name){
    		this.name = name
    		this.forceUpdate();
    	}
    	render(){
    		return(
    			<div>
          	<p>{this.name}</p>
            <button onClik={this.fun.bind{this,"hello world"}}>点我改值并强制刷新</button>
          </div>	
    		)
    	}
    }
    

    react前端配置正向代理跨域

    1.找到项目目录下/node_modules/react-script/config/webpackDevServer.config.js
    2.然后在其中找到proxy
    	proxy:{
    		"/api":{
          target:"请求地址",
          changeOrigin:true,
          "pathRewrite":{
    				"^/api":"/"
          }
    		}
    	},
    	3.修改api中的地址
    

    路由

    根据不同的url来切换对应的组件
    实现spa(单页面应用)应用:
    整个项目只有一个完整页面
    页面切换不会刷新页面
    
    React-Router:提供了一些router的核心API,包括Router,Route,Switch等,但是它没有提供DOM操作进行跳转的API
    React-Router-DOM:提供了BrowserRouter,Route,Link等API,我们可以通过DOM的事件控制路由。例如点击一个按钮进行跳转,大多数情况下我们是这种情况,所以在开发过程中,我们更多使用React-Router-DOM。
    

    路由模式-HashRouter和BrowserRouter

    HashRouter(hash模式)
    URL中会有个#,例如localhost:3000/#,HashRouter就会出现这种情况,他是通过hash值来对路由进行控制。如果你是用HashRouter,你的路由就会默认有这个#。刷新不会丢失数据
    
    BrowserRouter(历史记录模式)
    是通过历史记录api进行路由的切换的很多情况下我们则不是这种情况,我们不需要这个#,因为他看起来很乖,这是我们就需要用到BrowserRouter。刷新会丢失404(上线中会出现问题 本地开发中不会)
    
    要切换路由是只需要在index.js设置路由模式即可
    import {HashRouter} from "react-router-dom"
    ReactDOM.render((<HashRouter><App/>						</HashrRouter>,document.getElementById('root'))
    

    路由-Link与Switch

    Link主要API是to,to可以接受string或者一个object,来控制url
    NavLink它可以为当前选中的路由设置类名、样式以及回调函数
    to属性跳转路径activeClassName当元素处于活动状态是应用于元素的样式
    
    //引用Link
    import React from 'react'
    import {Route,Link,NavLink} from "react-router-dom"
    import Demoa from "./components/demoa"
    import Demob from "./components/demob"
    function App(){
      return(
      	<div className="App">
        	{/*路由导航*/}
          <Link to="/demoa">去demoa</Link>
          <Link to="/demob">去demob</Link>
          {/*NavLink 可以设置选中状态元素的类名*/}
          <NavLink to="/demoa" acticeClassName="xxx">demoa</NavLink>
          <NavLink to="/demob" acticeClassName="xxx">demob</NavLink>
          {/*路由规则*/}
          <Route path="/demoa" component={Demoa}/>
          <Route path="/demob" component={Demob}/>
        </div>
      )
    }
    export default App
    如果在vscode的终端中启动项目可能会无效 在外部cmd中启动
    

    路由的基本使用

    路由最基本的职责就是当页面的访问地址与Route上的path匹配时,就会渲染出对应的UI界面。
    1.下载路由模块
    	npm install --save react-router-dom
    2.在index.js引用路由模块
    	import{BrowserRouter}from react-router-dom;
    3.在index.js使用路由模式包裹组件
    	ReactDOM.render(<BrowserRouter><App/>						</BrowserRouter>,document.getElementById('root'))
    4.在app.js中引用路由出口
    	import {Router} from "react-router-dom"
    5.配置
    	<Router path="/路径" component={组件}/>
    

    exact精准匹配

    exact代表当前路由oath的路径采用精准匹配,比如说Home的path如果不加上exact,那么path="/about"将会匹配他自己与path="/"这两个,所以一般path="/"这个路由一般不会加上exact,另外需要注意一点的是嵌套路由不要加exact属性,如果父级路由加上,这里例如topics加上该属性,他下面的子路由将不会生效,因为外层强制匹配了。
    <Route exact path="/" component={Home}/>
    <Route exact path="/about" component={About}/>
    <Route exact path="/topics" component={Topics}/>
    

    路由--重定向

    为了解决route的唯一渲染,他是为了保证路由只渲染一个路径
    <Switch>是唯一的,因为他仅仅只会渲染一个路径,当它匹配完一个路径后,就会停止渲染了
    
    导入Redirect
    import {BrowserRouter,Route,Link,NavLink,Redirect} from 'react-router-dom'
    定义重定向路径<Redirect from="/" to="/demoa" exact/>
      
      return(
      	<div>
          {/*路由导航*/}
        	<NavLink to="/demoa" activeClassName="xuanzhong">去demoa</NavLink>
        	<NavLink to="/demob" activeClassName="xuanzhong">去demob</NavLink>
          <Switch>
          	<Route path="/demoa" component={Demoa}/>
          	<Route path="/demob" component={Demob}/>
            {/*设置重定向*/}
            <Redirect from="/" to="/demoa" exact/>
            {/*404页面需要卸载最下面 不需要设置path*/}
            <Route component={Democ}/>
          </Switch>
        </div>
      )
    

    二级路由

    在子页面中引种路由模块
    import  {Route,Link} from 'react-router-dom'
    设置相关规则 与路由导航
    import React,{Component} from 'react'
    import {Route,NavLink} from 'react-router-dom'
    import Era from "./era"
    import Erb from "./erb"
    export default class demoa extends Component{
      render(){
        return(
        	<div>
          	demoa
            <NavLink to="/demoa/era">去era</NavLink>
            <NavLink to="/demoa/erb">去erb</NavLink>
            <Route path="/demoa/era" component={Era}/>
            <Route path="/demoa/erb" component={Erb}/>
          </div>
        )
      }
    }
    
    默认选状态在上一级导航中设置to
    

    路由--js跳转

    1.push方法在路由页面中跳转this.props.history.push('/dome')
    
    replace()替换当前路径
    goBack()后退
    goForward()前进
    
    <button onClik={()=>{this.props.history.push('/demoa')}}>push跳转</button>
    <button onClik={()=>{this.props.history.goBack()}}>后退</button>
    <button onClik={()=>{this.props.history.goForward()}}>前进</button>
    

    路由--withRouter

    withRouter作用是让不是理由切换的组件也具有路由切换组件的三个属性(loaction match history)
    
    <button onClik={()=>{this.props.history.push('/demoa')}}>push跳转</button>
    会有报错:
    	TypeError:Cannot read property 'push' of undefined
    解决方式:引用withRouter组件
    import {withRouter} from 'react-router-dom'
    修改导航组件删除组件的暴露
    	export default class bottom extends Component{}
    在最底部重新暴露组件
    	export default withRouter(Demoa)
    

    路由--params传参

    在Router标签后面拼接传递参数的名字
    <Route path="/a/:name" component={Demoa}/>
     设置发送的数据
    //声明式
    	<Link to="/a/我是params传参声明式的数据">a</Link>
    //编程式
    <button onClick={()=>{this.props.history.push('/a/我是params传参编程式的数据')}}>点我去a</button>
    
    在需要接受的路由组建中接受this.props.match.params.name
    componentWillMount(){
      console.log(this.props.match.params.name)
    }
    
    优势:刷新地址栏,参数依然存在
    缺点:只能传字符串,并且,如果传的值太多的话,url会变得长而丑陋
    

    路由--state传参

    在link中设置发送的数据
    <Link to={{{pathname:"/demoa"},state:{name:"cwl"}}}>点我去demoa</Link>
    
    在需要接受的路由组件中接受
    console.log(thsi.props.location.state.name)
    
    优势:传参优雅,地址栏不显示传递的数据,传递参数可传对象
    缺点:刷新地址栏,参数丢失
    

    路由render渲染写法

    修改Router里面的组件调用方式为:
    render={(props)=>{return <组件/>}}
    render调用一个函数那么我们就可以决定什么时候渲染他同时传入props那么就可以在路由组件中国使用histroy:{...},location:{...},match:{...}这几个对象
    
    <Route path="/demoa" render={(props)=>{return <Demo {props}/>}}/>
    <Route path="/demob" component={Demo}/>
    

    路由render渲染写法传递数据

    只需要向组件内部传递即可 正向传值
    <Route path="/demoa" render={(props)=>{return <Demo {...props} text='我是给路由传递的参数'/>}}/>
    <Route path="/demob" component={Demo}/>
      
      取值
    render(){
      console.log(this.props.text)
      return(<div>我是组件</div>)
    }
    

    路由验证

    如果想对路由进行验证的话只需要在函数中进行逻辑编写 既可以设置具体渲染那个组件
    <Route path="/demoa" render={(props)=>{return false?<Demoa {...props}/>:<Redirect to="/login"/>}}/>
    <Route path="/demob" component={Demob}/>
    

    高阶组件HOC

    在React组件的构建过程中,常常有这样的场景,有一类功能需要被不同的组件公用。可以复用在react组件中的代码与逻辑
    HOC--参数是组建同事返回值也是组件 这类组件我们叫做高阶组件,高阶组件的本质是高阶函数 比如js中的map()
    
    高阶组价是React中用于崇墉组件逻辑的高级技术。HOC本身不是React API的一部分。他们是从React构思本质中浮现出来的一种模式。
    
    例如封装一个功能在很多歌组件中都想使用 就是每个组件都带一个版权信息
    import React from "react"
    let HOC=(Com)=>{//形参自己定义(首字母大写)
      render(){
        return(
        	<div>
        			来自于:cwl.<Com/>
        	</div>	
        )
      }
    }
    export default HOC
    
    使用的时候需要修改组件大的导出方式
    import React,{Component} from 'react'
    import HOC from '../HOC/index'
    class Demoa extends Component{
      render(){
        return(
        	<div>
          	我是组件demoa
          </div>
        )
      }
    }
    export default HOC(Demoa)
    

    高阶组件HOC接受props

    如果组件是被高阶组件导出的 那么在正向传值的时候需要在高阶组件中进行传递

    import React,{Component} from 'react'
    import HOC from '../HOC/index'
    class Demoa extends Component{
      render(){
        return(
        	<div>
          	来自于:cwl<Com {...this.props}/>
          </div>
        )
      }
    }
    export default HOC()
    
    <div>
    	我是一个组件--{this.props.text}  
    </div>
    

    高阶组件HOC--反向继承

    反向继承最核心作用,是渲染劫持(拦截了渲染可以让我们进行条件渲染)。super.render()是调用父类的render()渲染
    var Xw=(Com,num)=>{
      return class Demo extends Component{
        render(){
          if(num===1){
            return(
              <div>
              	来自于:cwl<Com {...this.props}/>
              </div>
            )
          }else{
            return(<Com {...this.props}/>)
          }
        }
      }
    }
    

    REDUX

    Redux是为javascript应用程序提供一个状态管理工具
    集中的管理react中多个组件的状态
    redux是专门作状态管理的js库(不是react插件库可以用在其他js框架中例如vue,但是基本用在react中)
    

    redux使用场景

    某个组件的状态需要共享的时候
    某个组件的状态需要在任何地方都可以拿到
    一个组件需要改变全局状态
    一个组件需要改变另一个组件的状态
    

    redux三大原则

    单一数据源:整个英勇的state被存储在一个object tree中,并且这个object tree只存在于唯一一个store中
    State是只读的:唯一改变state的方法就是触发action,action是一个用于描述已发生事件的普通对象
    使用纯函数来执行修改:为了描述action如何改变state tree,你需要编写reducers(一些纯函数,他接受先前的state和action)
    

    redux常用方法和概念

    store:管理着整个应用的状态,可以通过getState()来重新获得最新的状态(state)
    action:是唯一可以改变状态(state)的方式,服务器的各种推送、用户自己做的一些操作,最终都会转换成一个个的action,而且这些action就是修改的动作,可以通过dispatch()方法来进行调用
    reducer:reducer是一个纯函数,它接受action和当前state作为参数,返回一个新的state.(纯函数就是只要传入相同的参数,每次都应返回相同的结果)
    
    createStore() 	创建一个redux store来存放应用中所有的		state,一个应用智能有一个store。函数返回store对象
    getState() 获取数据
    dispatch()分发action,这是改变state的唯一方法
    subscribe()添加一个变化监听器,每当改变store的时候就会执行
    

    redux使用

    npm install --save redux 下载
    建议:在项目中创建一个store文件夹用来保存redux相关的内容
    
    在store中创建index.js
    
    import {createStore} from "redux"//1.引用
    let data={//5.初始化数据
    	text:"我是数据"
    }
    let reducer=(state=data,action)=>{
    	//4、创建reducer 并且传入state数据 action动作
    	return state
    }
    let store =createStore(reducer);
    //2、创建store核心对象并且传入reducer
    export default store //3、暴露
    
    在src下的index.js中使用
    import store from "./store/index.js"
    console.log(store.getState().text)
    
    在组件中使用
    import React,{Component}from 'react'
    import store from "../store/index"//1.引用
    export default class demo extends Component{
      constructor(props){
        super(props)
        this.state={
          txt:store.getState().text//读取数据并且赋值给state
        }
      }
      render(){
        return(
        	<div>
          	redux的数据:{this.state.text}
          </div>
        )
      }
    } 
    

    redux修改数据

    在reducer中添加action任务
    let data = {
    	text:"我是redux的数据"
    }
    export default (state=data,action)=>{
      switch(action.type){
        case "ADD":
          return {...state,text:state.text+1}
        default:
          return state
      }
    }
    
    dispatch()方法来进行调用修改动作
    subscribe()订阅一个变化监听器,每当改变store的时候就会执行
    import React,{Component} from "react"
    import store from "../store/index.js"
    export default class index extends Component{
      constructor(props){
        super(props)
        this.state={
          n:store.getState().text
        }
      }
      componentDidMount(){
        store.subscribe(()=>{
          //当store数据修改时会执行这个回调
          this.setState({
            n:store.getState().text
          })
        })
      }
      fun(){
        store.dispatch({type:"ADD"})//调用任务
      }
      render(){
    		return(
     			<div>
          	index--{this.state.n}
            <button onClick={this.fun.bind(this)}>点我修改数据</button>
          </div>   	
        )
      }
    }
    

    redux修改数据传递参数

    在dispatch中进行参数传递
    	upFun=()=>{
    		store.dispatch({type:"ADD",num:123})//传递参数
    	}
    	reducer中使用
    	let con={
    		text:"我是render的数据"
    	}
    	export default {state=con,action}=>{
    		switch(action,type){
    		case "ADD":
    			return {...state,text:state.text+action.num}
    			default:
    				return state
    		}
    	}
    

    使用actionCreator统一创建action

    redux希望使用专门的工厂函数来创建action
    创建store来存放actionCreator.js  
    封装相关动作
    let add=(num)=>{
    	return {type:"ADD",num}
    }
    let del=(num)=>{
      return {type:"DEl",num}
    }
    export default {
      add,del
    }
    
    在组件中引用并且使用
    import React,{Component} from "react"
    import store from "../store/index"
    import action from "./actionCreator.js"
    export default class demo extends Conmponent{
      constructor(props)
      super(props)
      this.state={
        txt:store.getState().text
        //当store数据修改时会执行这个回调函数
          store.subscribe(()=>{
            this.setState({
               txt:store.getState().text
            })
          })
    	}
      upFun=()=>{
        store.dispatch(action.add(123))
        //调用封装好的动作并且传入需要的参数
      }
      render()=>{
        return(
          <div>
          	index--{this.state.txt}
            <button onClick={this.upFun}>点我修改数据</button>
          </div>
        )
      }
    }
    

    redux封装派发动作名(action任务名

    在store中创建actionType.js文件
    并且创建常亮来存储
    export const NUMBER_ADD = "ADD";
    
    在需要使用的地方引用使用
    import {ADD} from "./actionType"
    let data={
    	text:"我是redux"
    }
    export default (state=data,action)=>{
    	Switch(action.type){
    	acse ADD:
    		return {...state,text:state.text+action.num}
    	default:
    		return state
    	}
    }
    
    在actionCreator.js中也要进行修改
    

    redux封装reducer模块

    在组件中创建一个reducer.js来存储
    把原本在store文件夹中reducer里面的内容放到刚创建的reducer中
    import {ADD} from "../store/actionType"
    let data={
    	name:"cwl",
    	age:19
    }
    let reducer=(state=data,action)=>{
    	Switch(action.type){
    		case ADD:
    			return {...state,name:state.name+"123"}
    		default:
    			return state
    	}
    }
    export default reducer
    

    redux合并reducer

    使用redux提供的把多个reducer合并成一个
    
    //引用需要合并的reducer
    import homeReducer from "../views/reducer.js"
    //调用合并reducer方法
    import {combineReducers} from "redux"
    let reducer=combineReducers({//开始合并
      homeReducer
    })
    export default reducer
    
    修改调用数据
    import React,{Component} from "react"
    import store from "../store/index"
    import fun from "./actionCreator.js"
    export default class homeReducer extends Component{
      constructor(props)
      super(props)
      this.state={
        //store.getState().模块名.变量
        txt:store.getState().homeReducer.text
        store.subscribe(()=>{
          this.setState({
            //store.getState().模块名.变量
            txt:store.getState().homeReducer.text
          })
        })
    	}
      fun=()=>{
        store.dispatch(fun.fun())
      }
      render(){
        return (
        	<div>
    				数据是:{this.state.txt}
            <button onClick={this.fun}>点我修改数据</button>
          </div>
        )
      }
    }
    

    react-redux

    一个react的插件
    专门用来简化react应用中使用redux
    npm install --save react-redux 下载
    
    <Provider/>组件:把 store 提供给其子组件
    connect 高阶组件:链接  链接react组件和redux(组件状态要从redux中获取
    

    react-redux使用

    在index.js中创建Provoider
    import React from 'react'
    import ReactDOM from 'react-dom'
    import './index.css'
    import App from './components/home/home.jsx'
    import * as serviceWorker from "./serviceWorker"
    import {Provider} from 'react-redux'
    import store from "./store"
    ReactDOM.render(
    	<Provider store={store}>使用Provider把state数据进行提供
    		<App/>
      <Provider/>,
      document.getElementById('root')
    );
    serviceWorker.unregister();
    
    在需要使用数据的组件中
    import {connect} from "react-redux"
    	class home extends Component{
        constructor(props){
          super(props)
          this.state={
            txt:this.props.state.name
          }
        }
      }
    export default connect(state=>{state})(home)
    
    修改数据
    add=()=>{
      this.props.diospatch({type:"add"})
    }
    

    性能优化

    我们在更新数据的时候使用setState修改整个数据 那么数据变了 便利的时候所有内容都要被重新渲染。
    数量少没有关系 如果遍历出来的数据很多 那么就会严重影响我们的性能

    解决方式1 		————类组件中使用
    	使用生命周期的shouldComponentUpdate(nextProps,nextstate)判定组件是否要更新
    	shouldComponentUpdate(nextProps,nextState){
    		//如果值改变了我就渲染 否则就不渲染
    		return nextProps.style !== this.props.style
    	}
    运行之后发现可以减少不必要的渲染 提高性能
    
    解决方式2 纯组件(PureComponent)————类组件中使用
    PureComponent是优化react应用程序最重要的方法,组件发生更新时,组件的props和state没有改变,render方法就不会触发 .可以减少不必要的render次数,提高性能。
    
    省去虚拟dom生成和对比的过程,其实就是react自动帮忙做了一个浅比较(它只比较props和state的内存地址,如果内存地址相同,则shouldComponentUpdate生命周期就返回false。)
    import React,{Component,PureComponent} from 'react'
    export default class home extends PureComponent{
    	render(){
    		return()
    	}
    }
    
    解决方式3 React.memo() ————无状态组件中使用
    类似于PureComponent,不同于React.memo是函数/无状态组件,React.PureComponent是类组件。memo依然是一种对象的浅比较
    
    import React,{Component} from 'react'
    let home = React.memo((props)=>{
    	let {style,text,title}=props
    	return (
    		<div>
        	<input typr="checkbox" checked={style} onChange={ck.bind(this,i)}/>
          <span style={{color:style?'red':''}}>{text}</span>
        </div>
    	)
    })
    export default home
    

    修改react脚手架端口

    端口默认是3000
    修改路径:项目路径/node_modules/react-scripts/scripts/start.js
    
    改端口 const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 8888(你要修改的端口);
    

    启动react项目

    找到项目中的 package.json文件找到 scripts属性:
     "scripts": {
        "start": "react-scripts start",
        "build": "react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject"
      },
    修改后
    "scripts": {
        "server": "react-scripts start",
        "build": "react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject"
      },
    修改之后启动命令 npm run serve
    

    react脚手架使用confirm报错问题

    原因是eslint 屏蔽了这个方法
    render(){
    	console.log(confirm('aa'))
    	return(<div></div>)
    }
    err:Unexpected use of 'confirm' no-restricted-globals
    
    解决方式 使用//eslint-disable-next-line
    
    //eslint-disable-next-line
    console.log(confirm('lkljk'))
    

    react脚手架使用--配置路径别名

    npm install react-app-rewired -D 下载
    修改启动命令在package.json 中修改scripts为:"start": "react-app-rewired start"
    在根目录文件下,新建 “config-overrides.js” 文件配置
    const path = require('path');
    function resolve(dir) {
        return path.join(__dirname, '.', dir)
    }
    module.exports = function override(config, env) {
        config.resolve.alias = {
            '@': resolve('src'),
            "com":resolve("src/components")
        }
        return config;  }
    重新启动即可
    

    fetch ---es6

    XMLHttpRequest的最新替代技术——Fetch API, 它是W3C的正式标准  (xx.json转换成json字符串)
    
    class Ajax extends React.Component{
      constructor(props){
        super(props)
        this.state={
          name:"cwl"
        }
      }
      componentDidMount(){
        fetch("http://localhost:8888/home/test")
        	.then(res=>{res.json()})//转换成json
        	.then((data)=>{
          	console.log(data);
          	this.setState({name:data})
        })
      }
      render(){
        return(
        	<div></div>
        )
      }
    }
    
    //发送 get
    fetch('http://localhost:8888/get?key=val',{
    	method:"get"
    })
    .then(req=>req.json())
    .then((ok)=>{console.log(ok)})
    
    //发送post
    fetch("地址",{
    	method:"POST",
    	headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    	body: "key=val&key=val"
    })
    	.then(req=>req.json())
    	.then(function(ok) {console.log(ok)})
    

    fetch VS ajax VS axios

    传统 Ajax 指的是 XMLHttpRequest(XHR), 最早出现的发送后端请求技术,隶属于原始js中,核心使用XMLHttpRequest对象,多个请求之间如果有先后关系的话,就会出现回调地狱。JQuery ajax 是对原生XHR的封装
    
    axios 是一个基于Promise ,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,
    
    fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。
    

    qs

    下载 npm install --save qs
    引用 import qs from "qs"
    使用 let key =qs.stringify({key:val,key1:val1})
    

    react与typescript

    image-20201124163119051

    解决方式卸载掉原有的create-react-app
    
    npm uninstall -g create-react-app
    
    重新全局下载create-react-app 
    
    创建项目 create-react-app 项目名 --template typescript
    
    创建项目:npx create-react-app 项目名 --template typescript
    
    在 npm version >= 5.2.0 开始,自动安装了npx。
    
    npx 这条命令会临时安装所依赖的环境。命令完成后原有临时下载的会删掉,不会出现在 全局中。下次再执行,还是会重新临时安装。不用全局安装,不用担心长期的污染。
    
    也就是说 npx 会自动查找当前依赖包中的可执行文件,如果找不到,就会去 PATH 里找。如果依然找不到,就会帮你安装
    

    文件结构

    tsconfig.json包含了工程里TypeScript特定的选项。
    tslint.json保存了代码检查器,TSLint将要使用的设置。
    package.json包含了依赖,还有一些命令的快捷方式,如测试命令,预览命令和发布应用的命令。
    public包含了静态资源如HTML页面或图片。除了index.html文件外,其它的文件都可以删除。
    src包含了TypeScript和CSS源码。index.tsx是必须的入口文件
    

    组件创建

    创建一个tsx后缀名的文件
    import React,{Component} from 'react'
    export default class home extends Component{
    	public render(){
    		return (
          <div>
          	<h1>我是一个组件</h1>
          </div>
    		)
    	}
    }
    在需要的位置引用使用  但是引用的时候可能会出错
    在引用时候不要加后缀名
    //在引用时候不要加后缀名
    在引用时候不要加后缀名
    

    数据传递

    ​ 使用传统的方式进行数据传递的时候发现会报错

    在进行数据传递的时候要使用 interface接口对类型限制
    在子组件中进行限制设定
    import React,{omponent} from "react"
    interface IProps{//定义接口数据类型
    	title:string
    }
    export default class home extends Component <IProps> {
    	public constryctor(props:any){
        super(props)
      }
      public render(){
        return (
        	<div>
          	<h1>我是一个组件{this.props.title}</h1>
          </div>
        )
      }
    }
    

    在街口中定义的内容必须全部传递不能只定义传值

    interface ICon {//建议接口的首字母使用I来表示
    	title:string,
    	age:number   //这里定义的数据必须咋父组件进行传值
    }
    

    如果不确定会不会有值那么可以再定义接口的时候设置称为可选项

    interface ICon{
    	title:string,
    	age?:number      //使用? 表示可选项
    }
    

    直接定义状态会出现问题 必须先定义状态的接口类型

    import React ,{ Component} from 'react'
    interface IProps{
    	title:string,
    	age?:number
    }
    interface IState{
    	name:string
    }
    export default class home extends Component <IProps,IState>{
      public constructor(props:IPoprs){
        super(props)
        this.state={
          name:"我是一个状态"
        }
      }
      public render(){
        return (
        	<div>
          	<h1>我是一个组件{this.props.title}</h1>
            <h1>使用状态{this.state.name}</h1>
          </div>
        )
      }
    }
    状态修改
    fun=()=>{
      this.setState({name:"我被改了"})
    }
    

    逆向传值

    子组件
    import React,{Component} from 'react'
    //3.父组件传递过来的props 那么需要定义类型
    interface ICon{
    	myClick:any
    }
    export default class list extends Component <ICon>{
      constructor(props:any){
        super(props)
      }
      //2.使用props调用父组件传递过来的函数 并且传入参数
      fun=()=>{
        //父组件传递进来的事件 但是需要在街口上面订定义
        this.props.myClick("我是传递给父组件的数据")
      }
      render (){
        return (
        	<div>
          		我是子组件
            <button onClick={this.fun}>点我逆向传值</button>
            1.子组件创建事件调用函数
          </div>
        )
      }
    }
    
    父组件
    import React,{Component} from "react"
    import List from "./list"
    export default class home extends Component {
      constructor(props:any){
        super(props)
      }
      fun=(val:string)=>{
        console.log(val)
        //1.父组件创建函数并且 定义形参接受传递的数据
      }
      render(){
        return (
        	<div>
          	我是一个组件
            2.传递给子组件
            <List myClick={this.fun}/>
          </div>
        )
      }
    }
    

    列表

    import React,{Component} from "react"
    interface IState{
    	arr:Array<any>	//定义state类型
    }
    export default class home extends Component <{},IState>{
      constructor(props:any){
        super(props)
        this.state={
          arr:[]
        }
      }
      componentDidMount() {
        //fetch es6原生请求数据的方法
        fetch("http://api.artgoer.cn:8084/artgoer/api/v1/user/324380/v3/topic/topicHomeByLabel?pageIndex=1&token=b544cd63-6d42-46fe-a96c-3cf96bae3113&topicId=62187")
        .then(res=>res.json())
        .then(ok=>{
          console.log(ok)
          this.setState({
            arr:ok.data.commentList
          })
        })
      }
      render(){
        return (
          <div>
          	我是一个组件
            {
              this.state.arr.length > 0 ? this.state.arr.map((v,i)=>{
                return (
                	<p key={i}>{v.commentTxt}</p>
                )
              }):<p>请稍等</p>
            }
          </div>
        )
      }
    }
    

    umiJS

    Umi 是蚂蚁金服的底层前端框架 中文可发音为乌米,是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备。同时有强大的插件扩展各种功能

    umi可以更好的组织路由 放到配置文件中统一管理

    什么时候不用umi?
      需要支持 IE 8 或更低版本的浏览器
      需要支持 React 16.8.0 以下的 React
      需要跑在 Node 10 以下的环境中
      有很强的 webpack 自定义需求和主观意愿
      需要选择不同的路由方案
    
    不同

    create-react-app 是基于 webpack 的打包层方案,包含 build、dev、lint 等,他在打包层把体验做到了极致,但是不包含路由,不是框架,也不支持配置。如果想基于他修改部分配置,或者希望在打包层之外的事情,就会遇到困难。

    yarn和npm命令对比
    功能 yarn npm
    初始化 yarn init npm init
    安装依赖 yarn install 或者 yarn npm intall xxx --save
    新增依赖 yarn add xxx npm intall xxx --save
    删除依赖 yarn remove xxx npm uninstall xxx --save
    全局安装依赖 yarn global add xxx npm install xxx -g
    同时下载多个 yarn add xxx xxx npm install --save xxx xxx

    yarn更换镜像源

    • yarn config set registry https://registry.npm.taobao.org

    • umi下载安装

      全局安装	npm install -g umi / yarn global add umi 
      查看版本 umi -v
      安装项目 npm或yarn create @umijs/umi-app
      安装依赖 cnpm install 或yarn
      启动项目 npm/yarn start
      运行http://localhost:8000
      
    • 目录结构

      .umirc.ts配置文件,包含umi内置功能和插件的配置
      .env 环境变量
      dist目录 执行umi build 后,产物默认会存放在这里
      mock目录 存储mock 文件,此目录下所有js和ts文件会被解析为mock文件
      public目录 此目录下所有文件会被copy到输出路径
      /src/.umi	临时文件目录,比如入口文件、路由等,都会被临时生成到这里 目录
      /src/layouts/index.tsx 约定式路由时的全局布局文件
      /src/pages目录 所有路由组件存放在这里
      /src/app.ts 运行时配置文件,可以在这里扩展运动时的能力,比如修改路由、修改render方法等
      
    • HTML模板

      修改默认模板
      新建src/pages/document.ejs,umi约定这个文件存在,会作为默认模板
      
      作用:如果要引用第三方js/css文件 可以直接在HTMl模板进行配置。有很多第三方的插件都没有react版本的 所以采用import的发放时是没有办法进行引用的 也就只能采取上面的方式进行引用
      
    • 页面创建

      umi g命令创建页面或组件
      在项目根路径下执行
      
      umi g page xxx (页面名) 或umi g page xxx/xxx
      umi g page xxx --typescript --less 创建ts页面与less
      
      也可以自己创建react类组件
      
    • 路由

      在 Umi 中,应用都是单页应用,页面地址的跳转都是在浏览器端完成的,不会重新请求服务端获取 html,html 只在应用初始化时加载一次。所有页面由不同的组件构成,页面的切换其实就是不同组件的切换,你只需要在配置中把不同的路由路径和对应的组件关联上。

      配置路由

      ​ 在配置文件umirc.ts中通过routes进行配置,格式为路由信息的数组。

      export default defineConfig({
      	nodeModulesTransform:{
      		type:'none',
      	},
      	router:[
      		{exact:true,path:'/',component:'@/pages/index'},
      	],
      });
      path:路径
      component:组件路径 @代表src路径
      exact: 路径精准匹配
      

      title:当前页面标题

      import {defineConfig} from 'umi';
      export default defineConfig({
      	nodeModuulesTranform:{
      		type:'none'
      	},
      	router:[
      		{path:"/",component:"@/pages/index",title:"我是标题"},
      	],
      });
      

    页面跳转

    在umi里,页面之间条状有两种方式:声明式和命令式

    声明式:通过Link使用,通常作为React组件事件。

    import {Link} from 'umi';
    export default ()=>{
      <Link to="/home">home</Link>
    }
    

    命令式:通过history使用,通常在事件处理中被调用。

    import {history} from 'umi';
    function ooo(){
    	history.push("/home");
    }
    

    二级路由

    routes:配置路由的子路由

    然后在 一级路由中 通过props.children 渲染子路由

    export default defineConfig({
    	nodeModulesTransform:{
    		type:'none'
    	},
      routes:[
        {path:'/',component:'@/pages/index',title:'我是标题'},
        {path:'/home',component:'@/pages/home'},
        {
      		path:"/homea",
      		component:'@/pages/phone',
      		routes:[
            {path:"/homep/era",component:"@/pages/era"},
            {path:"/homep/erb",component:"@/pages/erb"},
          ]
    		},
      ],
    });
    

    约定式路由

    • 除配置式路由外,Umi 也支持约定式路由。约定式路由也叫文件路由,就是不需要手写配置,文件系统即路由,通过目录和文件及其命名分析出路由配置。

    • 如果没有 routes 配置,Umi 会进入约定式路由模式,然后分析 src/pages 目录拿到路由配置。

    • 但是实际上你写了文件之后,手动在浏览器地址栏输入路由没有发生跳转 原因: 使用脚手架搭建的项目会在配置文件中对路由进行配置。

    • 解决方式:删除.umirc.ts路由配置文件

    约定式路由---动态路由

    • 约定[]包裹的文件或文件夹为动态路由

      image-20201125153743961

    • 接受

      import React,{useEffect} from 'react'
      export default (props:any)=>{
          useEffect(()=>{
              console.log(props.match.params.id)
          })
          return (
              <div>
              	我是接受动态路由的
              </div>
          )
      }
      

    约定式路由---二级路由

    • Umi 里约定目录下有 _layout.tsx 时会生成嵌套路由,以 _layout.tsx 为该目录的 layout。layout 文件需要返回一个 React 组件,并通过 **props.children **渲染子组件。

    image-20201125154220866

    • 必须在父路由中加入this.props.children来渲染子路由

      <div>
      	user
      	{this.props.children}
      </div>
      

    模拟数据json-server

    我们在开发中并不想使用简单的静态数据,而是希望自己起一个本地模拟请求以及请求回来的过程。json-server就是用来完成模拟数据的
    
    下载:npm install json-server -g
    查看版本: json-server --version
    
    创建数据

    在项目下 创建一个mock的文件夹并且写入相关的数据.json 与src同级

    启动
    json-server默认端口为3000 我们不能直接启动会和react脚手架冲突 所以我们启动的时候需要修改端口
    
    1.cd 到mock文件夹路径下 
    2.json-server --watch json的名字 --port 4000
    3.在浏览器中测试一下 http://localhost:4000/数据的key
    
    模拟数据请求
    componentDidMount(){
      this.ajaxData()
    }
    ajaxData=()=>{
      axios.get("http://localhost:8888/home").then(ok=>{
        this,setState({
          arr:ok.data
        })
      })
    }
    
    模拟数据发送
    尝试在代码中进行发送数据--数据会存储在json文件中
    add=()=>{
    	axios.post("http://localhost:8888/home",
    						{"name":"我是新数据"},
    						{'headers':{"Content-type":"application/json"}}
    	).then(()=>{
    		//重新调用下数据获取的那个函数否则数据添加到json中但是页面不会刷新
    		this.ajaxData()
    	})
    }
    
    注意 用来存放模拟数据的json文件必须要有一个id 否则添加可能会失败,修改json必须重启json-server服务
    {
      "arr":[
        {
          "id":1,
          "name":"cwl"
        },
        {
          "id":2,
          "name":"cwl2"
        }
      ]
    }
    

    不可变对象immutable

    immutable介绍

    • Facebook 工程师使用3年时间打造,与React同期出现,但是没有被默认放到React工具集中,它内部实现了一套完整的数据持久化 里面有很多常见的数据类型Collection List Map Set等

    • 它里面有三种重要的数据结构:

    • Map:键值对集合,对应于Object ES6中也有专门的Map对象

    • List:有序可以重复的列表,对应于Array

    • set:无序且不可重复key的数组

    • immutable Data就是一旦创建就不能再被改变的数据,对于immutable对象的任何修改或添加删除操作都会返回一个新的immutable对象

    • 为什么每次都要返回一个新的immutable对象呢?----- 因为redux中数据是只读的,如果任意一个使用的位置都可以直接修改redux中的数据,那么可能会影响到其它位置引用的内容,造成显示错误

    immutable原理

    • immutable实现的原理是(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免深拷贝把所有节点都赋值一遍带来的性能损耗,immutable使用了(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享

    immutable修改

    let {Map} = require('immutable')
    let a=Map({
        name:'xixi',
        love:Map({
            lovename:'haha',
            age:18
        })
    })
    let b=a;//创建一个新的变量把Map赋值给b
    console.log(b===a);//true 当没有修改时是把地址直接赋值给b
    
    let c=a.set('name','wuwu');//修改必须通过set来进行
    console.log(c===a);//false 当修改时会创建一个新的immutable对象
    //读取必须使用get
    console.log('a.name' + a.get('name') + '----c.name' + c.get("name"))
    

    immutable---Map

    • Map:键值对集合

    • 创建/读取map集合

      let {Map} = require('immutable')
      //创建
      let a=Map({
          name:'xixi'
      })
      //读取
      console.log(a.get('name'))
      
    • 修改

      let {Map}=require('immutable')
      let a=Map({
          name:'xixi'
      })
      let b=a.set('name','haha')//修改会返回一个新的immutable对象
      console.log(b.get('name'))
      
    • 删除map集合

      let {Map}=require('immutable')
      let a=Map({
          name:'xixi'
      })
      let b=a.delete('name')//删除会返回一个新的immutable对象
      console.log(b.get('name'))
      
    • 批量删除map集合

      let {Map}=require('immutable')
      let a=Map({
          name:'xixi',
          age:18,
          sex:'男'
      })
      let b=a.deleteAll(['name','age'])//删除会返回一个新的immutable对象
      console.log(b.get('sex'))
      
    • 清除并返回新的map

      let {Map}=require('immutable')
      let a=Map({
          name:'xixi',
          age:18
      })
      let b=a.clear()
      console.log(b)
      
    • 合并并返回新的map

      let {Map}=require('immutable')
      let a=Map({
          name:'xixi',
          age:18
      })
      let b=Map({
          sex:'男',
          city:'陕西西安'
      })
      let c=a.merge(b)//合并并返回新的map
      console.log(c)
      
    • toJS把Map转换成原生的Object,深转换(无论有多少层都会转换)

    • toJSON或toObject把Map转换成原生Object,浅转换(只转换第一层)

      let {Map}=require('immutable')
      let a=Map({
          b:Map({
              c:Map({
                  d:'xixi'
              })
          })
      })
      //深转换
      let demoa=a.toJS()
      console.log(demoa)
      //浅转换
      let demob=a.toJSON()
      consoel.log(demob)
      let democ=a.toObject()
      console.log(democ)
      

      image-20201125142003797

    • toArray转换成数组(浅转换)

      let {Map}=require('immutable')
      let a=Map({
          name:'xixi',
          age:18
      })
      let demoa=a.toArray()
      console.log(demoa)
      

    immutable---List

    • List:有序可以重复的列表,对应于Array

    • 创建List两种方式

      let {List}=require('immutable')
      let listArr=List(['a','b','c'])
      console.log(listArr)
      
      //List.of()创建List
      let listArrb=List.of(1,2,3,4)//不用[]
      console.log(listArrb)
      
    • size获取list长度

      let {List}=require('immutable')
      let listArr=List([1,2,3,4])
      console.log(listArr.size)
      
    • set(下标,值)用于设置指定下标的值

      let {List}=require('immutable')
      let listArr=List([1,2,3,4])
      let la=listArr.set(1,'我被修改了')//每次修改都会返回一个新的list
      console.log(la)
      let lb=listArr.set(10,'我的下标大于当前数组长度最大值')//可以大于数组长度
      console.log(lb)
      let lc=listArr.set(-1,'负数是从右往左')
      console.log(lc)
      
    • delect(下标)删除指定下标

      let {List}=require('immutable')
      let listArr=List([1,2,3,4])
      let a=listArr.delect(1)
      console.log(a)
      let b=listArr.delect(-1)//负数从右往左数
      console.log(b)
      
    • insert()用来更新指定下标的值

      let {List}=require('immutable')
      let listArr=List([1,2,3,4])
      let a=listArr.insert(1,'我被修改了')
      console.log(a)
      let b=listArr.insert(-0,'我被修改了')
      console.log(b)
      
    • update(下标,回调函数)用于更新指定下标的值

      let {List}=require('immutable')
      let listArr=List([1,2,3,4])
      let a=listArr.update(1,x=x+'更新了')
      console.log(a)
      
    • clear()清空并返回一个空list

      let {List}=require('immutable')
      let listArr=List([1,2,3,4])
      let a=listArr.clear()
      console.log(a)
      
    • push、pop、unshift、shift和数组方法功能相同,请自行尝试

    • setSize()重新设置数组长度,小于原始list会被截取,大于会用undefined填充

      let {List}=require('immutable')
      let listArr=List([1,2,3,4])
      let a=listArr.setSize(2)
      console.log(a)
      let b=listArr.setSize(10)
      console.log(b)
      
    • concat()把多个list拼接成一个list,merge()是concat()别名

      let {List}=require('immutable')
      let listArr=List([1,2,3,4])
      let listArrb=List(["aa",'bb'])
      let a=listArr.concat(listArrb)
      console.log(a)
      //merge是concat的别名
      let b=listArr.merge(listArrb)
      console.log(b)
      

    immutable在react中使用

    immutable-react

    • 简易购物车

    import React,{Component} from 'react'
    import List from '../components/list.jsx'
    export default class home extends Component{
        constructor(props){
            super(props)
            this.state={
                list:[]
            }
        }
        add=()=>{
            let inputname=this.inputname.value
            let inputnum=this.inputnum.value
            this.state.list[inputname]=inputnum
            this.setState({
                list:this.state.list
            })
        }
        render(){
            return (
                <div>
                	<input type='text' ref={(xiaoming)=>{this.inputname=xiaoming}}/>
                    <input type='text' ref={(xiaohong)=>{this.inputnum=xiaohong}}/>
                    <button onClick={this.add}>添加到购物车</button>
                    <br/>
                    <List demolist={this.state.list}/>
                </div>
            )
        }
    }
    
    • 编写子组件用来展示数据

    import React,{Component} from 'react'
    export default class list extends Component{
        constructor(props){
            super(props)
            this.state={
                
            }
        }
        render(){
            let newlist=this.props.demolist//得到props数据
            return (
                <div>
                	{
                        Object.keys(newlist).map((v,i)=>{
                            return (
                                <p key={v}>商品名:{newlist[v]}</p>
                            )
                        })
                    }
                </div>
            )
        }
    }
    
    • 使用immutable改造

    import React,{Component} from 'react'
    import List from '../components/list.jsx'
    import {Map} from 'immutable'
    export default class home extends Component{
        constructor(props){
            super(props)
            this.state={
                list:Map({})
            }
        }
        add=()=>{
            let inputname=this.inputname.value
            let inputnum=this.inputnum.value
            //immutable对象修改会返回一个新的immutable对象
            let immlist=this.state.list.set(inputname,inputnum)
            this.setState({
                list:immlist
            })
        }
    }
    
    import React,{Component} from 'react'
    export default class list extends Component{
        constructor(props){
            super(props)
            this.state={}
        }
        render(){
            let elarr=[]
            this.props.demolist.forEach((v,i)=>{
                elarr.push(
                    <p key={i}>商品名:{v}---数量:{i}</p>
                )
            })
            return(
                <div>{elarr}</div>
            )
        }
    }
    

    immutable在redux中使用

    redux-immutable

    • 安装:npm install redux-immutable --save

    • 创建文件夹与文件来创建redux-immuable

    • 引用redux-immutable并使用combineReducers进行合并

      import {createStore} from 'redux'
      import {combineReducers} from 'reudx-immutable'
      let {Map}=require('immutable')
      //state中的数据必须是一个immutable
      let indata=Map({
          name:'xixi',
          age:18
      })
      let data=(state=indata,action)=>{
          return state
      }
      //combineReducers里的对象合并成是一个immutable对象
      let reducers=combineReducers({
          data
      })
      let store=createStore(reducers)
      export default store
      
    • 在根组件传递store

      import React from 'react';
      import ReactDOM from 'react-dom';
      import './index.css';
      import App from './views/home';
      import reportWebVitals from './reportWebVitals';
      import {Provider} from 'react-redux'
      import store from './store'
      ReactDOM.render(
        <Provider store={store}>
          <App />
        </Provider>,
        document.getElementById('root')
      );
      // If you want to start measuring performance in your app, pass a function
      // to log results (for example: reportWebVitals(console.log))
      // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
      reportWebVitals();
      
    • 在需要的组件中读取

      import React,{Comoponent} from 'react'
      import {connect} from 'react-redux'
      class home extends Component{
          render(){
              return (
                  <div>
                  	home
                      {this.props.state.get('name')}
                  </div>
              )
          }
      }
      export default connect((state)=>{
          return {state:state.get('data')}
      })(home)
      

    redux-immutable修改

    • 通过dispatch调用修改actions

      class home extends Component{
          add=()=>{
              this.props.dispatch({
                  type:'NUM_ADD',
                  payload:1
              })
          }
          render(){
              return (
                  <div>
                  	home==={this.props.state.get('age')}
                      <button onClick={this.add}>点我修改</button>
                  </div>
              )
          }
      }
      
    • 创建修改操作actions

      let data=(state=indata,action)=>{
          switch (action.type){
              case "NUM_ADD":
                  console.log(state.get('age'))
                  console.log(action.payload)
                  return state.set('age',state.get('age') + action.payload)
                  break;
              default:
                  return state
                  break;
          }
      }
      

    dva

    • React本身只是一个DOM的抽象层,使用组件构建虚拟DOM

    • 如果开发大应用,还需要解决一个问题。

    • 通信:组件之间如何通信?

    • 数据流:数据如何和视图串联起来?路由和数据如何绑定?如何编写异步逻辑?等等

    通信问题

    • 组件会发生三种通信

    • 向子组件发消息

    • 向父组件发消息

    • 向其他组件发消息

    • React只提供了一种通信手段:传参。对于大应用很不方便

    dva是什么

    • dva是体验技术部开发的React应用框架,将上面三个React工具库包装在一起,简化了API,让开发React应用更加方便和快捷

    • dva = React-Router + Redux + Redux-saga

    • dva名字来自于守望先锋的D.Va(是守望先锋中的一个英雄)

    安装dva-cli

    • 全局安装dva-cli:npm install dva-cli -g

    • 查看版本:dva -v

    • 创建新应用:dva new 项目名

    • 启动命令:npm start

    定义路由

    • 路由页面创建在dva提供的router文件夹下创建

    • 添加路由信息到路由表,编辑router.js

      import Products from './routes/Products';
      <Route path="/products" exact component={Products} />
      

    定义路由导航

    • 在dva中使用路由导航要从dva/router中进行引用

    • 声明式:

      import React,{Component} from 'react'
      import {Link} from 'dva/router'
      export default class topbar extends Component{
          render(){
              return (
                  <div>
                      <Link to='/'>home</Link>
                      <Link to='/shop'>shop</Link>
                      <Link to='/user'>user</Link>
                  </div>
              )
          }
      }
      
    • 编程式:

      import React,{Component} from 'react'
      import {Link,withRouter} from 'dva/router'
      class topbar extends Component{
          fun=()=>{
              this.props.history.push('/shop')
          }
          render(){
              return (
                  <div>
                      <Link to='/'>home</Link>
                      <Link to='/shop'>shop</Link>
                      <Link to='/user'>user</Link>
                      <hr/>
                      <button onClick={this.fun}>点我去shop</button>
                  </div>
              )
          }
      }
      export default withRouter(topbar)
      

    定义路由模式

    • 切换HashHistory为BrowserHistory

    • 需要下载history依赖:npm install --save history

    • 然后修改入口文件(即src下的index.js文件):

      import { createBrowserHistory as createHistory } from 'history';
      const app = dva({
        history: createHistory(),
      });
      

    定义组件

    • 组件在components下进行定义

    • 在需要的地方引用使用

    • 定义组件的方式可以定义无状态组件,也可以是class类组件

    • 无状态组件的创建形式使代码的可读性更好,并且减少了大量冗余的代码,精简至只有render方法,大大的增强了编写一个组件的便利

    无状态组件的特点

    • 组件不会被实例化,整体渲染性能得到提升

    • 因为组件被精简成一个render方法的函数实现的,无状态就不会再有组件实例化的过程,无实例化过程也就不需要分配多余的内存,从而性能得到一定的提升

    • 组件不能访问this对象

    • 无状态组价由于没有实例化过程,所以无法访问组件this中的对象,若想访问this就不能使用这种形式创建组件

    • 组件无法访问生命周期的方法

    定义Model

    • model用来处理数据和逻辑,可以把数据和交互都放到model中方便使用

    • 在model文件夹中创建model文件

      export default {
          namespace:"xiaoming",//命名空间名字,必填
          //state就是用来放初始值的
          state:{
      		name:'xixi',
              age:18
          }
      };
      

    引用Model

    • 在全局文件index.js中进行引用

      //设置引用
      app.model(require('./model/文件名').default);
      

    使用Model

    • 在需要使用的组件中使用connect进行连接

      import React,{Component} from 'react'
      import {connect} from 'dva'
      class home extends Component{
          render(){
              return (
                  <div>
                  	home
                  </div>
              )
          }
      }
      export default connect()(home)
      

    读取Model数据

    • 定义方法读取数据

      import React,{Component} from 'react'
      import {connect} from 'dva'
      class home extends Component{
          render(){
              return (
                  <div>
                      {/*3、使用*/}
                  	home---{this.props.name}
                  </div>
              )
          }
      }
      //1、设置数据
      let mapdata=(state)=>{
          return {
              //state.命名空间名字.xxx
              name:state.homemodel.name
          }
      }
      //2、传递HOC
      export default connect(mapdata)(home)
      

    dva-reducers

    修改---Model数据

    • 在model文件中定义reducers属性来进行修改操作

      export default {
          namespace:'homemodel',
          state:{
      		name:'xixi',
              age:18
          },
          //修改操作
          reducers:{
         		updata(state,payload){
                  console.log('我是修改操作')
                  return state //必须要有返回值
              }
      	}
      }
      
    • 在组件中使用dispatch调用reudcer修改操作

      import React,{Component} from 'react'
      import {connect} from 'dva'
      class home extends Component{
          up=()=>{
      		this.props.dispatch({
                  //type:"命名空间名字/reducer名字"
                  type:'homemodel/updata'
              })
          }
          render(){
              return (
                  <div>
                      {/*3、使用*/}
                  	home---{this.props.name}
                      <button onClick={this.up}>点我进行修改</button>
                  </div>
              )
          }
      }
      //1、设置数据
      let mapdata=(state)=>{
          return {
              //state.命名空间名字.xxx
              name:state.homemodel.name
          }
      }
      //2、传递HOC
      export default connect(mapdata)(home)
      
    • 开始在reducers中进行修改

    • 传递修改数据

      up=()=>{
      		this.props.dispatch({
                  //type:"命名空间名字/reducer名字"
                  type:'homemodel/updata',
                  //传递修改数据
                  data:{
                      text:'我是要修改的数据'
                  }
              })
          }
      
    • reducers中接收数据并修改

      export default {
          namespace:'homemodel',
          state:{
      		name:'xixi',
              age:18
          },
          //修改操作
          reducers:{
         		updata(state,payload){
                  console.log('我是修改操作')
                  return {...state,name:payload.data.text} //必须要有返回值
              }
      	}
      }
      

    dva-effects

    知识点扩展---ES6 Generator

    • Generator主要是异步编程,用来封装一个异步任务,是一个异步任务的容器

    • 特点:交出普通函数的执行权(可以让函数在调用时按照我们的需要执行或暂停)

    • 普通函数:在调用时函数中的内容会全部执行

      //普通函数
      function fun(){
          console.log(1);
          console.log(2);
          console.log(3);
      }
      fun()//调用时会全部执行
      
    • generator函数可以 让函数体内的内容随着我们的需要走走停停

    • 在声明函数的function关键字和函数名之间有一个*号(用于区别普通函数)

    • yield(是异步不同阶段的分割线)在generator函数体内进行使用,可以定义不同的内部状态

    • 使用next()来执行generator函数

    • generator函数:

      function *fun(){
          console.log(1);
          yield
          consoel.log(2);
          yield
          consoel.log(2);
      }
      fun()//调用时函数内容不会执行
      
    • 运行后发现函数不会执行

    • 调用generator函数时会返回一个generator对象

    • 使用next()方法来执行generator函数

      function *fun(){
          console.log(1);
          yield 
          console.log(2);
          yield 
          console.log(2);
      }
      let genfun=fun()//会返回一个generator对象
      //每次next()就会执行yield后面的内容
      genfun.next()
      genfun.next()
      genfun.next()
      

    知识点扩展---ES6 Generator传参

    • 可以在next()方法中传递参数,可以把值传递到函数中对应的yield中,第一次不会传递

      function *fun(){
          console.log(1);
          yield 
          console.log(2);
          yield 
          console.log(2);
      }
      let genfun=fun()//会返回一个generator对象
      //每次next()就会执行yield后面的内容
      genfun.next(10)//第一次不会进行传递
      genfun.next(20)
      genfun.next(30)
      

    dva---异步操作

    • effect在dva框架下就是用来处理异步操作的

    • 在model文件中写入effects属性

      export default {
          namespace:'homemodel',
          state:{
      		name:'xixi',
              age:18
          },
          //修改操作
          reducers:{
         		updata(state,payload){
                  console.log('我是修改操作')
                  return {...state,name:payload.data.text} //必须要有返回值
              }
      	},
          effects:{
              //使用Generator语法
      		*genup({payload},{put,call}){
                  yield console.log('我被执行了')
              }
          }
      }
      
    • 在组件内进行调用,触发方式也是dispatch

      import React,{Component} from 'react'
      import {connect} from 'dva'
      class home extends Component{
          up=()=>{
      		this.props.dispatch({
                  //type:"命名空间名字/reducer名字"
                  type:'homemodel/updata'
              })
          }
          yibu=()=>{
      		this.props.dispatch({
                  //type:"命名空间名字/effects名字"
                  type:'homemodel/genup'
              })
          }
          render(){
              return (
                  <div>
                      {/*3、使用*/}
                  	home---{this.props.name}
                      <button onClick={this.up}>点我进行修改</button>
                       <button onClick={this.yibu}>点我调用异步</button>
                  </div>
              )
          }
      }
      //1、设置数据
      let mapdata=(state)=>{
          return {
              //state.命名空间名字.xxx
              name:state.homemodel.name
          }
      }
      //2、传递HOC
      export default connect(mapdata)(home)
      
    • 异步修改数据需要通过effects方法中的put去调用reducers进行修改

    • type对应的是调用reducers的任务名

    • data是传递给reducers的数据

      effects:{
              //使用Generator语法
      		*genup({payload},{put,call}){
                  yield put({
                      //type:'reducers名字'
                      type:'updata',
                      data:{
                          text:'我是effects传递的数据'
                      }
                  })
              }
          }
      

    dva-基本异步请求

    dva---异步请求

    • 异步请求在services文件夹下

    • 新建文件封装我们自己的请求

      //可以参考example.js创建我们自己的请求
      import request from '../utils/request'
      export function query(){
          return request('请求地址');
      }
      
    • 传入请求地址中国天气网测试请求地址(需要解决跨域)http://www.weather.com.cn/data/cityinfo/101320101.html

      import request from '../utils/request'
      export function query(){
          return request('http://www.weather.com.cn/data/cityinfo/101320101.html');
      }
      
    • 在组件内调用封装的请求

    • 会出现跨域问题

      import React,{Component} from 'react'
      import {connect} from 'dva'
      //* as 自己起的名字  相当于把当前模块下所有内容引用
      import * as homeapi from '../services/homeapi.js'
      class home extends Component{
          componentDidMount(){
              homeapi.query().then(ok=>{
      			console.log(ok)
              })
          }
          up=()=>{
      		this.props.dispatch({
                  //type:"命名空间名字/reducer名字"
                  type:'homemodel/updata'
              })
          }
          yibu=()=>{
      		this.props.dispatch({
                  //type:"命名空间名字/effects名字"
                  type:'homemodel/genup'
              })
          }
          render(){
              return (
                  <div>
                      {/*3、使用*/}
                  	home---{this.props.name}
                      <button onClick={this.up}>点我进行修改</button>
                       <button onClick={this.yibu}>点我调用异步</button>
                  </div>
              )
          }
      }
      //1、设置数据
      let mapdata=(state)=>{
          return {
              //state.命名空间名字.xxx
              name:state.homemodel.name
          }
      }
      //2、传递HOC
      export default connect(mapdata)(home)
      
    • 解决跨域:在webpackrc文件中设置跨域并修改请求

       "proxy":{
              "/api":{
                   "target":"http://www.weather.com.cn",
                   "changeOrigin":true,
                   "pathRewrite":{
                     "^/api":"/"
                   }
              }
          }
      
      import request from '../utils/request'
      export function query(){
          return request('/api/data/cityinfo/101320101.html');
      }
      

    dva-model异步请求

    dva---model异步请求

    • 如果需要在model中进行异步请求的话需要在effects中的call进行异步操作的发送

      import * as homeapi from '../services/homeapi.js'
      export default {
          namespace:'homemodel',
          state:{
      		name:'xixi',
              age:18
          },
          //修改操作
          reducers:{
         		updata(state,payload){
                  console.log('我是修改操作')
                  return {...state,name:payload.data.text} //必须要有返回值
              }
      	},
          effects:{
              //使用Generator语法
      		*genup({payload},{put,call}){
                  //在call中调用异步操作
                 let api=yield call(homeapi.query)
                 console.log(api)
              }
          }
      }
      
    • 页面内调用带有异步请求的effects

      import React,{Component} from 'react'
      import {connect} from 'dva'
      //* as 自己起的名字  相当于把当前模块下所有内容引用
      import * as homeapi from '../services/homeapi.js'
      class home extends Component{
          componentDidMount(){
              homeapi.query().then(ok=>{
      			console.log(ok)
              })
          }
          up=()=>{
      		this.props.dispatch({
                  //type:"命名空间名字/reducer名字"
                  type:'homemodel/updata'
              })
          }
          quest=()=>{
      		this.props.dispatch({
                  //type:"命名空间名字/effects名字"
                  type:'homemodel/genup'
              })
          }
          render(){
              return (
                  <div>
                      {/*3、使用*/}
                  	home---{this.props.name}
                      <button onClick={this.up}>点我进行修改</button>
                      <button onClick={this.quest}>点我调用异步请求</button>
                  </div>
              )
          }
      }
      //1、设置数据
      let mapdata=(state)=>{
          return {
              //state.命名空间名字.xxx
              name:state.homemodel.name
          }
      }
      //2、传递HOC
      export default connect(mapdata)(home)
      

    dva-model异步请求修改state

    dva---model异步请求

    • 可以通过put的方式修改state

       effects:{
              //使用Generator语法
      		*genup({payload},{put,call}){
                  //在call中调用异步操作
                 let api=yield call(homeapi.query)
                 if(api.data){
                    consoel.log(api.data.weatherinfo.city)
                    yield put({
                        type:'updata',
                        data:{
                            obj:api.data.weatherinfo.city
                        }
                    })
                 }
              }
          }
      
    • 创建修改reducers

      import * as homeapi from '../services/homeapi.js'
      export default {
          namespace:'homemodel',
          state:{
      		name:'xixi',
              age:18,
              obj:''//创建state
          },
          //修改操作
          reducers:{
         		updata(state,payload){
                  console.log(payload.data.obj)
                  return {...state,obj:payload.data.obj} //必须要有返回值
              }
      	},
          effects:{
              //使用Generator语法
      		*genup({payload},{put,call}){
                  //在call中调用异步操作
                 let api=yield call(homeapi.query)
                 if(api.data){
                    consoel.log(api.data.weatherinfo.city)
                    yield put({
                        type:'updata',
                        data:{
                            obj:api.data.weatherinfo.city
                        }
                    })
                 }
              }
          }
      }
      
    • 页面引用state数据给props并展示

      import React,{Component} from 'react'
      import {connect} from 'dva'
      //* as 自己起的名字  相当于把当前模块下所有内容引用
      import * as homeapi from '../services/homeapi.js'
      class home extends Component{
          componentDidMount(){
              homeapi.query().then(ok=>{
      			console.log(ok)
              })
          }
          up=()=>{
      		this.props.dispatch({
                  //type:"命名空间名字/reducer名字"
                  type:'homemodel/updata'
              })
          }
          quest=()=>{
      		this.props.dispatch({
                  type:'homemodel/genup'
              })
          }
          render(){
              return (
                  <div>
                      {/*3、使用*/}
                  	home---{this.props.name}---{this.props.obj}
                      <button onClick={this.up}>点我进行修改</button>
                      <button onClick={this.quest}>点我调用异步请求</button>
                  </div>
              )
          }
      }
      //1、设置数据
      let mapdata=(state)=>{
          return {
              //state.命名空间名字.xxx
              name:state.homemodel.name,
              //引用数据
              obj:state.homemodel.obj
          }
      }
      //2、传递HOC
      export default connect(mapdata)(home)
      

    dva-subscription订阅

    • model中的subscription相当于一个监听器,可以监听路由变化,鼠标,键盘变化,服务器连接变化,状态变化等,这样在其中就可以根据不同的变化做出相应的处理,在这个subsription中的方法名是随意定的,每次变化都会一次去调用里面的所有方法,所以一边会加相应的判断

      subscriptions:{
          resize({dispatch,history}){
              //window.onresize是页面尺寸变化时触发的事件
              window.onresize=()=>{
                  console.log('改变了')
              }
          }
      }
      
  • 相关阅读:
    实现带有头结点的链表
    数据结构之链式线性表
    HDU 1010 dfs+奇偶剪枝
    数据结构之顺序线性表
    POJ 1753 dfs+枚举
    HDU 4277 dfs+set去重
    平衡二叉树
    线性素数筛+欧拉线性筛(证明)
    dp--2019南昌网络赛B-Match Stick Game
    线段树+单调栈+前缀和--2019icpc南昌网络赛I
  • 原文地址:https://www.cnblogs.com/cwl1025/p/13949127.html
Copyright © 2020-2023  润新知