# React-笔记 **Repository Path**: fxym888/React-Notes ## Basic Information - **Project Name**: React-笔记 - **Description**: React-笔记 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 4 - **Forks**: 1 - **Created**: 2021-10-22 - **Last Updated**: 2025-03-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # React简介 **react是什么?** React用于构建用户界面的JS库。是一个将数据渲染为HTML视图的开源JS库。 **为什么学?** 1.原生JS操作DOM繁琐,效率低 2.使用JS直接操作DOM,浏览器会进行大量的重绘重排 3.原生JS没有组件化编码方案,代码复用低 # React入门 ## React 基础案例 1.先倒入三个包: 【先引入react.development.js,后引入react-dom.development.js】 ```js react.development.js react-dom.development.js babel.min.js ``` 2.创建一个容器 3.创建虚拟DOM,渲染到容器中 ``` html
``` 这样,就会在页面中的这个div容器上添加这个h1. ![渲染结果](./readmeimg/1611196030416.png) ## JSX基础语法 1.定义虚拟DOM,不能使用“” 2.标签中混入JS表达式的时候使用{} 3.样式的类名指定不要使用class,使用className 4.内敛样式要使用双大括号包裹 5.不能有多个根标签,只能有一个跟标签 6.标签必须闭合 7.如果小写字母开头,就将标签转化为html同名元素,如果html中无该标签对应的元素,就报错;如果是大写字母开头,react就去渲染对应的组件,如果没有就报错 实例如下: ```html ``` ## 两种创建虚拟DOM的方式 **1.使用JSX创建虚拟DOM** ```jsx //1.创建虚拟DOM const VDOM = ( /* 此处一定不要写引号,因为不是字符串 */

Hello,React

) //2.渲染虚拟DOM到页面 ReactDOM.render(VDOM, document.getElementById('test')) ``` 这个在上面的案例中已经演示过了 ,下面看看另外一种创建虚拟DOM的方式 **2.使用JS创建虚拟DOM** ```js // 1.创建虚拟DOM[在这使用了js的语法]React.createElement(标签,标签属性,内容) const VDOM = React.createElement('h1',{id:"title"},"nihao") ``` 使用JS和JSX都可以创建虚拟DOM,但是可以看出JS创建虚拟DOM比较繁琐,尤其是标签如果很多的情况下,所以还是比较推荐使用JSX来创建。 # 组件 当应用是以多组件的方式实现,这个应用就是一个组件化的应用 > **注意:** 组件名称必须以大写字母开头。 > > React 会将以小写字母开头的组件视为原生 DOM 标签。例如,< div />` 代表 HTML 的 div 标签,而 `< Weclome /> 则代表一个组件,并且需在作用域内使用 `Welcome` > > 传递的参数,不能在组件中改动 ## 函数式组件 ```js //1.创建函数式组件 function MyComponent(){ console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式 return

我是用函数定义的组件(适用于【简单组件】的定义)

} //2.渲染组件到页面 ReactDOM.render(,document.getElementById('test')) /* 执行了ReactDOM.render(.......之后,发生了什么? 1.React解析组件标签,找到了MyComponent组件。 2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。 */ ``` ![结果](./readmeimg/1611211670211.png) ## Class组件 ```js //1.创建类式组件 class MyComponent extends React.Component { render(){ //render是放在哪里的?—— MyComponent的原型对象上,供实例使用。 //render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。 console.log('render中的this:',this); return

