• 【尚硅谷React】——React全家桶笔记


    文章目录

    第1章 React简介

    • 用于构建用户界面的JavaScript库(只关注页面),将数据渲染为HTML视图;

    • 由Facebook开发且开源。

    • 中文官网
      英文官网

    React可以克服原生JS的以下缺点:

    1. 原生JS操作DOM繁琐且效率低,因为用DOM-API操作UI;
    2. JS直接操作DOM会使浏览器进行大量的重绘重排;
    3. 原生JS没有组件化编码方案,代码复用率低

    react开发者工具:Chrome插件 React Developer Tool(注意安装来源为facebook的)

    1.1 React的特点

    1. 采用组件化模式,声明式编码,提高开发效率及组件复用率;
    2. 在React Native中可以使用React语法进行_移动端开发_;
    3. 使用虚拟DOM+优秀的Diffing算法,尽量减少与真实DOM的交互。
      在这里插入图片描述
      对比两次生成的虚拟DOM,如果重复,直接用页面之前有的DOM,而不是全部重绘真实DOM

    1.2 引入文件

    • react.js (核心库):核心库要在react-dom之前引入
    • react-dom.js :提供操作DOM的react扩展库
    • babel.min.js:解析JSX语法代码转为JS代码的库,即ES6==>ES5;JSX==>JS

    1.3 JSX

    全称: JavaScript XML,是react定义的一种类似于XML的JS扩展语法,本质是React.createElement(component, props, ...children)方法的语法糖。JXS最终产生的虚拟DOM就是一个JS对象。

    1.3.1 为什么要用JSX

    • 更加简单地创建虚拟DOM
      (1)使用JSX创建虚拟DOM
      在这里插入图片描述
      (2)使用JS创建虚拟DOM(用原生JS,不用babel,开发中不使用)
      在这里插入图片描述

    • JSX创建虚拟DOM的方法是JS方法的语法糖

    1.3.2 JSX语法规则

    • 定义虚拟DOM时不用写引号

    • 标签中混入JS表达式时要用{}

      const VDOM=(


      前端js框架列表



      • {
        data.map((item,index)=>{
        return
      • {item}

      • })
        }


      )

    遇到以 { 开头的代码,以JS语法解析,且标签中的js表达式必须用{ }包含,比如

      中的{}中的{index}{item}

      注:key={index}:在进行组件遍历的时候必须要加一个key来区分每个组件

      • CSS类名指定不用class,用className
      • 内联样式style={{key:value}},如style={{color:‘white’,fontSize:20px}}
      • 虚拟DOM必须只有一个根标签
      • 标签必须闭合,如
      • 标签首字母
        (1)若小写字母开头,则转为html5中的同名元素,如无则报错
        (2)若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错

      辨析【js表达式】和【js语句 (代码)】

      • 表达式:一个表达式可以产生一个值,可以放在任何需要值的地方,如
        (1)a
        (2)a+b
        (3)demo(1)
        (4)arr.map()
        (5)function test () {}
      • 语句(代码):如
        (1)if () {}
        (2)for () {}
        (3)switch () {case:xxxx}

      1.4 虚拟DOM

      1. 本质是Object,即一般对象(不是数组对象和函数对象)
      2. 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是react内部在用,无需真实DOM中那么多属性
      3. 虚拟DOM最终会被react转换为真实DOM呈现在页面上

      1.5 模块与组件

      1.5.1 模块

      • 向外界提供特定功能的js程序。随着业务逻辑增加,代码越来越多且复杂,此时js一般会拆成多个js文件来编写,一般一个js文件就是一个模块
      • 作用:复用js,简化js的编写,提高js的运行效率
      • 模块化:当应用的js都以模块来编写的, 这个应用就是一个模块化的应用

      1.5.2 组件

      • 用来实现局部功能效果的代码和资源的集合(html/css/js/image等等)。比如一个功能齐全的Header栏就是一个组件。
      • 复用编码, 简化项目编码, 提高运行效率
      • 组件化:当应用是以多组件的方式实现, 这个应用就是一个组件化的应用

      第2章 React面向组件编程

      2.1 创建组件

      2.1.1 函数式组件

      适用于简单组件(无state

      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      注意事项:

      • 函数中的this指向undefined,因为babel编译后开启了严格模式

      本例中,执行了ReactDOM.render(.......)之后,发生了什么?

      1. React解析组件标签,找到了MyComponent组件。
      2. 发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。

      2.1.2 类式组件

      适用于复杂组件(有state

      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      注意事项:

      • 类式组件必须要继承react的内值类React.Component
      • 一定要写render函数,且要有返回值
      • render是放在类的原型对象上,供实例使用。
      • render中的this指向类的实例对象(原因见下方) <=> 类式组件的实例对象。

      本例中,执行了ReactDOM.render(.......)之后,发生了什么?

      1. React解析组件标签,找到了MyComponent组件。
      2. 发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
      3. render返回的虚拟DOM转为真实DOM,随后呈现在页面中。

      2.2 组件实例的三大属性

      针对于类式组件,hooks可以让函数式组件也有三大属性

      2.2.1 state属性

      • state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
      • 组件被称为"状态机", 通过更新组件的state更新对应的页面显示(重新渲染组件)

      代码实例: 点击切换天气状态

      标准写法

      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46

      注意事项:

      • 在构造器constructor中初始化state,且要用对象形式初始化state
      • render函数中创建虚拟DOM时,直接在标签中绑定事件,且事件写法不同于原生JS,如原生JS中的onclick事件,在react中要写成onClick,其他同理。
      • onClick={this.changeWeather}是将this.changeWeather函数赋值给onClick,函数后面不能加括号,否则就是将函数返回值赋值
      • 事件的回调函数要写在类中,此时它放在类的实例对象的原型链上,供实例使用。在本例中,由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用,且类中的方法默认开启了局部的严格模式,所以其中this的指向不是实例对象,而是undefinedrender函数也是放在对象的原型链上,但是它是由类的实例对象调用的,所以this指向实例对象
      • 自定义的事件回调函数的this可以在构造器中用bind更改指向,生成的新函数直接在类中,所以this指向实例对象
      • 注意: 状态必须通过setState以对象的形式进行更新,且更新是一种合并,不是替换
      • const {isHot,wind} = this.state是ES6中的对象解构,获取多个对象属性的方法

      简写方法

      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26

      注意事项:

      • 写入数据有两种方式:
        a)在虚拟DOM标签中直接写入数据,如({18}代表是数值型,“18”代表是字符串)。props是对象,里面存储着键值对形式,name="18" 中,name是键,"18"是值
        b)在虚拟DOM标签中用扩展运算符展开对象,如。这里…不是三点(拓展/展开运算符),因为对象不能使用拓展运算符(数组和可遍历的伪数组可以)。而{...obj}是ES6语法,是一个复制对象。但是在这里,{...p}并不是复制对象,因为这里的{}表示括号里面要写js表达式了,所以真正写的还是...p,这里react+babel就允许用展开运算符展开对象,不能随便使用(不能用console.log()查看),仅仅适用于标签传递数据时。
      • 数据会存入到类式组件props属性中,在render中可以通过this.props获取数据
      • 内部读取某个属性值用this.props.属性名
      • 组件类的构造函数constructor可省。

      在 react 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前调用super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。

      通常,在 react 中,构造函数仅用于以下两种情况: a)通过给 this.state 赋值对象来初始化内部 state
      b)为事件处理函数绑定实例

      对props进行限制

      首先要引入prop-types库,用于对组件标签属性进行限制

      限制props有两种方法:
      a)限制内容写在类外面

      Person.propTypes = {
      			name:PropTypes.string.isRequired, //限制name必传,且为字符串
      			sex:PropTypes.string,//限制sex为字符串
      			age:PropTypes.number,//限制age为数值
      			speak:PropTypes.func,//限制speak为函数
      		}
      		//指定默认标签属性值
      Person.defaultProps = {
      	sex:'男',//sex默认值为男
      	age:18 //age默认值为18
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      b)限制内容写在类里面

      static propTypes = {
      				name:PropTypes.string.isRequired, //限制name必传,且为字符串
      				sex:PropTypes.string,//限制sex为字符串
      				age:PropTypes.number,//限制age为数值
      			}
      
      			//指定默认标签属性值
      static defaultProps = {
      	sex:'男',//sex默认值为男
      	age:18 //age默认值为18
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

      注意事项:

      • 首字母小写的propTypes是类里的属性规则
      • 首字母大写的PropTypes是prop-types库里的内置对象
      • React v15.5 开始已弃用的写法name: React.PropTypes.string.isRequired

      函数组件使用props

      三大属性中,只有props可以用于函数组件,因为函数可以接收参数,staterefs都不能用于函数组件。

      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26

      注意事项:

      • 限制props只能使用第一种方法

      2.2.3 refs属性

      组件内的标签可以定义ref属性来标识自己。

      this.refs拿到真实DOM
      字符串形式的ref

      class Demo extends React.Component{
      			//展示左侧输入框的数据
      			showData = ()=>{
      				console.log(this);
      				const {input1} = this.refs
      				alert(input1.value)
      			}
      			//展示右侧输入框的数据
      			showData2 = ()=>{
      				console.log(this);
      				const {input2} = this.refs
      				alert(input2.value)
      			}
      			render(){
      				return(
      					
         
      ) } } //渲染组件到页面 ReactDOM.render(,document.getElementById('test'))
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25

      不被官方推荐,因为效率不高

      回调ref

      1. 内联函数(推荐)

       this.input1 = currentNode } type="text" placeholder="点击按钮提示数据"/>  
      {/*这里的this是指向实例对象,因为箭头函数没有指向,查找外侧的this指向*/}
      
      • 1
      • 2

      注意:

      • 函数中的参数currentNoderef所在的节点
      • 此时input1是类的属性,即直接绑定到类里,而不是像字符串ref一样添加到refs对象里

      回调函数:有定义、没有执行(函数名())、最终执行(别人调用)

      2. 类绑定函数

      saveInput = (c)=>{
      				this.input1 = c;
      				console.log('@',c);
      			}
      
      
      • 1
      • 2
      • 3
      • 4
      • 5

      3. 回调ref中回调执行次数
      内联函数更新时会执行两次,一次清空,一次执行函数,类绑定函数不会。

      交互和更改状态的区别:取决于是否修改render函数中节点的内容

      createRef(react最推荐)

      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的,有多少个节点表示ref,就要调用多少次 React.createRef

      2.2.4 事件处理

      1. 通过onXxx属性指定事件处理函数(注意大小写)
        a)React使用的是自定义(合成)事件, 而不是使用的原生DOM事件——为了更好的兼容性
        b)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)———为了高效
      2. 通过event.target得到发生事件的DOM元素对象。发生事件的元素就是操作的元素则可以省略ref。
      3. 不要过度使用ref

      2.3 受控组件和非受控组件

      以表单提交案例为例

      2.3.1 非受控组件

      ref实现

      页面中所有的输入类DOM现用现取,即通过ref标识DOM,进而获取数据

      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

      知识点:

      1. 表单
        中都有onSubmit属性来控制提交之后的状态
      2. 输入DOM(如)得有name属性才能通过GET请求获取到query参数(用?携带)
      3. 删掉action无法阻止表单页面刷新以及地址栏更新,得要禁止默认事件event.preventDefault()

      2.3.2 受控组件

      onChange+state实现
      页面中所有的输入类DOM将数据存在state

      更推荐用受控组件,减少ref的使用

      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40

      2.4.1 高阶函数

      如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。

      1. 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
      2. 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
        常见的高阶函数有:PromisesetTimeoutarr.map()等等

      函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。

      function sum(a){
      	return(b)=>{
      		return (c)=>{
      			return a+b+c
      		}
      	}
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      用函数的柯里化实现受控组件
      为了不重复编写相似的代码,如saveUsernamesavePassword

      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • this.saveFormData('username'),有了小括号,立即调用,但是返回的还是一个函数,符合回调函数的要求

      • [dataType]:调用变量形式的对象属性名

      • event形参不需要实参,可以直接调用,所以event不能写进this.saveFormData('username')的参数中,得用柯里化形式来体现
        不用函数柯里化的实现方式
        只需改两处:

        saveFormData = (dataType,event)=>{
        this.setState({[dataType]:event.target.value})
        }
        用户名: this.saveFormData(‘username’,event) } type=“text” name=“username”/>

      两种方法都常用

      2.5 生命周期

      2.5.1 引入

      1. 组件从创建到死亡它会经历一些特定的阶段。
      2. React组件中包含一系列钩子函数(生命周期回调函数), 会在特定的时刻调用。
      3. 我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。
      4. 生命周期回调函数<=> 生命周期钩子函数<=>生命周期函数<=>生命周期钩子
      5. 挂载 mount: 组件第一次被渲染到DOM中的时候,就为其设置一个计时器,叫挂载 ReactDOM.render(, document.querySelector('test'))
      6. 卸载 unmount:组件被删除时,应该清除计时器,在react中被称为卸载 ReactDOM.unmountComponentAtNode(document.querySelector('test'))

      知识点:

      • 行内样式写法:style={{opacity:opacity}}
      • 对象中属性名和属性值相同时触发简写:{opacity:opacity}简写为{opacity}
      • render执行次数:1+n(初始化渲染+更新状态)

      2.5.2 react生命周期(旧)

      在这里插入图片描述
      三条线:

      1. 父组件render–>componentWillReceiveProps–>shouldComponentUpdate–>componentWillUpdate–>render–>componentDidUpdate–>componentWillUnmount
      2. setState()–>shouldComponentUpdate–>componentWillUpdate–>render–>componentDidUpdate–>componentWillUnmount
      3. forceUpdate()–>componentWillUpdate–>render–>componentDidUpdate–>componentWillUnmount

      知识点:

      • shouldComponentUpdate返回值必须为truefalse,若不写,默认为true
      • forceUpdate():强制更新。不更改任何状态中的数据,强制更新一下
      • componentWillReceiveProps:第一次调用不算。

      生命周期的三个阶段(旧)

      1. 初始化阶段: 由ReactDOM.render()触发—初次渲染
        1)constructor()
        2)componentWillMount()
        3)render()常用,一定得调用
        4)componentDidMount()常用,一般在这个钩子中做一些初始化的事,如开启定时器、发送网络请求、订阅消息
      2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
        1)shouldComponentUpdate()
        2)componentWillUpdate()
        3)render()
        4)componentDidUpdate()
      3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
        1)componentWillUnmount()常用,一般在这个钩子中做一些收尾的事,如关闭定时器、取消订阅消息

      2.5.3 react生命周期(新)

      在这里插入图片描述

      • 17及以上版本componentWillMountcomponentWillUpdatecomponentWillReceiveProps三个钩子使用前要加UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。
      • 新的生命周期和旧的生命周期相比,除了即将废弃三个钩子,还添加了两个新的钩子getDerivedStateFromPropsgetSnapshotBeforeUpdate
      • static getDerivedStateFromProps:适用于罕见用例(几乎不用),返回null或state对象,state的值在任何时候都取决于props
      • getSnapshotBeforeUpdate(prevProps,prevState,):在更新之前获取快照,返回值传递给componentDidUpdate(prevProps,prevState,snapshotValue)

      2.6 虚拟DOM与DOM Diffing算法

      DOM Diffing算法对比的最小粒度是标签,且逐层对比
      在这里插入图片描述

      2.6.1 key的作用

      经典面试题:

      1. react/vue中的key有什么作用?(key的内部原理是什么?)
      2. 为什么遍历列表时,key最好不要用index

      解释:

      • 虚拟DOM中key的作用:
      1. 简单地说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。

      2. 详细地说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】, 随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
        a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
        (1) 若虚拟DOM中内容没变, 直接使用之前的真实DOM
        (2) 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
        b. 旧虚拟DOM中未找到与新虚拟DOM相同的key:
        根据数据创建新的真实DOM,随后渲染到到页面

      • 用index作为key可能会引发的问题:
      1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
        会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

      2. 如果结构中还包含输入类的DOM(如input,虚拟DOM中标签属性少,input没有value属性):
        会产生错误DOM更新 ==> 界面有问题。

      3. 注意! 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

      • 开发中如何选择key
      1. 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
      2. 如果确定只是简单的展示数据,用index也是可以的。

      第3章 react应用(基于react脚手架)

      使用脚手架开发的项目:模块化、组件化、工程化

      3.1 前期准备

      3.1.1 安装node.js及npm

      安装教程

      1. 下载.msi安装包 node官网
      2. 安装路径: D: odejs
      3. 查看是否安装成功:win+r输入cmd,输入命令node;查看npm版本 npm -v。node.js安装时会自动安装npm
      4. 改变npm下载的模块包的默认存储地址:
        npm config set prefix “D: odejs ode_global”
        更改默认缓存位置:npm config set cache “D: odejs ode_cache”
        测试:npm list -global或者npm config get prefix或者npm config get cache
      5. 更改环境变量
        控制面板–系统–高级系统设置–高级–环境变量
        1) 用户变量 path 中把 C:UsersAdministratorAppDataRoaming pm;—改为----D: odejs ode_global
        2)在系统变量中 新增变量NODE_PATH----- D: odejs ode_global ode_modules

      安装cnpm: npm install -g cnpm --registry=https://registry.npm.taobao.org
      安装yarn: 不推荐用npm i -g yarn 安装(但我是这样装的)
      react扩展程序: React Developer Tools(facebook)

      问题:

      1. 安装了cnpm之后,才成功安装了 cnpm i -g create-react-app
      2. cmd必须得以管理员模式运行才能使用npm命令。解决方式是将vscode以管理员模式运行,并用vscode运行npm命令

      全局安装react脚手架: npm i -g create-react-app (不成功),得用 cnpm i -g create-react-app
      创建项目: 切换到想创建项目的文件夹,使用命令create-react-app app_name

      换源: 在这里建议把npm换源,使用国内镜像创建项目。因为使用脚手架配置项目的时候,需要下载的依赖实在太多,不换源的话很可能中途卡住就失败了。(没换)
      换源方式: npm config set registry https://registry.npm.taobao.org
      验证是否换源成功:npm config get registry
      显示出上述地址的话就是换源成功了
      npm cnpm npx nvm区别

      知识点:

      • vscode安装时勾选open with code
      • react是SPA应用
      • index.html文件中,%PUBLIC_URL% 代表public文件夹的路径
      • robots.txt 爬虫规则文件
      • export name 分别暴露;export default name 默认暴露
      • import React,{Component} from ‘react’ :{Component}在react文件里分别暴露了

      3.1.2 react脚手架项目结构

      • public ---- 静态资源文件夹
        favicon.icon ------ 网站页签图标
        index.html -------- 主页面
        logo192.png ------- logo图
        logo512.png ------- logo图
        manifest.json ----- 应用加壳的配置文件
        robots.txt -------- 爬虫协议文件
      • src ---- 源码文件夹
        App.css -------- App组件的样式,App.js里引用
        App.js --------- App组件,子组件可以都写在components文件夹里,子组件文件夹首字母大写。路由组件写在pages文件夹中
        App.test.js ---- 用于给App做测试
        index.css ------ 样式,通用样式,入口文件里引用
        index.js ------- 入口文件
        logo.svg ------- logo图
        reportWebVitals.js-------页面性能分析文件(需要web-vitals库的支持)
        setupTests.js-------组件单元测试的文件(需要jest-dom库的支持)

      注意:
      组件js文件除了首字母大写,还可以将后缀改为jsx,来和普通js文件进行区分

      3.1.3 样式模块化

      import name from './name.module.css'
      className={name.title}
      
      • 1
      • 2

      基本上不用,而是用less嵌套

      遇到的问题:

      • 删除引入网站页签图标的代码后,页签还是在???
        因为浏览器缓存了之前的图标,清空浏览器缓存之后,就正常了(并不是)

      • win10系统下在cmd中 输入npm start后,项目能正常跑起来,但是整个cmd窗口就不能再输入任何东西
        ctrl+c 可以停掉

      • 安装插件 ES7+ React/Redux/React-Native snippets

      3.1.4 功能界面的组件化编码流程(通用)

      1. 拆分组件: 拆分界面,抽取组件
      2. 实现静态组件: 使用组件实现静态页面效果
      3. 实现动态组件
        3.1 动态显示初始化数据
        3.1.1 数据类型
        3.1.2 数据名称
        3.1.3 保存在哪个组件
        3.2 交互(从绑定事件监听开始)

      3.2 案例:组件的组合使用——TodoList

      3.2.1 注意事项

      • APP给list用props传递参数:,这里是以键值对的形式传递数组,键是第一个todos,数组是第二个todos。其中第二个todos(数组)为const {todos}=this.statetodos是从state中解构出来的由对象组成的数组。

      • List给Item用props传递参数:

        todos.map(todo=>{
        return
        })

      其中 {todos}=this.props ,是 todos解构对象成数组,即 todos=[{},{}],todo代表todos数组中的每个元素(对象)。

      {...todo}是在展开对象。原生js中,对象不能使用拓展运算符(数组和可遍历的伪数组可以)。而{...obj}是ES6语法,是一个复制对象。但是在这里,{...todo}并不是复制对象,因为这里的{}表示括号里面要写js表达式了,所以真正写的还是...todo,这里react+babel就允许用展开运算符展开对象,不能随便使用(不能用console.log()查看),仅仅适用于标签传递数据时。

      • 父组件向子组件传数据:直接在子组件标签中添加键值对形式的数据(props

      • 子组件向父组件传递数据:父组件通过props给子组件传递一个函数,子组件在想要传递数据给父组件时,调用该函数

      • 生成时间戳:Date.now()

      • UUID:生成唯一时间戳的库
        npm i uuid (库比较大)
        npm i nanoid(库小,很快安装)/ yarn add nanoid
        用法:

        import {nanoid} from ‘nanoid’
        //调用
        nanoid()

      • str.trim() 去除字符串的头尾空格

      • onMouseLeave={this.handleMouse(false)}:小括号调用了函数,定义函数时要柯里化

      • 状态在哪里,操作状态的方法就在哪里

      • window.confirm("确定删除吗"):返回true或false

      • 在react中,若加了checked属性,则必须加onChange事件,不然无法改变勾选状态。defaultChecked只在第一次起效果。

      3.2.2 todoList案例相关知识点

      1. 拆分组件、实现静态组件,注意:classNamestyle的写法
      2. 动态初始化列表,如何确定将数据放在哪个组件的state中?
        1)某个组件使用:放在其自身的state
        2)某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
      3. 关于父子之间通信:
        1)【父组件】给【子组件】传递数据:通过props传递
        2)【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
      4. 注意defaultCheckedchecked的区别,类似的还有:defaultValuevalue
      5. 状态在哪里,操作状态的方法就在哪里

      第4章 React ajax

      4.1 理解

      1. React本身只关注于界面, 并不包含发送ajax请求的代码
      2. 前端应用需要通过ajax请求与后台进行交互(json数据)
      3. react应用中需要集成第三方ajax库(或自己封装,但是基本不会自己封装)

      4.2 实现

      1. jQuery: 要操作DOM,并且比较重, 如果需要另外引入不建议使用
      2. axios: 轻量级, 建议使用
        1)封装XmlHttpRequest对象的ajax
        2)promise风格
        3)可以用在浏览器端和node服务器端

      4.3 配置代理

      react脚手架配置代理总结

      方法一

      在package.json中追加如下配置

      "proxy":"http://localhost:5000"
      
      • 1

      说明:

      1. 优点:配置简单,前端请求资源时可以不加任何前缀。
      2. 缺点:不能配置多个代理。
      3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)

      方法二

      1. 第一步:创建代理配置文件

        在src下创建配置文件:src/setupProxy.js(不允许改名)
        
        • 1
      2. 编写setupProxy.js配置具体代理规则:

        const proxy = require('http-proxy-middleware')
        
        module.exports = function(app) {
          app.use(
            proxy('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
              target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
              changeOrigin: true, //控制服务器接收到的请求头中host字段的值
              /*
              	changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
              	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
              	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
              */
              pathRewrite: {'^/api1': ''} //去除请求前缀/api1,保证交给后台服务器的是正常请求地址(必须配置)
            }),
            proxy('/api2', { 
              target: 'http://localhost:5001',
              changeOrigin: true,
              pathRewrite: {'^/api2': ''}
            })
          )
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21

      说明:

      1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
      2. 缺点:配置繁琐,前端请求资源时必须加前缀。

      学习原生AJAX时,解决跨域问题是在server.js代码中加入以下代码(即在后端解决)

        //设置响应头  设置允许跨域  
        response.setHeader('Access-Control-Allow-Origin', '*');
        //响应头
        response.setHeader('Access-Control-Allow-Headers', '*');
      
      • 1
      • 2
      • 3
      • 4

      为CORS解决方案

      注意:127.0.0.1和localhost的区别

      1. 127.0.0.1 为保留地址,直白地说 127.0.0.1 就是一个 ip 地址,不同于其它 ip 地址的是它是一个指向本机的 ip 地址,也称为环回地址,该 ip 地址不能使用在公网 ip 上,对任何一台电脑来说,不管是否连接到INTERNET上,127.0.0.1 都是指向自己。
        事实上整个 127.* 网段都是环回地址,127.* ip 段都为保留地址,只是规则制定者将其中的 127.0.0.1 规定为指向本机自己。

      2. 在电脑网络中,localhost 为“本地主机”,是给回路网络接口(loopback)的一个标准主机名,IPv4 相对应的地址为 127.0.0.1,IPv6 相对应的地址为 [::1],这个名称也是一个保留域名。
        说白了就是 localhost 就是一个本地域名,不是地址。该本地域名指向的 ip 地址就是 127.0.0.1 ,也就是指向本机,localhost 更方便记忆与输入,因 hosts文件 定义了 localhost = 127.0.0.1 ,所以你只需要记住 localhost 就可以代表本机了。

      3. 对机器来说,它需要通过 hosts 文件来定义 localhost = 127.0.0.1(多一次解析 ip 的步骤) ,才能知道 localhost 代表的是本机,机器只知道 127.0.0.1 代表本机,因此当你向 localhost 发消息的时候,机器会自动翻译后给127.0.0.1发消息。

      4. 在实际工作中,localhost 是不经过网卡传输的,所以它不受网络防火墙和与网卡相关的种种限制,而 127.0.0.1 则要通过网卡传输数据,是必须依赖网卡的。这一点也是 localhost 和 127.0.0.1 的最大的区别,这就是为什么有时候用 localhost 可以访问,但用 127.0.0.1 就不可以的情况。

      4.4 案例—github用户搜索

      4.4.1 axios发送请求

      1. 连续解构赋值:

        const {keyWordElement:{value}} = this

      相当于 this.keyWordElement.value,此时keyWordElement并没有被解构,只是写的过程,即

      console.log(keyWordElement) // undefined
      
      
      const {keyWordElement:{value:keyWord}} = this
      
      • 1
      • 2
      • 3
      • 4

      连续解构赋值+重命名

      1. 状态中的数据驱动着页面的展示

      2. 连续写三元表达式

        isFirst ?

        欢迎使用,输入关键字,随后点击搜索

        :
        isLoading ?

        Loading…

        :
        err ?

        {color:‘red’}}>{err}

      如果第一个判断为正确,就不会执行后面的代码了,所以顺序很重要

      4.4.2 消息订阅-发布机制(pubsub-js)

      使用:

      1. 工具库: PubSubJS
      2. 下载: npm install pubsub-js --save
      3. 使用:
        1. import PubSub from ‘pubsub-js’ //引入
        2. PubSub.subscribe(‘delete’, function(data){ }); //订阅
        3. PubSub.publish(‘delete’, data) //发布消息

      注意:
      4. 适用于任意组件之前的消息沟通
      5. 谁用谁接,谁传谁发

      4.4.3 扩展:使用Fetch发送网络请求(用得不多)

      • 发送AJAX请求的方法:
      1. 原生AJAX:xhr
      2. jQuery:对xhr的封装
      3. axios:对xhr的封装
      4. fetch:不是库,是windows内置,且是Promise风格。和xhr并列
      • fetch的特点关注分离
      • fetch兼容性不高

      4.4 案例总结

      1. 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。

      2. ES6小知识点:解构赋值+重命名
        let obj = {a:{b:1}}
        const {a} = obj; //传统解构赋值
        const {a:{b}} = obj; //连续解构赋值
        const {a:{b:value}} = obj; //连续解构赋值+重命名

      3. 消息订阅与发布机制
        1.先订阅,再发布(理解:有一种隔空对话的感觉)
        2.适用于任意组件间通信
        3.要在组件的componentWillUnmount中取消订阅

      4. fetch发送请求(关注分离的设计思想)

        try {
        	const response= await fetch(`/api1/search/users2?q=${keyWord}`)
        	const data = await response.json()
        	console.log(data);
        } catch (error) {
        	console.log('请求出错',error);
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7

      第5章 react路由(v5)

      5.1. 相关理解

      5.1.1. SPA的理解

      1. 单页Web应用(single page web application,SPA)。
      2. 整个应用只有一个完整的页面。
      3. 点击页面中的链接不会刷新页面,只会做页面的局部更新
      4. 数据都需要通过ajax请求获取, 并在前端异步展现。
      5. 单页面、多组件

      5.1.2. 路由的理解

      • 什么是路由
      1. 一个路由就是一个映射关系(key:value)
      2. key为路径, value可能是function或component
      • 路由分类
      1. 后端路由:
        1. 理解: value是function, 用来处理客户端提交的请求。
        2. 注册路由: router.get(path, function(req, res))
        3. 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据
      2. 前端路由:
        1. 浏览器端路由,value是component,用于展示页面内容。
        2. 注册路由:
        3. 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件
          4)前端路由也要靠BOM 上的 history

      5.1.3. react-router-dom的理解

      1. react的一个插件库
      2. 专门用来实现一个SPA应用。
      3. 基于react的项目基本都会用到此库。

      **注意:**react-router总共有三种形式的库,适用于三种场景:

      • web,对应的库名叫react-router-dom
      • native
      • anywhere(哪里都可以用)

      5.2 路由的基本使用

      1. 明确好界面中的导航区、展示区

      2. 导航区的a标签改为Link标签
        Demo

      3. 展示区写Route标签进行路径的匹配(在呈现路由组件内容的位置注册路由)
        以前的版本

        
         
        
        • 1
        • 2

        现在的版本 6.x.x

        
        	}/>
        	}/>
        
        
        • 1
        • 2
        • 3
        • 4
      4. 的最外侧包裹了一个

      注意:

      • import {Link,Route} from 'react-router-dom' 分别暴露,用哪个组件取哪个组件 -
      • 地址栏中#后面的内容不会作为资源发给服务器

      5.3 路由组件与一般组件

      1. 写法不同:
        一般组件:
        路由组件:

      2. 存放位置不同:
        一般组件:components
        路由组件:pages

      3. 接收到的props不同:
        一般组件:写组件标签时传递了什么,就能收到什么
        路由组件:接收到三个固定的属性

        history:
        go: ? go(n)
        goBack: ? goBack()
        goForward: ? goForward()
        push: ? push(path, state)
        replace: ? replace(path, state)
        location:
        pathname: “/about”
        search: “”
        state: undefined
        match:
        params: {}
        path: “/about”
        url: “/about”

      5.4 NavLink及其封装

      1. NavLink可以实现路由链接的高亮,通过activeClassName指定样式名

      2. 标签体内容是一个特殊的标签属性,可以通过this.props.children获取。因此以下两段代码是等价的。

        About

      5.5 Switch

      1. 注册路由时用Switch包裹所有路由
      2. 通常情况下,path和component是一一对应的关系。
      3. Switch可以提高路由匹配效率(单一匹配)。

      5.6 解决样式丢失问题

      1. 什么时候样式丢失?
        路由路径多级,且刷新的时候

      2. 解决办法
        1)public/index.html 中 引入样式时不写 ./ 而是写 / (常用)。因为./是相对路径,去掉之后就是绝对路径,直接去localhost:3000下调文件

        
        
        • 1

        2)public/index.html 中 引入样式时不写 ./ 写 %PUBLIC_URL% (常用)。因为%PUBLIC_URL%代表public的绝对路径

        
        
        • 1

        3)将改为

      5.7 路由的模糊匹配与严格匹配

      1. 默认使用的是模糊匹配(简单记:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)

        Home  //模糊匹配
        
        
        • 1
        • 2
      2. 开启严格匹配:

        // exact={true}可以简写为exact
      3. 严格匹配不要随便开启,需要再开,有些时候开启会导致无法继续匹配二级路由

      5.8 Redirect重定向

      1. 一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由

      2. 具体编码:

      5.8 嵌套路由(多级路由)

      1. 注册子路由时要写上父路由的path值

        {/* 注册路由 */}




      2. 路由的匹配是按照注册路由的顺序进行的

      5.9 向路由组件传递参数

      5.9.1 params参数(最多)

      1. 路由链接(携带参数):

      5.9.2 search参数

      1. 路由链接(携带参数):

      5.9.3 state参数(不同于普通组件的state属性)

      1. 路由链接(携带参数):

        详情
      2. 注册路由(无需声明,正常注册即可):

      3. 接收参数:this.props.location.state
        备注:刷新也可以保留住参数,但回退不行,无法回退到上一个·同级路由**????**(老师演示的时候可以回退,自己跑代码回退不了)

      5.10 push和replace(难)

      1. 路由是对浏览器历史记录的操作,总共有两种操作,push(压栈)和replace(替代栈顶元素)。
      2. 默认是push模式,要想开启replace模式,则在路由连接标签中加入replace={true}replace

      5.11 编程式路由导航

      借助this.prosp.history对象上的API对操作路由跳转、前进、后退,而不用路由的,但还是要注册路由

      • this.prosp.history.push()
      • this.prosp.history.replace()
      • this.prosp.history.goBack()
      • this.prosp.history.goForward()
      • this.prosp.history.go()

      5.12 withRouter的使用

      1. withRouter可以加工一般组件,让一般组件具备路由组件所特有的API

      2. withRouter的返回值是一个新组件。

      3. 在一般组件中要用到路由组件的props属性时引入。

        import {withRouter} from 'react-router-dom'
        
        • 1

        需要暴露

        export default withRouter(Header)
        
        • 1

      5.12 BrowserRouter与HashRouter的区别

      1. 底层原理不一样:
        BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
        HashRouter使用的是URL的哈希值。
      2. path表现形式不一样
        BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
        HashRouter的路径包含#,例如:localhost:3000/#/demo/test
      3. 刷新后对路由state参数的影响
        1)BrowserRouter没有任何影响,因为state保存在history对象中。
        2)HashRouter刷新后会导致路由state参数的丢失!!!
      4. 备注: HashRouter可以用于解决一些路径错误相关的问题。

      第6章 React UI组件库

      6.1 简介

      material-ui(国外)

      1. 官网: http://www.material-ui.com/#/
      2. github: https://github.com/callemall/material-ui

      ant-design(国内蚂蚁金服)

      1. 官网: https://ant.design/index-cn
      2. Github: https://github.com/ant-design/ant-design/
      3. 不是所有场景都使用,最适用于后台管理系统

      6.2 使用

      安装依赖:npm install antd
      引入样式(不太合理,引入了全部组件的样式,应该按需引入

      import '../node_modules/antd/dist/antd.css'
      
      • 1

      引入库:

      import { Button,DatePicker } from 'antd';
      import {WechatOutlined,WeiboOutlined,SearchOutlined} from '@ant-design/icons'
      
      • 1
      • 2

      学会查看官网文档
      其他UI组件库:element ui、vant等

      6.3 按需引入和自定义主题

      重点: 学会查看文档(3.x文档更清楚,且适用于4.x)

      1. 安装依赖:yarn add react-app-rewired customize-cra babel-plugin-import less less-loader

      2. 修改package.json

        “scripts”: {
        “start”: “react-app-rewired start”,
        “build”: “react-app-rewired build”,
        “test”: “react-app-rewired test”,
        “eject”: “react-scripts eject”
        },

      3. 根目录下创建config-overrides.js

        //配置具体的修改规则
        const { override, fixBabelImports,addLessLoader} = require(‘customize-cra’);
        module.exports = override(
        fixBabelImports(‘import’, {
        libraryName: ‘antd’,
        libraryDirectory: ‘es’,
        style: true,
        }),
        addLessLoader({
        lessOptions:{
        javascriptEnabled: true,
        modifyVars: { ‘@primary-color’: ‘green’ },
        }
        }),
        );

      4. 备注:不用在组件里亲自引入样式了,即:import 'antd/dist/antd.css'应该删掉

      第7章 redux

      7.1 redux理解

      7.1.1 学习文档

      1. 英文文档: https://redux.js.org/
      2. 中文文档: http://www.redux.org.cn/
      3. Github: https://github.com/reactjs/redux

      7.1.2 redux简介

      1. redux是一个专门用于做状态管理的JS库(不是react插件库)。
      2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用。
      3. 作用: 集中式管理react应用中多个组件共享的状态。

      7.1.3 什么情况下需要使用redux

      1. 某个组件的状态,需要让其他组件可以随时拿到(共享)。
      2. 一个组件需要改变另一个组件的状态(通信)。
      3. 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。

      7.1.4 redux工作流程

      在这里插入图片描述

      • dispatch(action)是一个函数,action是一个对象
      • Action Creators(服务员)
        Store(老板)
        Reducers(厨师)

      7.2 redux的三个核心概念

      7.2.1. action

      1. 动作的对象
      2. 包含2个属性
        type:标识属性, 值为字符串, 唯一, 必要属性
        data:数据属性, 值类型任意, 可选属性
      3. 例子:{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }

      7.2.2. reducer

      1. 用于初始化状态、加工状态。
      2. 加工时,根据旧的state和action, 产生新的state的纯函数
      3. 有几个组件就有几个reducer?

      7.2.3. store

      1. 将state、action、reducer联系在一起的对象

      2. 如何得到此对象

        1. import {createStore} from 'redux'
        2. import reducer from './reducers'
        3. const store = createStore(reducer)
      3. 此对象的功能

        1. getState(): 得到state
        2. dispatch(action): 分发action, 触发reducer调用, 产生新的state
        3. subscribe(listener): 注册监听, 当产生了新的state时, 自动调用

      7.3. redux的核心API

      7.3.1. createstore()

      作用:创建包含指定reducer的store对象

      7.3.2. store对象

      1. 作用: redux库最核心的管理对象

      2. 它内部维护着:

        1. state
        2. reducer
      3. 核心方法:

        1. getState()
        2. dispatch(action)
        3. subscribe(listener) :store监测redux里的状态变化,发生变化则会被调用
      4. 具体编码:

        1. store.getState()

        2. store.dispatch({type:'INCREMENT', number})

        3. 放在入口文件index.js中

          store.subscribe(()=>{
          ReactDOM.render(,document.getElementById(‘root’))
          })

      7.3.3. applyMiddleware()

      作用:应用上基于redux的中间件(插件库)

      7.3.4. combineReducers()

      作用:合并多个reducer函数

      7.4 求和案例

      7.4.1 求和案例_redux精简版

      1. 去除Count组件自身的状态

      2. src下建立:

        //文件
        -redux
        	-store.js
        	-count_reducer.js
        
        • 1
        • 2
        • 3
        • 4
      3. store.js:
        1)引入redux中的createStore函数,创建一个store
        2)createStore调用时要传入一个为其服务的reducer
        3)记得暴露store对象(store.js默认暴露一个函数调用,函数返回一个对象,其他文件引入的时候将此对象命名为store)

      4. count_reducer.js:
        1)reducer的本质是一个函数,接收:preState,action,返回加工后的状态
        2)reducer有两个作用:初始化状态,加工状态
        3)reducer被第一次调用时,是store自动触发的。传递的preStateundefined,传递的action是:{type:'@@REDUX/INIT_a.2.b.4}

      5. 在index.js中监测store中状态的改变,一旦发生改变重新渲染

      备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。

      7.4.2 求和案例_redux完整版

      新增文件

      1. count_action.js 专门用于创建action对象
        注意:箭头函数返回一个对象时,要用()包裹,不然会被识别为函数体的{}

        createIncrementAction = data => ({type:INCREMENT,data})

      2. constant.js 放置容易写错的type值,目的只有一个:便于管理的同时防止程序员单词写错(要暴露)

      7.4.3 求和案例_redux异步action版(非必须)

      1. 同步actionaction的值是对象
        异步actionaction的值是函数。异步action一般会调用同步action
      2. 明确:延迟的动作(异步操作)不想交给组件自身,想交给action
      3. 何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。
      4. 具体编码:
        1)yarn add redux-thunk,并配置在store中
        2)创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。
        3)异步任务有结果后,分发一个同步的action去真正操作数据。
      5. 备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action

      7.4.4 求和案例_react-redux基本使用

      在这里插入图片描述

      1. 明确两个概念:
        1)UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。
        2)容器组件:负责和redux通信,将结果交给UI组件。

      2. 如何创建一个容器组件—靠react-redux 的connect函数:

        connect(mapStateToProps,mapDispatchToProps)(UI组件)
        
        • 1

        1)mapStateToProps:映射状态,返回值是一个对象。返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value
        2)mapDispatchToProps:映射操作状态的方法,返回值是一个对象。返回的对象中的key就作为传递给UI组件props的key,value就作为传递给UI组件props的value

      3. 备注:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入

      7.4.5 求和案例_react-redux优化

      1. 容器组件和UI组件整合一个文件

      2. 若有多个容器组件,无需自己给每个容器组件传递store,给包裹一个即可。

      3. 使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。

      4. mapDispatchToProps也可以简单的写成一个对象,因为react-redux可以自动dispatch

      5. 一个组件要和react-redux“打交道”要经过哪几步?
        1)定义好UI组件—不暴露
        2)引入connect生成一个容器组件,并暴露,写法如下:

        connect(
        	state => ({key:value}), //映射状态
        	{key:xxxxxAction} //映射操作状态的方法
        )(UI组件)
        
        • 1
        • 2
        • 3
        • 4

        3)在UI组件中通过this.props.xxxxxxx读取和操作状态

      7.4.6 求和案例_react-redux数据共享版

      1. 定义一个Person组件,和Count组件通过redux共享数据。
      2. 为Person组件编写:reducer、action,配置constant常量。
      3. 重点:Person的reducer和Count的Reducer要使用combineReducers进行合并。合并后的总状态是一个对象!!!
      4. 交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。
      5. reducers进行浅比较,数组、对象只比较地址,所以地址不变就不会重新渲染

      7.4.7 求和案例_react-redux开发者工具的使用

      Redux DevTools
      在这里插入图片描述

      1. 引入库

        yarn add redux-devtools-extension

      2. 在store.js中进行配置

        import {composeWithDevTools} from ‘redux-devtools-extension’
        export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

      7.4.8 求和案例_react-redux最终版

      1. 所有变量名字要规范,尽量触发对象的简写形式。
      2. reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer

      7.5 纯函数和高阶函数

      7.5.1. 纯函数

      1. 一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)
      2. 必须遵守以下一些约束
        1. 不得改写参数数据
        2. 不会产生任何副作用,例如网络请求,输入和输出设备
        3. 不能调用Date.now()或者Math.random()等不纯的方法
      3. redux的reducer函数必须是一个纯函数

      7.8.2. 高阶函数

      1. 理解: 一类特别的函数
        1. 情况1: 参数是函数
        2. 情况2: 返回是函数
      2. 常见的高阶函数:
        1. 定时器设置函数
        2. 数组的forEach()/map()/filter()/reduce()/find()/bind()
        3. promise
        4. react-redux中的connect函数
      3. 作用: 能实现更加动态, 更加可扩展的功能

      7.9 项目打包

      1. 打包

        npm run build
        
        • 1

        生成build文件夹

      2. 部署在服务器上
        1)阿里云线上服务器
        2)express(Node中快速搭建服务器的框架)搭建服务器
        3)借助第三方库库快速搭建:以指定的文件夹作为根目录快速开启一台服务器。如 serve

        npm i serve -g  //全局安装
        serve build   // 打开生成的build文件夹
        
        • 1
        • 2

      第8章 扩展

      8.1 setState

      setState更新状态的2种写法

      1. setState(stateChange, [callback])------对象式的setState
        1)stateChange为状态改变对象(该对象可以体现出状态的更改)
        2)callback是可选的回调函数, 它在状态更新完毕界面也更新后(render调用后)才被调用

      2. setState(updater, [callback])------函数式的setState
        1)updater为返回stateChange对象的函数。
        2)updater可以接收到stateprops
        3)callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。

      总结:

      1. 对象式的setState是函数式的setState的简写方式(语法糖)
      2. 使用原则:
        1)如果新状态不依赖于原状态 ===> 使用对象方
        2)如果新状态依赖于原状态 ===> 使用函数方式
        3)如果需要在setState()执行后获取最新的状态数据,
        要在第二个callback函数中读取

      8.2 lazyLoad

      路由组件的lazyLoad

      1. 通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包

      2. 通过指定在加载得到路由打包文件前显示一个自定义loading界面

        //1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
        const Login = lazy(()=>import('@/pages/Login')) //函数体不能加花括号
        
        //2.通过指定在加载得到路由打包文件前显示一个自定义loading界面
        loading.....}>
            
                
                
            
        
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10

      8.3 Hooks

      8.3.1 React Hook/Hooks是什么

      1. Hook是React 16.8.0版本增加的新特性/新语法
      2. 可以让你在函数组件中使用 state 以及其他的 React 特性

      8.3.2 三个常用的Hook

      1. State Hook: React.useState()
      2. Effect Hook: React.useEffect()
      3. Ref Hook: React.useRef()

      8.3.3 State Hook

      1. State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作

      2. 语法:

        const [xxx, setXxx] = React.useState(initValue)

      3. useState()说明:
        参数: 第一次初始化指定的值在内部作缓存
        返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数

      4. setXxx()的2种写法:
        setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
        setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值

      8.3.4 Effect Hook

      1. Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)

      2. React中的副作用操作:
        发ajax请求数据获取
        设置订阅 / 启动定时器
        手动更改真实DOM(尽量避免)

      3. 语法和说明:

        useEffect(() => {
        // 在此可以执行任何带副作用操作
        return () => { // 在组件卸载前执行
        // 在此做一些收尾工作, 比如清除定时器/取消订阅等
        }
        }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行

      4. 可以把 useEffect Hook 看做如下三个函数的组合
        componentDidMount()
        componentDidUpdate()
        componentWillUnmount()

      8.3.5 Ref Hook

      1. Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据

      2. 语法:

        const refContainer = useRef() //声明
        refContainer.current.xxx //调用

      3. 作用: 保存标签对象,功能与React.createRef()一样

      8.4 Fragment

      使用

      import {Fragment } from 'react'
      //类似于一个
      //可以添加一个属性 'key',用于遍历 <> //不能添加任何属性
      • 1
      • 2
      • 3
      • 4

      作用:可以不用必须有一个真实的DOM根标签了

      8.5 Context

      8.5.1 理解

      一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

      8.5.2 使用

      1. 创建Context容器对象:

        const XxxContext = React.createContext()  
        //const {Provider,Consumer} = XxxContext 也可以结构赋值
        
        • 1
        • 2
      2. 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:

        
        		子组件
            
        
        • 1
        • 2
        • 3
      3. 后代组件读取数据:
        1) 第一种方式:仅适用于类组件

          static contextType = xxxContext  // 声明接收context
        	  this.context // 读取context中的value数据
        
        • 1
        • 2

        2)第二种方式: 函数组件与类组件都可以

         
        		 //组件中的函数
        	    {
        	      value => ( // value就是context中的value数据
        	        要显示的内容
        	      )
        	    }
        	  
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8

      注意: 在应用开发中一般不用context, 一般都用它的封装react插件(如,react-redux)

      8.6 组件优化

      Component的2个问题

      1. 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低

      2. 只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

      效率高的做法

      只有当组件的stateprops数据发生改变时才重新render()

      原因

      Component中的shouldComponentUpdate()总是返回true

      解决

      1. 办法1: 重写shouldComponentUpdate()方法
        比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false

        shouldComponentUpdate(nextProps,nextState){
        		// console.log(this.props,this.state); //目前的props和state
        		// console.log(nextProps,nextState); //接下要变化的目标props,目标state
        		return !this.state.carName === nextState.carName
        	}
        
        • 1
        • 2
        • 3
        • 4
        • 5
      2. 办法2: 使用PureComponent
        PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true

        import React, { PureComponent } from 'react'
        //之前是import React, { Component } from 'react'
        
        • 1
        • 2

      注意:
      1. PureComponent,只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
      2. 不要直接修改state数据, 而是要产生新数据(新对象)。所以不能用unshift
      5. 项目中一般使用PureComponent来优化


      8.7 render props

      8.7.1 如何向组件内部动态传入带内容的结构(标签)

      1. Vue中:
        使用slot(插槽)技术, 也就是通过组件标签体传入结构

        
        
        • 1
      2. React中:
        使用children props:通过组件标签体传入结构
        使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性

      8.7.2 children props

      
       
      
      	{this.props.children}
      
      • 1
      • 2
      • 3
      • 4

      问题: 如果B组件需要A组件内的数据, ==> 做不到

      8.7.3 render props

       } />
      //相当于 }>
      
      • 1
      • 2

      A组件: {this.props.render(内部state数据)}
      C组件: 读取A组件传入的数据显示 {this.props.data}
      注意: 只要组件不带内容,单标签和双标签实现效果相同(即==

      8.8 错误边界

      8.8.1 理解

      错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面

      8.8.2 特点

      1. 只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
      2. 只适用于生产环境(build打包),而不是开发环境

      8.8.3 使用方式

      getDerivedStateFromError配合componentDidCatch

      // 生命周期函数,一旦后台组件报错,就会触发
      static getDerivedStateFromError(error) {
          console.log(error);
          // 在render之前触发
          // 返回新的state
          return {
              hasError: true,
          };
      }
      
      componentDidCatch(error, info) {
          // 统计页面的错误。发送请求发送到后台去
          console.log(error, info);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14

      8.9 组件通信方式总结

      8.9.1 组件间的关系:

      • 父子组件
      • 兄弟组件(非嵌套组件)
      • 祖孙组件(跨级组件)

      8.9.2 几种通信方式:

      1. props:
        1)children props
        2)render props
      2. 消息订阅-发布:
        pubs-sub、event等等
      3. 集中式管理:
        redux、dva等等
      4. conText:
        生产者-消费者模式

      8.9.2 比较好的搭配方式:

      • 父子组件:props
      • 兄弟组件:消息订阅-发布、集中式管理
      • 祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)

      第9章 React Router 6 快速上手

      9.1 概述

      1. React Router 以三个不同的包发布到 npm 上,它们分别为:

        1)react-router: 路由的核心库,提供了很多的:组件、钩子。
        2) react-router-dom: 包含react-router所有内容,并添加一些专门用于 DOM 的组件,例如
        3) react-router-native: 包括react-router所有内容,并添加一些专门用于ReactNative的API,例如:等。

      2. 与React Router 5.x 版本相比,改变了什么?

        1) 内置组件的变化:移除 ,新增 等。

        2) 语法的变化:component={About} 变为 element={}等。

        3).新增多个hook:useParamsuseNavigateuseMatch等。

        4) 官方明确推荐函数式组件了!!!

      9.2 Component

      1.

      1. 说明: 用于包裹整个应用。

      2. 示例代码:

        import React from "react";
        import ReactDOM from "react-dom";
        import { BrowserRouter } from "react-router-dom";
        
        ReactDOM.render(
          
            {/* 整体结构(通常为App组件) */}
          ,root
        );
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9

      2.

      1. 说明:作用与一样,但修改的是地址栏的hash值。
      2. 备注:6.x版本中 的用法与 5.x 相同。

      3.

      1. v6版本中移出了先前的,引入了新的替代者:

      2. 要配合使用,且必须要用包裹

      3. 相当于一个 if 语句,如果其路径与当前 URL 匹配,则呈现其对应的组件。

      4. 属性用于指定:匹配时是否区分大小写(默认为 false)。

      5. 当URL发生变化时, 都会查看其所有子 元素以找到最佳匹配并呈现组件 。

      6. 也可以嵌套使用,且可配合useRoutes()配置 “路由表” ,但需要通过 组件来渲染其子路由。

      7. 示例代码:

        
            /*path属性用于定义路径,element属性用于定义当前路径所对应的组件*/
            }>
        
        		/*用于定义嵌套路由,home是一级路由,对应的路径/home*/
            }>
               /*test1 和 test2 是二级路由,对应的路径是/home/test1 或 /home/test2*/
              }>
              }>
         	
        	
        		//Route也可以不写element属性, 这时就是用于展示嵌套的路由 .所对应的路径是/users/xxx
            
               } />
            
        
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16

      4.

      1. 作用: 修改URL,且不发送网络请求(路由链接)。

      2. 注意: 外侧需要用包裹。

      3. 示例代码:

        import { Link } from "react-router-dom";
        
        function Test() {
          return (
            
        按钮
        ); }
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9

      5.

      1. 作用: 与组件类似,且可实现导航的“高亮”效果。

      2. 示例代码:

        // 注意: NavLink默认类名是active,下面是指定自定义的className
        
        //自定义样式
         {
                console.log('home', isActive)
                return isActive ? 'base one' : 'base'
            }}
        >login
        
        /*
        	默认情况下,当Home的子组件匹配成功,Home的导航也会高亮,
        	当NavLink上添加了end属性后,若Home的子组件匹配成功,则Home的导航没有高亮效果。
        */
        home
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16

        注意: 默认情况下,当Home的子组件匹配成功,Home的导航也会高亮,当NavLink上添加了end属性后,若Home的子组件匹配成功,则Home的导航没有高亮效果。

      6.

      1. 作用:只要组件被渲染,就会修改路径,切换视图。

      2. replace属性用于控制跳转模式(push 或 replace,默认是push)。

      3. 示例代码:

        import React,{useState} from 'react'
        import {Navigate} from 'react-router-dom'
        
        export default function Home() {
        	const [sum,setSum] = useState(1)
        	return (
        		

        我是Home的内容

        {/* 根据sum的值决定是否切换视图 */} {sum === 1 ?

        sum的值为{sum}

        : }
        ) }
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14

      7.

      1. 产生嵌套时,渲染其对应的后续子路由。

      2. 示例代码:

        //根据路由表生成对应的路由规则
        const element = useRoutes([
          {
            path:'/about',
            element:
          },
          {
            path:'/home',
            element:,
            children:[
              {
                path:'news',
                element:
              },
              {
                path:'message',
                element:,
              }
            ]
          }
        ])
        
        //Home.js
        import React from 'react'
        import {NavLink,Outlet} from 'react-router-dom'
        
        export default function Home() {
        	return (
        		

        Home组件内容

        • News
        • Message
        {/* 指定路由组件呈现的位置 */}
        ) }
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43
        • 44
        • 45

      9.3 Hooks

      1-6常用

      1. useRoutes()

      1. 作用:根据路由表,动态创建

      2. 示例代码:

        //路由表配置:src/routes/index.js
        import About from '../pages/About'
        import Home from '../pages/Home'
        import {Navigate} from 'react-router-dom'
        
        export default [
        	{
        		path:'/about',
        		element:
        		children:[    //子路由
            		{  
            		path:'news',
            		element:
            		}
        		]
        	},
        	{
        		path:'/home',
        		element:
        	},
        	{
        		path:'/',
        		element:
        	}
        ]
        
        //App.jsx
        import React from 'react'
        import {NavLink,useRoutes} from 'react-router-dom'
        import routes from './routes'
        
        export default function App() {
        	//根据路由表生成对应的路由规则
        	const element = useRoutes(routes)  //一级路由用useRoutes()和element定位,子路由用定位
        	return (
        		
        ...... {/* 注册路由 */} {element} ......
        ) }
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25
        • 26
        • 27
        • 28
        • 29
        • 30
        • 31
        • 32
        • 33
        • 34
        • 35
        • 36
        • 37
        • 38
        • 39
        • 40
        • 41
        • 42
        • 43

        注意: 一级路由(App.js中)用useRoutes()和element定位,子路由用定位

      2. useNavigate()

      1. 作用:返回一个函数用来实现编程式导航。

      2. 示例代码:

        import React from 'react'
        import {useNavigate} from 'react-router-dom'
        
        export default function Demo() {
          const navigate = useNavigate()
          const handle = () => {
            //第一种使用方式:指定具体的路径
            navigate('/login', {
              replace: false,
              state: {a:1, b:2}
            }) 
            //第二种使用方式:传入数值进行前进或后退,类似于5.x中的 history.go()方法
            navigate(-1)
          }
          
          return (
            
        ) }
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21

      3. useParams()

      1. 作用:回当前匹配路由的params参数,类似于5.x中的match.params

      2. 示例代码:

        import React from 'react';
        import { Routes, Route, useParams } from 'react-router-dom';
        import User from './pages/User.jsx'
        
        function ProfilePage() {
          // 获取URL中携带过来的params参数
          let { id } = useParams();
        }
        
        function App() {
          return (
            
              }/>
            
          );
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16

      4. useSearchParams(search,setSearch)

      1. 作用:用于读取和修改当前位置的 URL 中的查询字符串。

      2. 返回一个包含两个值的数组,内容分别为:当前的seaech参数、更新search的函数。

      3. 示例代码:

        import React from 'react'
        import {useSearchParams} from 'react-router-dom'
        
        export default function Detail() {
        	const [search,setSearch] = useSearchParams()
        	const id = search.get('id')
        	const title = search.get('title')
        	const content = search.get('content')
        	return (
        		
        • 消息编号:{id}
        • 消息标题:{title}
        • 消息内容:{content}
        ) }
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19

      5. useLocation()

      1. 作用:获取当前 location 信息,对标5.x中的路由组件的location属性。可以传递state参数

      2. 示例代码:

        import React from 'react'
        import {useLocation} from 'react-router-dom'
        
        export default function Detail() {
        const {state:{id,title,content}} = useLocation()
        	//const x = useLocation()
        	//console.log('@',x)
          // x就是location对象: 
        	/*
        		{
              hash: "",
              key: "ah9nv6sz",
              pathname: "/login",
              search: "?name=zs&age=18",
              state: {a: 1, b: 2}
            }
        	*/
        	return (
        		
        • 消息编号:{id}
        • 消息标题:{title}
        • 消息内容:{content}
        ) }
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25

      6. useMatch()

      1. 作用:返回当前匹配信息,对标5.x中的路由组件的match属性。

      2. 示例代码:

        }/>
        登录
        
        export default function Login() {
          const match = useMatch('/login/:x/:y')
          console.log(match) //输出match对象
          //match对象内容如下:
          /*
          	{
              params: {x: '1', y: '10'}
              pathname: "/LoGin/1/10"  
              pathnameBase: "/LoGin/1/10"
              pattern: {
              	path: '/login/:x/:y', 
              	caseSensitive: false, 
              	end: false
              }
            }
          */
          return (
          	

        Login

        ) }
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
        • 19
        • 20
        • 21
        • 22
        • 23
        • 24
        • 25

      7. useInRouterContext()

      作用:如果组件在 的上下文中呈现,则 useInRouterContext 钩子返回 true,否则返回 false。

      8. useNavigationType()

      1. 作用:返回当前的导航类型(用户是如何来到当前页面的)。
      2. 返回值:POPPUSHREPLACE
      3. 备注:POP是指在浏览器中直接打开了这个路由组件(刷新页面)。

      9. useOutlet()

      1. 作用:用来呈现当前组件中渲染的嵌套路由。

      2. 示例代码:

        const result = useOutlet()
        console.log(result)
        // 如果嵌套路由没有挂载,则result为null
        // 如果嵌套路由已经挂载,则展示嵌套的路由对象
        
        • 1
        • 2
        • 3
        • 4

      10.useResolvedPath()

      1. 作用:给定一个 URL值,解析其中的:path、search、hash值。
  • 相关阅读:
    reg与wire的用法,证明reg可以在右边,wire型在左边,来作组合逻辑处理。
    Vue.js vs React vs Angular
    聊聊什么是SpringBoot 的自动装配原理
    JS10day(api 阶段性完结,正则表达式简介,过滤敏感词案例,注册模块验证案例)
    亚马逊发布更新Alexa语音助手以应对Bard等大语言模型带来的竞争
    Azure SQL DB/MI以及SQL SERVER中sys.databases视图介绍
    本轮压力测试下,DeFi协议们表现如何?
    如何解决 CentOS 7 官方 yum 仓库无法使用的问题
    JAVA在线电子病历编辑器源码 B/S架构
    《算法图解》阅读笔记
  • 原文地址:https://blog.csdn.net/m0_54853503/article/details/126053342