我是用类定义的组件(适用于【复杂组件】的定义)

} } //2.渲染组件到页面 ReactDOM.render(,document.getElementById('test')) ``` 执行过程: ​ 1.React解析组件标签,找到相应的组件 ​ 2.发现组件是类定义的,随后new出来的类的实例,并通过该实例调用到原型上的render方法 ​ 3.将render返回的虚拟DOM转化为真实的DOM,随后呈现在页面中 ## 组件案例 下面,我们通过一个案例更好的理解组件:【只关注与核心代码】 我们发现组件是可以包含中使用的, 而且如果创建的数组,必须要代一个key。数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值 # 组件实例的三大属性 ## state 我们都说React是一个状态机,体现是什么地方呢,就是体现在state上,通过与用户的交互,实现不同的状态,然后去渲染UI,这样就让用户的数据和界面保持一致了。state是组件的私有属性。 在React中,更新组件的state,结果就会重新渲染用户界面(不需要操作DOM),一句话就是说,用户的界面会随着状态的改变而改变。 state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合) **案例**: 1.需求:页面显示【今天天气很炎热】,鼠标点击文字的时候,页面更改为【今天天气很凉爽】 核心代码如下: ```js //1.创建组件 class Weather extends React.Component{ //构造器调用几次? ———— 1次 constructor(props){ console.log('constructor'); super(props) //初始化状态 this.state = {isHot:false,wind:'微风'} //解决changeWeather中this指向问题 // this指向实例,changeWeather在其原型对象上可以找到,通过bind(this)生成一个函数并 this.changeWeather = this.changeWeather.bind(this) } //render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数 render(){ console.log('render'); //读取状态 const {isHot,wind} = this.state return

今天天气很{isHot ? '炎热' : '凉爽'},{wind}

} //changeWeather调用几次? ———— 点几次调几次 changeWeather(){ //changeWeather放在哪里? ———— Weather的原型对象上,供实例使用 //由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用 //类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined //需要在constructor 里修改this指向this.changeWeather = this.changeWeather.bind(this) console.log('changeWeather'); //获取原来的isHot值 const isHot = this.state.isHot //严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。 this.setState({isHot:!isHot}) console.log(this); //严重注意:状态(state)不可直接更改,下面这行就是直接更改!!! //this.state.isHot = !isHot //这是错误的写法 } } //2.渲染组件到页面 ReactDOM.render(,document.getElementById('test')) ``` 需要注意的是: 1.组件的构造函数,必须要传递一个props参数 2.特别关注this【重点】,类中所有的方法局部都开启了严格模式,如果直接进行调用,this就是undefined 3.想要改变state,需要使用setState进行修改,如果只是修改state的部分属性,则不会影响其他的属性,这个只是合并并不是覆盖。 this.setState(),该方法接收两种参数:对象或函数。 1. 对象:即想要修改的state 2. 函数:接收两个函数,第一个函数接受两个参数,第一个是当前state,第二个是当前props,该函数返回一个对象,和直接传递对象参数是一样的,就是要修改的state;第二个函数参数是state改变后触发的回调 ## 在此还需要注意的是,setState有异步更新和同步更新两种形式,那么什么时候会同步更新,什么时候会异步更新呢? **React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval等**。 **大部分开发中用到的都是React封装的事件,比如onChange、onClick、onTouchMove等,这些事件处理程序中的setState都是异步处理的。** ```js //1.创建组件 class St extends React.Component{ //可以直接对其进行赋值 state = {isHot:10}; render(){ //这个This也是实例对象 return

点击事件

} //箭头函数 [自定义方法--->要用赋值语句的形式+箭头函数] dem = () =>{ //修改isHot this.setState({ isHot: this.state.isHot + 1}) console.log(this.state.isHot); } } ``` 上面的案例中预期setState使得isHot变成了11,输出也应该是11。然而在控制台打印的却是10,也就是并没有对其进行更新。这是因为异步的进行了处理,在输出的时候还没有对其进行处理。 ```js componentDidMount(){ document.getElementById("test").addEventListener("click",()=>{ this.setState({isHot: this.state.isHot + 1}); console.log(this.state.isHot); }) } ``` 但是通过这个原生JS的,可以发现,控制台打印的就是11,也就是已经对其进行了处理。也就是进行了同步的更新。 **React怎么调用同步或者异步的呢?** 在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到队列中延时更新,而 isBatchingUpdates 默认是 false,表示 setState 会同步更新 this.state;但是,有一个函数 batchedUpdates,该函数会把 isBatchingUpdates 修改为 true,而当 React 在调用事件处理函数之前就会先调用这个 batchedUpdates将isBatchingUpdates修改为true,这样由 React 控制的事件处理过程 setState 不会同步更新 this.state。 **如果是同步更新,每一个setState对调用一个render,并且如果多次调用setState会以最后调用的为准,前面的将会作废;如果是异步更新,多个setSate会统一调用一次render** ```js dem = () =>{ this.setState({ isHot: 1, cont:444 }) this.setState({ isHot: this.state.isHot + 1 }) this.setState({ isHot: 888, cont:888 }) } ``` 上面的最后会输出:isHot是888,cont是888 ```js dem = () =>{ this.setState({ isHot: this.state.isHot + 1, }) this.setState({ isHot: this.state.isHot + 1, }) this.setState({ isHot: this.state.isHot + 888 }) } ``` 初始isHot为10,最后isHot输出为898,也就是前面两个都没有执行。 **注意!!这是异步更新才有的,如果同步更新,每一次都会调用render,这样每一次更新都会 ** **简化版本:** 1.state的赋值可以不再构造函数中进行 2.使用了箭头函数,将this进行了改变 ```js //1.创建组件 class Weather extends React.Component{ //初始化状态 state = {isHot:false,wind:'微风'} render(){ const {isHot,wind} = this.state return

今天天气很{isHot ? '炎热' : '凉爽'},{wind}

} //自定义方法————要用赋值语句的形式+箭头函数 changeWeather = ()=>{ const isHot = this.state.isHot this.setState({isHot:!isHot}) } } ``` 如果想要在调用方法的时候传递参数,有两个方法: ```js ``` 在这两种情况下,React 的事件对象 `e` 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 `bind` 的方式,事件对象以及更多的参数将会被隐式的进行传递。 ## Props Props主要用来传递数据,比如组件之间进行传值 基本使用: ```js //创建组件 class Person extends React.Component{ render(){ // console.log(this); const {name,age,sex} = this.props return (
  • 姓名:{name}
  • 性别:{sex}
  • 年龄:{age+1}
) } } //渲染组件到页面 ReactDOM.render(,document.getElementById('test1')) ``` 如果传递的数据是一个对象,可以更加简便的使用 ```js const p = {name:"张三",age:"18",sex:"女"} ReactDOM.render(,document.getElementById("test1")); ``` **对于props限制** cnpm i -S prop-types 很多时候都想要传递的参数进行相应的限制,比如:限制传递参数的类型,参数的默认值等等 react对此提供了相应的解决方法: - propTypes:类型检查,还可以限制不能为空 - defaultProps:默认值 ```js //对标签属性进行类型、必要性的限制 static propTypes = { name:PropTypes.string.isRequired, //限制name必传,且为字符串 sex:PropTypes.string,//限制sex为字符串 age:PropTypes.number,//限制age为数值 } //指定默认标签属性值 static defaultProps = { sex:'男',//sex默认值为男 age:18 //age默认值为18 } ``` **函数式组件的使用**: 函数在使用props的时候,是作为参数进行使用的(props); ```js function Person (props){ const {name,age,sex} = props return (
  • 姓名:{name}
  • 性别:{sex}
  • 年龄:{age}
) } ``` ## Refs Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。 ### Refs主要提供了三种方式: **1.字符串形式** 在想要获取到一个DOM节点,可以直接在这个节点上添加ref属性。利用该属性进行获取该节点的值。 案例:给需要的节点添加ref属性,此时该实例对象的refs上就会有这个值。就可以利用实例对象的refs获取已经添加节点的值 ```js {/*字符串式ref,最新版react已经不推荐了*/}     showData = ()=>{ const {input1} = this.refs alert(input1.value) } //展示右侧输入框的数据 showData2 = ()=>{ const {input2} = this.refs alert(input2.value) } ``` **2.回调形式** 回调形式会在ref属性中添加一个回调函数。将该DOM作为参数传递过去。 如:ref里面就是一个回调函数,currentNode就是该input标签。然后在将该DOM元素赋值给实例对象中的一个属性 ![input标签](./readmeimg/1611495051999.png) 也可以将函数提取出来,在ref中进行调用 ```js showData = ()=>{ const {input1} = this alert(input1.value) } {this.input1 = currentNode} } type="text" placeholder="点击按钮提示数据"/>    ``` # 组件的生命周期 ## 效果 需求:定义组件实现以下功能: * 1. 让指定的文本做显示 / 隐藏的渐变动画 * 2. 从完全可见,到彻底消失,耗时2S * 3. 点击“不活了”按钮从界面中卸载组件 ![输入图片说明](readmeimg/component.gif "QQ截图20201229183512.png") ## 理解 * 1.组件从创建到死亡它会经历一些特定的阶段。 * 2.React组件中包含一系列勾子函数(生命周期回调函数), 会在特定的时刻调用。 * 3.我们在定义组件时,会在特定的生命周期回调函数中,做特定的工作。 ## 生命周期流程图(旧) ![输入图片说明](readmeimg/图片9.png "QQ截图20201229183512.png") * 生命周期的三个阶段(旧) * 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() ## 生命周期流程图(新) ![输入图片说明](readmeimg/图片10.png "QQ截图20201229183512.png") * 生命周期的三个阶段(新) * 1. 初始化阶段: 由ReactDOM.render()触发---初次渲染 * 1.constructor() * 2.getDerivedStateFromProps * 3.render() * 4.componentDidMount() * 2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发 * 1.getDerivedStateFromProps * 2.shouldComponentUpdate() * 3.render() * 4.getSnapshotBeforeUpdate * 5.componentDidUpdate() * 3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发 * 1.componentWillUnmount() ## 重要的勾子 1.render:初始化渲染或更新渲染调用 2.componentDidMount:开启监听, 发送ajax请求 3.componentWillUnmount:做一些收尾工作, 如: 清理定时器 ## 即将废弃的勾子 1.componentWillMount 2.componentWillReceiveProps 3.componentWillUpdate 现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。 # 虚拟DOM与DOM Diffing算法 ## 效果 需求:验证虚拟DOM Diffing算法的存在 ![输入图片说明](readmeimg/虚拟DOM.gif "QQ截图20201229183512.png") ## 基本原理图 ![输入图片说明](readmeimg/图片11.png "QQ截图20201229183512.png") ## 一、todoList案例相关知识点 #### 1.拆分组件、实现静态组件,注意:className、style的写法 #### 2.动态初始化列表,如何确定将数据放在哪个组件的state中? ——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升) #### 3.关于父子之间通信: - 1.【父组件】给【子组件】传递数据:通过props传递 - 2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数 - 4.注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value - 5.状态在哪里,操作状态的方法就在哪里 ## 二、github搜索案例相关知识点 #### 1.设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办。 #### 2.ES6小知识点:解构赋值+重命名 ```js let obj = {a:{b:1}} const {a} = obj; //传统解构赋值 const {a:{b}} = obj; //连续解构赋值 const {a:{b:value}} = obj; //连续解构赋值+重命名 ``` #### 3.消息订阅与发布机制 + 1.先订阅,再发布(理解:有一种隔空对话的感觉) + 2.适用于任意组件间通信 + 3.要在组件的componentWillUnmount中取消订阅 + 4.fetch发送请求(关注分离的设计思想) ```js try { const response= await fetch(`/api1/search/users2?q=${keyWord}`) const data = await response.json() console.log(data); } catch (error) { console.log('请求出错',error); } ```