目录
第一个react实例
react安装
对react机制最直观的理解
如果你第一次用npm
关于初始化package.json的问题
使用 create-react-app 快速构建 React 开发环境
项目目录结构
修改一下代码执行源头APP.js
React元素渲染
将元素渲染到DOM中
更新元素渲染
关于vue的更新视图和react更新元素渲染
函数写法
类的写法
关于注意点的解答
关于react算法和vue2与vue3算法比较
React JSX
react实例
注意
使用
独立文件(外连接使用)
关于此时此刻对react和vue的思考
react中封装vue模板语法(参考)
样式
注释
数组
封装组件
函数定义组件(用的多)
关于用ES6 class定义组件(可能因为繁琐,用的少)
注意*
props“内部”传值
属性关键字注意
props父子组件传值
关于细节注意Name name
React State(状态)
前言
React 实例
将生命周期方法添加到类中
生命周期钩子解释
数据自顶向下流动
实例
解析
和dom冒泡操作的关系
和瀑布流的关系
React Props
state 和 props
使用 Props
默认 Props
props实例
Props 验证*
React 16.4 实例
React 15.4 实例
更多验证器内容
vue中处理props
react事件处理(*待深入理解)
关于vue的绑定和react绑定
开始
站在生命周期(钩子函数)有关的角度
vue的事件处理
react条件渲染
元素变量
React 实例
与运算符 &
&
注意
三目运算符
阻止组件渲染*
React 列表 & Keys
重构组件*
Keys*
React API
react组件生命周期
挂载
更新
卸载
react ajax*(实战类待补充)
React 表单与事件(实战重点)
一个简单的实例
实例
实例2*
Select 下拉菜单
多个表单
React Refs(谨慎使用)
使用方法
完整实例
React 实例
vue中ref
第一个react实例
首先是常规的src引入资源链接
<div id="example"></div> <script type="text/babel"> ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') ); </script>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Hello React!</title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') ); </script> </body> </html>
或者react脚手架下;ES6引入
import React from "react"; import ReactDOM from "react-dom"; **function** Hello(props) { **return** <h1>Hello World!</h1>; } ReactDOM.render(<Hello />, document.getElementById("root"));
到这里react给我的印象有点像后端传递给网页标签(数据存放在后端;其实我会想这样做是不是对网站安全,某种程度上;然而确实提供了方便)
react安装
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <!-- 生产环境中不建议使用 --> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script
对于cdn使用有几点要了解:
这三个要按顺序使用;development开发版本
其中react和react-dom是必须同时引入的,react负责组件创建,react-dom负责DOM渲染。
-
react和react-dom是必须同时引入的,react负责组件创建,react-dom负责DOM渲染。
-
development和production版本的区别:- development版本:包含完整的调试信息,大小较大,用于开发环境。
- production版本:移除了调试信息,文件大小较小,用于生产环境。所以在生产环境中应使用production版本,开发环境可以使用development版本。
-
babel-standalone是可选的,它包含了Babel编译器,可以在浏览器中编译JSX和ES6代码。如果你的代码中使用了JSX或ES6语法,则需要引入babel-standalone。如果代码已经提前编译为普通JavaScript,则可以不引入。
-
以上三个库需要按照顺序引入,babel-standalone在最前,react和react-dom之后。
-
CDN只建议在开发环境使用,生产环境中仍然推荐下载并配置npm版本的React。因为CDN的版本更新不一定及时,也无法自定义配置。
对react机制最直观的理解
如果你第一次用npm
关于初始化package.json的问题
以往都是要先初始化项目出package.json的,今天发现新创建一个项目的时候会自动初始化,好好好😎
使用 create-react-app 快速构建 React 开发环境
跑起来了捏,还自带文档引导,还挺友好🤨
项目目录结构
修改一下代码执行源头APP.js
好像code里的代码默认置后
没看到这里react引用的意义何在,或许是必须的🤔
解答
在这个React代码中,import React, { Component } from 'react';这一行是必不可少的。
原因有:
-
React是React项目的核心库,没有它jsx语法和组件等都无法使用。
-
Component是React的基础组件类,我们的App组件继承自它。如果没有导入Component,无法定义组件类。
-
jsx语法需要React来编译,没有React无法编译jsx,会报错。
修改下入口文件
React元素渲染
我的理解是原生JavaScript在获取dom对象时会返回全部的对象,这对性能是消耗极大的,如果不加以封装,而框架对性能优化的体现就在这里(其中之一)
将元素渲染到DOM中
主要是解释了ReactDOM.render() 的方法的作用和原理;接收react元素
它展示了React的两大核心概念:
-
使用JSX创建React元素(表示UI)
-
使用ReactDOM.render()将元素渲染到DOM中(展示UI)这也是很多React入门教程和示例的开篇代码。它清晰简明地展示了React的基本工作流程和理念。
更新元素渲染
这里主要讲了react如何实现的“实时更新”,相比vue,react更像是Java开发人员写的(好像是事实哦,因为我印象里定时器和java关系不浅)
通过定时器每时每刻重新渲染局部视图模板(接收函数)
关于vue的更新视图和react更新元素渲染
React和Vue在更新元素渲染方面有以下几点不同:1. React采用虚拟DOM,通过比对新旧虚拟DOM树来更新真实DOM。Vue也使用虚拟DOM,但它是一个观察者,能更精确地更新真实DOM。2. React只有state发生改变时才会更新视图。Vue使用数据绑定,数据发生变化就会更新视图。3. React需要手动调用setState()来更新state和触发重新渲染。Vue的视图是响应式的,数据变化会自动更新。4. React渲染过程为递归比较新旧DOM树。Vue使用依赖跟踪来精确更新视图,较少不必要的DOM操作。5. React的JSX语法需要使用this.state获取状态,要写在render()方法内。Vue的模板可以在data外直接使用data中的数据。更具体来说:在React中:- 调用setState()更新state,触发重新渲染 - state变化会重新执行render(),返回新的JSX元素树 - 新旧JSX元素树会递归比较,更新真实DOM - 只有state发生变化时才会重新渲染在Vue中:- 数据(data)变化会触发依赖更新,精确更新视图 - 响应式数据可以在模板(template)内直接使用 - 数据绑定会监听数据变化,并立即更新视图 - 任何绑定了数据的元素都会更新所以总结来说,React的更新渲染较Vue更精确地依赖state的变化,需要手动触发。Vue的数据绑定使其对数据变化更敏感,可以自动更新视图。但React使用虚拟DOM可以在更新时有更高的性能。两者在理念和使用上有差异,但目的都是高效地渲染UI,开发者可以根据喜好选择使用。掌握了两者的差异,也可以很好地结合使用。
函数写法
将上面要展示的内容封装为函数
解析:
这段代码定义了一个Clock组件和一个tick函数,作用是每秒更新一次Clock组件。具体来说:Clock组件:
jsx function Clock(props) { return ( <div> <h1>Hello, world!</h1> <h2>现在是 {props.date.toLocaleTimeString()}.</h2> </div> ); }
这是一个函数组件,接受props入参,返回一个JSX元素。它显示一个h1标题和当前时间的h2元素,当前时间来自props.date。tick函数:
jsx function tick() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById('example') ); }
tick函数每秒调用一次,它使用ReactDOM.render()将Clock组件渲染到DOM中,并传入新的Date对象作为date prop。setInterval(tick, 1000);这行代码使用setInterval()每秒调用一次tick函数。所以总的效果是:每秒渲染一次Clock组件,并显示更新后的当前时间。它通过不断调用ReactDOM.render()来更新组件,实现了类似计时器的效果。
类的写法
需要注意
class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>现在是 {this.props.date.toLocaleTimeString()}.</h2> </div> ); } } function tick() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById('example') ); } setInterval(tick, 1000);
关于注意点的解答
在React的类组件中,props是通过this.props访问的。this代表当前的组件实例。从源码角度分析,当React遇到类组件时,它会通过调用该类的构造函数来初始化组件的实例。在构造函数中,会将传入的props属性赋值给this.props。所以在类组件内部,我们通过this.props访问props。例如上面的代码中,Clock组件的构造函数大致如下:
js constructor(props) { super(props); this.props = props; // 将props赋值给this.props }
然后在render方法中,通过this.props.date来访问date这个prop。如果直接使用props,那么render方法中的this.props.date将找不到props,因为在方法作用域内,没有props这个变量,只有通过this.props可以访问 constructor 中赋值的props。所以在React类组件中,要记住一定通过this.props来读取props,而不是直接props。this代表当前组件实例,通过它可以访问到在构造函数中初始化的属性。总结:1. 类组件中的props通过this.props访问
-
this代表当前组件实例
-
构造函数中会将props赋值给this.props,所以类组件内部通过this.props读取props
关于react算法和vue2与vue3算法比较
React使用diff算法来更新必要的DOM部分。diff算法会比较元素前后两次渲染的不同,并仅更新改变的地方。React的diff算法包含两部分:1. tree diff:比较两棵树(两次渲染的React元素树)的差异,找出需要删除、添加和更新的节点。2. component diff:对相同类型的两个组件进行细致的比较,找出需要更新的props和state。tree diff使用keys来判断两个节点是否相同,如果没有keys,React会使用元素类型和位置来判断相同性,这会导致很多没有必要的DOM操作。而component diff会对相同类型的组件进行props和state的对比,只更新必要变化的地方,这可以提高更新性能。和Vue相比:- Vue 2使用基于路径的虚拟DOM diff算法。它具有O(n^3)的时间复杂度,但在一定程度上避免了不必要的计算,所以在大多数情况下性能还不错。- Vue 3使用基于slot的diff算法,复杂度为O(n)。它采用跨层级比较的方式找出真正需要更新的地方,大大提高了diff的性能。- React的diff算法在时间复杂度和更新精确性上介于Vue 2和Vue 3之间。它采用跨层级比较的方式实现tree diff,但component diff的比较不是那么精确,会导致一定程度的不必要重渲染,所以性能不及Vue 3。总体来说,三者的diff算法都实现了较好的更新性能,但Vue 3的slot diff算法在这个方面是最优的。不过在大多数应用场景下,React和Vue的diff算法都可以满足需求,性能上的差异也不会很大。
.
React JSX
JSX更像是javaweb中的jsp
同样是用来替代常规JavaScript
const element = <h1>Hello, world!</h1>;
react实例
这里我们得知在jsx语法中,可以class命名大致和JavaScript的class命名规则一致
一般JavaScript命名中
var myDivElement = <div className="foo" />; ReactDOM.render(myDivElement, document.getElementById('example'));
注意
使用
ReactDOM.render+h5标签+获取dom节点并将H5标签渲染到上面
ReactDOM.render( <h1>Hello, world!</h1>, document.getElementById('example') );
貌似颠倒下顺序会更符合人的阅读,但一般不这样🤨
独立文件(外连接使用)
建立一个常规命名.js文件,之后jsx代码放在里面,外连接引入编写代码;
使用JavaScript表达式
不能使用if else;但可以三元运算代替
关于此时此刻对react和vue的思考
React的JSX语法在可读性和便利性上不如Vue的模板语法。例如上面那段代码,如果用Vue的模板语法写会是:
html <div> <h1 v-if="i == 1">True!</h1> <h1 v-else>False</h1> </div>
很明显Vue的模板语法更加清晰和易读。这是因为:1. Vue的模板语法更接近HTML,而JSX仍然是JavaScript,需要理解JavaScript的语法和规则。2. Vue的模板语法有指令(如v-if、v-for等)和内置功能(如插槽、动态组件等)可以直接使用,而React需要通过JavaScript实现。3. Vue的模板编译过程会做静态分析、优化和错误检查,而JSX最终还是会被编译为React.createElement()调用,需要开发者自己确保正确使用。4. Vue的模板语法通过扩展可以实现高级功能(如渲染函数),React的JSX始终只是createElement()调用的语法糖。所以,总体来说,Vue的模板语法在开发体验上会更加友好,特别是对于初学者来说,不需要过多理解底层实现,可以更快上手。但这并不意味着React差于Vue。React的JSX也有其优点:1. 更加灵活,可以根据需要组合各种UI元素2. 编码规则和标准JavaScript一致,易于理解3. 有利于将UI和逻辑保持在一起,符合React的设计思想4. 编译后仍然是JavaScript,可以利用现有的工具和规则进行检查、优化和打包等所以,二者都有各自的优缺点。但对于初学者而言,Vue的模板语法会更加友好易上手一些。随着对React理解的深入,JSX的优点也会更加明显。
react中封装vue模板语法(参考)
React本身并没有和Vue模板语法类似的指令或操作码。但我们可以通过自定义Hooks来模拟Vue模板的某些功能。例如,要模拟Vue的v-if指令,可以写一个useTogglehook:
js function useToggle(initialValue = false) { const [state, setState] = useState(initialValue); const toggle = () => setState(!state); return [state, toggle]; }
然后在JSX中这样使用:
jsx const [show, toggle] = useToggle(true); return ( <div> {show && <ChildComponent />} <button onClick={toggle}>Toggle</button> </div> )
这就实现了类似v-if的功能。我们也可以写一个useModel来实现v-model:
js function useModel(initialValue) { const [value, setValue] = useState(initialValue); const onChange = (e) => setValue(e.target.value); return [value, onChange]; }
使用:
jsx const [text, onChange] = useModel('hello'); <input value={text} onChange={onChange} />
这样就可以实现数据双向绑定的效果。类似地,我们可以写自定义Hooks来模拟Vue模板的slot、动态组件、过滤器等功能。React的强大之处在于,它只提供了非常基础的功能,但通过自定义Hooks可以轻易扩展出许多便捷的 abstraction。相比之下,Vue的模板语法自身已经内置了大量便捷的功能,不太鼓励开发者去频繁自定义。这两种方式各有优势,取决于你更偏向于开箱即用还是喜欢自行搭建系统。总之,通过自定义Hooks,React也可以模拟实现Vue模板很多常用的功能。但由于JSX的限制,代码的可读性和语义化还是不及Vue的模板语法。不过,对于React开发者来说,写自定义Hooks来扩展React的本质功能,也是一种提高技能的好方式。
样式
react推荐内联样式
之前就react的这种做法做过分析,在少量的样式下并没什么问题,大概就是没有前端规范的写法;因为实际开发中都是运行即可,前端并不会发挥css这种特殊技能,因此规范这方面只能被忽略了,不然无论是react还是vue框架在规范上都不达标,不合格,但是性能为王,碾压一切...
react会自动在数字后默认添加px,下面实例演示了为h1添加样式
var myStyle = { fontSize: 100, color: '#FF0000' }; ReactDOM.render( <h1 style = {myStyle}>菜鸟教程</h1>, document.getElementById('example') );
注释
注释需要写在花括号中,这点
ReactDOM.render( <div> <h1>菜鸟教程</h1> {/*注释...*/} </div>, document.getElementById('example') );
数组
JSX 允许在模板中插入数组,数组会自动展开所有成员:
var arr = [ <h1>菜鸟教程</h1>, <h2>学的不仅是技术,更是梦想!</h2>, ]; ReactDOM.render( <div>{arr}</div>, document.getElementById('example') );
封装组件
接下来我们封装一个输出 "Hello World!" 的组件,组件名为 HelloMessage:
function HelloMessage(props) { return <h1>Hello World!</h1>; } const element = <HelloMessage />; ReactDOM.render( element, document.getElementById('example') );
函数定义组件(用的多)
我们可以使用函数定义了一个组件:
function HelloMessage(props) { return <h1>Hello World!</h1>; }
关于用ES6 class定义组件(可能因为繁琐,用的少)
es6还是用的少,没必要添加麻烦
关于es6 class定义组件
在React中,可以使用ES6的class语法来定义组件。例如:
jsx class Hello extends React.Component { render() { return <h1>Hello {this.props.name}</h1>; } }
这定义了一个名为Hello的组件类,它扩展了React.Component基类。class组件有以下特点:1. 必须有render()方法,它会返回要渲染的React元素2. 有自己的生命周期方法,可以在组件初始化、更新或销毁时执行某些操作3. 有state和props,它们都可以在class组件中通过this访问4. 可以使用构造函数来初始化state例如:
jsx class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>现在是 {this.state.date.toLocaleTimeString()}.</h2> </div> ); } }
这里Clock组件在构造函数中初始化了state,并在render()方法中通过this.state.date读取。除了生命周期方法和state之外,class组件的其他功能与函数组件相同。所以在某些情况下,函数组件的概念会更简单。总结,关于ES6 class定义的React组件,你需要知道:1. 需要扩展React.Component基类2. 必须有render()方法来返回UI元素3. 构造函数可用于初始化state4. 拥有生命周期方法,如componentDidMount()等5. 通过this可以访问state和props6. 除了状态管理和生命周期之外,其他功能与函数组件相同
注意*
const element = <HelloMessage /> 为用户自定义的组件。
注意,原生 HTML 元素名以小写字母开头,而自定义的 React 类名以大写字母开头,比如 HelloMessage 不能写成 helloMessage。除此之外还需要注意组件类只能包含一个顶层标签,否则也会报错。
function HelloMessage(props) { return <h1>Hello {props.name}!</h1>; } const element = <HelloMessage name="Runoob"/>; ReactDOM.render( element, document.getElementById('example') );
props“内部”传值
在这个示例中,name属性的值"Runoob"被作为props传递给了HelloMessage组件。在HelloMessage组件中,可以通过props.name来访问这个属性。所以,这里的props相当于一个包含所有传入属性的对象。props.name就表示从这个对象中读取name属性的值。这说明:1. 组件的属性通过props对象以键值对的形式传入2. 组件可以通过props访问这些属性的值3. attribute变为对应props对象的一个属性,其值由attribute的值指定4. 因此,props允许父组件有意义地自定义子组件例如,我们可能有:
jsx <HelloMessage name="John" age={30} />
那么,在HelloMessage组件中可以通过props访问:- props.name = "John" - props.age = 30所以,props允许父组件向子组件传递信息和数据,这是React组件之间通信的一种方式。子组件通过props得到父组件传递过来的信息之后可以根据这些信息渲染不同的内容。在这个示例中,我们把name属性的值"Runoob"作为props传递给了HelloMessage组件,所以在组件内部可以通过props.name访问到这个值,并将其渲染出来。总结:1. 组件通过props得到外部传递过来的信息
-
props中的每个属性都由组件的属性指定
-
组件可以根据props的值来决定渲染什么内容
-
这是React组件之间通信的一种方式 - 从父到子
属性关键字注意
注意,在添加属性时, class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。
props父子组件传值
在React中,父组件可以通过props向子组件传递数据或回调方法。这是父子组件之间通信的一种方式。例如,我们有这样的父组件:
jsx const Parent = () => { const handleClick = () => { console.log('Clicked!'); } return ( <Child onClick={handleClick} name="John" age={30} /> ) }
我们向子组件Child传递了:- onClick回调方法 - name和age这两个props在子组件中,可以这样接收:
jsx const Child = (props) => { const { onClick, name, age } = props; return ( <div> <p>Name: {name} Age: {age}</p> <button onClick={onClick}>Click Me</button> </div> ) }
我们从props对象中解构出onClick、name和age,这些就是父组件传递过来的信息。- onClick可以被绑定到子组件的元素上,当点击按钮时会调用父组件中的方法 - name和age可以在子组件的渲染结果中使用所以,通过props,React组件可以:- 将数据从父组件传递到子组件 - 将事件处理方法从父组件传递到子组件 - 自定义组件的外观和功能对比下不使用props的情况:
jsx const Parent = () => { const handleClick = () => { console.log('Clicked!'); } return <Child /> } const Child = () => { return ( <div> <p>Name: John Age: 30</p> <button onClick={handleClick}>Click Me</button> // Error! handleClick is undefined </div> ) }
在这种情况下,子组件没有办法访问父组件中的handleClick方法,所以onClick绑定会报错。通过props,我们建立了父子组件之间的通信渠道,使子组件得以访问父组件传递过来的各种信息。总结:1. 父组件可以通过props向子组件传递数据和回调方法
-
子组件通过props接收父组件传递过来的信息
-
子组件可以根据props的值来渲染内容或绑定事件
-
这是React组件之间通信的一种方式 - 从父到子
关于细节注意Name name
jsx <Name name="菜鸟教程" />
里面,Name开头要使用大写字母,而name属性要使用小写字母。这是因为在JSX(React的语法)中,大写开头的元素代表React组件。而小写开头的元素代表HTML内置元素。所以,在这个例子中:- Name是一个自定义的React组件 - name是Name组件的一个props属性如果写成:
jsx <name name="菜鸟教程" />
那么name会被识别为一个HTML元素,而不是React组件,导致错误。而如果写成:
jsx <NAME NAME="菜鸟教程" />
那么NAME和NAME都会被识别为React组件,也会导致错误,因为React组件名必须以大写字母开头。所以,总结来说,在JSX中:- 大写字母开头代表React组件 - 小写字母开头代表HTML内置元素 - React组件的props属性要使用小写字母,遵循camelCase命名这个规则是JSX的语法规定,目的同样是为了提高可读性,让程序员可以清晰地分辨组件、元素和属性。
###
React State(状态)
前言
大体可能想说react是如何在性能上好的;
原本比如下面这个点击自增的按钮例子;
通常原生js会重新渲染整个dom树;
但如果我们劫持了dom,并用算法优化出需要改变的dom部分,只渲染改变的地方,那么性能会好很多
框架起的是这个作用
react 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
以下实例创建一个名称扩展为 React.Component 的 ES6 类,在 render() 方法中使用 this.state 来修改当前的时间。
添加一个类构造函数来初始化状态 this.state,类组件应始终使用 props 调用基础构造函数。
React 实例
class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>现在是 {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('example') );
将生命周期方法添加到类中
举一个在登录页面开发中使用生命周期钩子的例子:我们有一个登录页面,需要在注册成功或者登录成功后跳转到主页面。并且在离开登录/注册页面前需要清除定时器,否则定时器会继续运行导致内存泄漏。那么我们可以这样编写代码:
jsx class Login extends React.Component { componentDidMount() { // 3秒后自动跳转到主页 this.timer = setTimeout(() => { this.props.history.push('/home'); }, 3000); } componentWillUnmount() { // 清除定时器 clearTimeout(this.timer); } // 登录/注册成功,3秒后跳转主页 onSuccess = () => { this.timer = setTimeout(() => { this.props.history.push('/home'); }, 3000); } // 跳转主页,同时清除定时器 goHome = () => { clearTimeout(this.timer); this.props.history.push('/home'); } render() { // ... } }
在这个示例中,我们在:- componentDidMount 中设置了一个3秒钟跳转到主页的定时器 - componentWillUnmount 中清除了这个定时器,防止内存泄漏 - onSuccess 方法中,登录/注册成功后也设置了一个3秒后跳转主页的定时器 - goHome 方法中,跳转主页的同时清除定时器所以,通过生命周期钩子与定时器的配合,我们完成了:1. 登录/注册页面3秒后自动跳转主页
-
登录/注册成功后3秒跳转主页
-
无论是手动跳转主页还是离开登录页,定时器都会被清除,避免内存泄漏可以看出,如果没有利用生命周期钩子来与定时器配合,很难完成这样的功能而又不产生内存泄漏的问题。所以,在开发React应用时,特别是包含定时器或异步操作的功能时,充分利用生命周期钩子,可以带来很多好处:1. 避免内存泄漏,防止定时器/异步操作随着组件卸载仍然在运行
-
让定时器与组件的 state/props 保持同步
-
增加组件的可复用性和稳定性
生命周期钩子解释
componentDidMount() 与 componentWillUnmount() 方法被称作生命周期钩子。
在组件输出到 DOM 后会执行 componentDidMount() 钩子,我们就可以在这个钩子上设置一个定时器。
this.timerID 为定时器的 ID,我们可以在 componentWillUnmount() 钩子中卸载定时器。
代码执行顺序:
-
当
<Clock />
被传递给ReactDOM.render()
时,React 调用Clock
组件的构造函数。 由于Clock
需要显示当前时间,所以使用包含当前时间的对象来初始化this.state
。 我们稍后会更新此状态。 -
React 然后调用
Clock
组件的render()
方法。这是 React 了解屏幕上应该显示什么内容,然后 React 更新 DOM 以匹配Clock
的渲染输出。 -
当
Clock
的输出插入到 DOM 中时,React 调用componentDidMount()
生命周期钩子。 在其中,Clock
组件要求浏览器设置一个定时器,每秒钟调用一次tick()
。 -
浏览器每秒钟调用
tick()
方法。 在其中,Clock
组件通过使用包含当前时间的对象调用setState()
来调度UI更新。 通过调用setState()
,React 知道状态已经改变,并再次调用render()
方法来确定屏幕上应当显示什么。 这一次,render()
方法中的this.state.date
将不同,所以渲染输出将包含更新的时间,并相应地更新 DOM。 -
一旦
Clock
组件被从 DOM 中移除,React 会调用componentWillUnmount()
这个钩子函数,定时器也就会被清除。
数据自顶向下流动
实例
function FormattedDate(props) { return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>; } class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } componentDidMount() { this.timerID = setInterval( () => this.tick(), 1000 ); } componentWillUnmount() { clearInterval(this.timerID); } tick() { this.setState({ date: new Date() }); } render() { return ( <div> <h1>Hello, world!</h1> <FormattedDate date={this.state.date} /> </div> ); } } function App() { return ( <div> <Clock /> <Clock /> <Clock /> </div> ); } ReactDOM.render(<App />, document.getElementById('example'));
解析
这段代码中定义了两个组件:1. FormattedDate 组件:- 它接收date作为props - 使用date.toLocaleTimeString()将date格式化为本地时间字符串 - 并渲染到<h2>元素中2. Clock 组件:- 在构造器中初始化state,设置date为当前时间 - 在componentDidMount生命周期钩子中设置定时器,每秒调用tick方法 - 在tick方法中调用setState更新date为当前时间 - 在render方法中渲染<FormattedDate>组件,并将state.date作为props传入 - 在componentWillUnmount中清除定时器,避免内存泄漏3. App组件:- 渲染三个<Clock>组件4. 调用ReactDOM.render将<App>组件渲染到DOM这个代码实现了一个简单的钟表应用,有三个钟同时运行,每秒更新一次时间并重新渲染。这个例子展示了React几个方面的内容:1. 组件生命周期:使用componentDidMount和componentWillUnmount来设置和清除定时器2. 通过this.setState更新组件状态,触发重新渲染3. 父子组件之间通过props传递数据,<Clock>将时间数据传给<FormattedDate>4. 复用同一个组件,这里渲染了三个相同的<Clock>组件5. 利用定时器与生命周期钩子,实现动态渲染效果所以,总的来说,这是一个很好的React小demo,涉及到组件的各个概念与技术点。如果理解这个例子,对学习React有很大帮助。
和dom冒泡操作的关系
这个东西和dom冒泡操作能做对比
自顶向下的数据流和事件冒泡虽然属于React中的两种不同机制,但它们的工作方向是相反的。- 自顶向下的数据流:从父组件流向子组件 - 事件冒泡:从子组件上传向父组件所以,从方向和层级的角度来看,这两种机制是相反的:- 数据流动是自上而下的 - 事件流动是自下而上的但就功能而言,这两种机制在React中协同工作,相互配合:- 父组件通过props传入事件处理函数和state给子组件 - 子组件触发事件,事件沿DOM树冒泡到父组件的事件处理函数 - 父组件的事件处理函数可以通过setState()来更新state,进而更新子组件的props - 实现了父子组件之间的交互与通信所以,更加准确的理解应该是:自顶向下的数据流和事件冒泡在React中属于两种不同的机制,方向相反,但功能上相互配合,共同支撑着组件与组件之间的交互与通信。
和瀑布流的关系
自顶向下的数据流与瀑布流(cascade)没有直接的关系。它们属于两个不同的概念。自顶向下的数据流是React应用中组件之间传递数据的方向。它指:1. 父组件可以通过props将state和方法传递给子组件
-
子组件不能直接修改父组件的state
-
子组件要通过父组件的方法来间接修改父组件的state
-
数据的流动方向是自上而下的,从父组件流向子组件而瀑布流通常指:1. 一个元素中的样式设置会“流动”或“覆盖”到子元素
-
子元素可以继承和覆盖父元素的样式
-
这种样式的传递会层层递进,就像瀑布一般由上至下所以,从定义来看:- 自顶向下的数据流描述的是React组件树中state和props的传递方向
- 瀑布流描述的是CSS中层叠样式表的样式继承机制它们涉及的层面不同:- 自顶向下的数据流是React的机制,关注组件树和state - 瀑布流是CSS的机制,关注页面样式和继承但是,在React应用的开发中,这两种机制有时也会一起使用,如:1. 父组件设置了样式,这些样式会往下“流动”到子组件
-
父组件通过props将样式设置函数传给子组件
-
子组件调用这个函数来覆盖或修改父组件设置的样式
-
实现了父子组件之间样式的交互和通信所以,总结来说:自顶向下的数据流和瀑布流是两个不同的概念,分属于React和CSS,但在React开发中,它们往往通过props传递等方式协同工作,相互配合。
React Props
state 和 props
在React中,state和props是两个非常重要的概念。理解state和props的区别对学习React有很大帮助。state 和 props 的主要区别有:1. 定义位置不同:- state 定义在组件内部,由该组件维护和控制。 - props 由组件外部传入,由使用该组件的父组件维护和控制。2. 变化方式不同:- state 的值可以在组件内部声明,并通过this.setState()进行更新。 - props 值不能由组件自身进行更新,只能由外部组件更新其值。3. 传递对象不同:- state 传递给组件渲染结果 - props 传递给组件本身4. 可变性不同:- state 的值可以在组件内部变化 - props 的值不可在组件内部变化总结来说:- state是组件内部可变的状态数据,由该组件控制和修改。 - props是从组件外部传入但组件内部不可变的属性数据。举个例子,我们有一个计数器组件:
jsx class Counter extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } }
在这个组件中:- this.state.count 是组件内部可变的状态数据 - 点击按钮时,调用this.setState()更新state,重新渲染UI现在,如果这个组件的初始count值是通过props传入的,代码如下:
jsx <Counter count={10} /> class Counter extends React.Component { render() { return ( <div> <p>You clicked {this.props.count} times</p> <button onClick={() => this.setState({ count: this.props.count + 1 })}> Click me </button> </div> ); } }
在这个例子中,this.props.count 就是从组件外部传入的不可变的props数据。 如果在组件内部直接修改this.props.count,则会报错。所以,props和state虽然都可以决定组件的输出,但它们的用途和特点不同。理解state和props的区别,对学习React和构建组件有很大帮助。
使用 Props
function HelloMessage(props) { return <h1>Hello {props.name}!</h1>; } const element = <HelloMessage name="Runoob"/>; ReactDOM.render( element, document.getElementById('example') );
默认 Props
你可以通过组件类的 defaultProps 属性为 props 设置默认值,
props实例
class HelloMessage extends React.Component { render() { return ( <h1>Hello, {this.props.name}</h1> ); } } HelloMessage.defaultProps = { name: 'Runoob' }; const element = <HelloMessage/>; ReactDOM.render( element, document.getElementById('example') );
Props 验证*
暂时不理解用处,
初步理解场景比如:密码框格式,可能会和正则匹配连用
在react16.4和15.4有重大区别
从React 16.4开始,Props验证有一些重要改动。主要的不同在于:16.4之前- 使用propTypes
属性来定义 props 验证 - 运行时(runtime)只在开发环境有效 - 发版时需要手动删除propTypes
jsx Foo.propTypes = { bar: PropTypes.number.isRequired }
16.4及以后- 使用PropTypes
作为 import 引入 - 运行时(runtime)在生产环境和开发环境都有效 - 无需删除,直接发布应用即可使用
jsx import PropTypes from 'prop-types'; Foo.propTypes = { bar: PropTypes.number.isRequired }
此外,16.4后propTypes
被重命名为PropTypes
,首字母大写。所以,总的来说16.4对props验证主要有以下改动:1. 使用import引入PropTypes
,代替直接使用propTypes
-
运行时效果由仅开发环境扩展到生产环境
-
无需手动删除props验证代码就可以发布生产版本
-
propTypes
属性名改为PropTypes
,首字母大写React团队通过这些改动来强调:- props验证应该在开发和生产环境中同时存在
- 开发者们不应该为了性能而删除props验证这有助于开发一组更加健壮和易用的组件
在React中,为了更完整地定义组件,经常需要对传入的props进行验证。这称为props验证。与状态(state)不同的是,由父组件提供的props可能与我们组件期望的不符,所以我们需要验证来保证props的格式和内容。React提供以下两种方式来验证props:1. PropTypes 库React提供了PropTypes库来验证组件的props是否符合要求。使用 PropTypes 最简单的方式就是:
jsx import PropTypes from 'prop-types'; MyComponent.propTypes = { name: PropTypes.string, age: PropTypes.number.isRequired }
这里定义了:- name props 为字符串类型(string) - age props 为必传(isRequired),为数值(number)类型如果父组件提供的props不符合要求,React会发出警告。PropTypes还支持:- array - bool - func - symbol - node - element - object - any等类型,详见文档。2.自定义验证函数我们也可以定义自定义的验证函数:
jsx validateAge = (props, propName, componentName) => { const age = props[propName]; if (age < 18) { return new Error( '请仅提供年满18岁成年人的姓名。' ); } } MyComponent.propTypes = { name: PropTypes.string, age: validateAge }
当传入 age < 18 时,React会抛出自定义的错误。总结总的来说,props验证可以:- 定义组件可接收的props类型 - 声明某些props为必传(isRequired) - 注明props的默认值(defaultProps) - 自定义验证函数来满足需要 - 发出警告提示开发者不正确的props用法所有这些都有助于定义功能完备且易于使用的组件。
React 16.4 实例
var title = "菜鸟教程"; // var title = 123; class MyTitle extends React.Component { render() { return ( <h1>Hello, {this.props.title}</h1> ); } } MyTitle.propTypes = { title: PropTypes.string }; ReactDOM.render( <MyTitle title={title} />, document.getElementById('example') );
React 15.4 实例
var title = "菜鸟教程"; // var title = 123; var MyTitle = React.createClass({ propTypes: { title: React.PropTypes.string.isRequired, }, render: function() { return <h1> {this.props.title} </h1>; } }); ReactDOM.render( <MyTitle title={title} />, document.getElementById('example') );
更多验证器内容
MyComponent.propTypes = { // 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的 optionalArray: React.PropTypes.array, optionalBool: React.PropTypes.bool, optionalFunc: React.PropTypes.func, optionalNumber: React.PropTypes.number, optionalObject: React.PropTypes.object, optionalString: React.PropTypes.string, // 可以被渲染的对象 numbers, strings, elements 或 array optionalNode: React.PropTypes.node, // React 元素 optionalElement: React.PropTypes.element, // 用 JS 的 instanceof 操作符声明 prop 为类的实例。 optionalMessage: React.PropTypes.instanceOf(Message), // 用 enum 来限制 prop 只接受指定的值。 optionalEnum: React.PropTypes.oneOf(['News', 'Photos']), // 可以是多个对象类型中的一个 optionalUnion: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.number, React.PropTypes.instanceOf(Message) ]), // 指定类型组成的数组 optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number), // 指定类型的属性构成的对象 optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number), // 特定 shape 参数的对象 optionalObjectWithShape: React.PropTypes.shape({ color: React.PropTypes.string, fontSize: React.PropTypes.number }), // 任意类型加上 `isRequired` 来使 prop 不可空。 requiredFunc: React.PropTypes.func.isRequired, // 不可空的任意类型 requiredAny: React.PropTypes.any.isRequired, // 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。 customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error('Validation failed!'); } } } }
vue中处理props
Vue在处理类似props验证时采用的方式不同于React。首先,Vue中没有单独的props
属性来接收来自父组件的数据,而是直接定义在组件的props
选项中:
js Vue.component('child', { props: { // props 定义 title: String, size: [String, Number] } })
这里定义了title
和size
两个prop,分别指定了类型。Vue内置支持以下prop类型:- String - Number - Boolean - Array - Object - Date - Function - Symbol这些 prop 类型会在组件初始化时进行类型检查。此外,Vue还提供以下验证方式:- required
来保证这个 prop 是必传的。 - default
来提供 prop 的默认值。 - validator
用来自定义验证函数。
js props: { title: { type: String, required: true }, age: { type: Number, default: 0, validator: value => value >= 0 } }
所以总的来说,Vue中通过:- 在props
选项中定义prop - 为每个prop指定类型 - 使用required
、default
和validator
来实现prop验证这样就实现了与React相似的效果。不同的是,Vue使用的是一个集中的props
选项,而不是像React那样有propTypes
和defaultProps
两个属性。
react事件处理(*待深入理解)
关于vue的绑定和react绑定
- Vue封装了this绑定,开发者直接使用即可 - React需要开发者针对不同情况手动绑定this具体来说:1. Vue中,开发者只需要通过v-on
指令将方法绑定到事件监听器上。
html <button v-on:click="handleClick">按钮</button>
-
在方法内部,
this
会自动绑定到Vue实例。不需要任何手动操作。3. 这是因为Vue封装了事件监听器,内部实现了this
的自动绑定。4. 而在React中,开发者需要针对不同情况,自行绑定this
:- 在构造函数中绑定
- 使用箭头函数 - 在回调函数中使用.bind(this)5. 因为React没有自动绑定this
的机制,需要开发者自行处理。所以,总的来说:- Vue中this
绑定是自动的,开发者使用简单方便 - React中this
绑定需要开发者针对每种情况手动完成 - 这是因为Vue内部封装了this
绑定机制,而React则没有
开始
与原生js区别的命名,react使用驼峰写法(不能算命名)
React 元素的事件处理和 DOM 元素类似。但是有一点语法上的不同:
-
React 事件绑定属性的命名采用驼峰式写法,而不是小写。
-
如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)
站在生命周期(钩子函数)有关的角度
只需要在开始添加钩子函数,react监测后自动帮你移除
在React中,你不需要使用像addEventListener
这类DOM API为组件添加监听器。而是可以直接在组件的render()
或者生命周期钩子中(如componentDidMount
)提供监听器。React会智能地在组件渲染或者更新时自动添加或更新相应的事件监听器。举个例子:
jsx class Button extends React.Component { componentDidMount() { document.addEventListener('click', this.handleClick); } componentWillUnmount() { document.removeEventListener('click', this.handleClick); } handleClick() { // ... } }
这里我们确实使用了addEventListener
在componentDidMount
中添加了监听器。但是更React的方式是:
jsx class Button extends React.Component { handleClick = () => { // ... } render() { return <button onClick={this.handleClick}>按钮</button>; } }
在render()
中直接提供一个onClick
监听器。React会在组件渲染时自动为这个<button>
元素添加事件监听器,调用this.handleClick
。在组件卸载时也会自动移除事件监听器。所以说,总的来说,React能帮我们处理:- 在组件初始渲染时自动添加事件监听器 - 在组件更新时更新事件监听器 - 在组件卸载时自动移除事件监听器这点与使用addEventListener
不同。所以,在React中通常不需要直接操作DOM来添加事件监听器,只需要在render()
或者生命周期函数中提供事件监听器即可。
vue的事件处理
Vue中事件处理的方式与React有一定不同。# Vue中事件处理主要有以下几种方式:1. 使用v-on
指令绑定事件监听器:
html <button v-on:click="doSomething"> </button>
v-on
会监听表示所绑定的事件。2. 在methods
选项中定义事件处理函数:
js methods: { doSomething: function () { // 处理逻辑... } }
-
事件处理函数接收一个
$event
参数:
html <button v-on:click="doSomething($event)">
$event
就是原生DOM事件,可以通过它调用preventDefault
等方法。4. 把方法的内联语句简写为:
html <button @click="doSomething">
-
同样支持使用
.native
修饰符绑定到原生事件:
html <button @click.native="doSomething">
-
Vue的自定义事件也使用
v-on
来监听,并使用$emit
触发:
html <my-component v-on:custom-event="doSomething"></my-component> js this.$emit('custom-event')
这就实现了组件间的通信。7. Vue还提供了$on/$off
方法在代码中监听/移除事件:
js this.$on('custom-event', function () {}) this.$off('custom-event')
总的来说,Vue中的事件处理主要通过:- v-on
指令绑定事件监听器 - 在methods
中定义事件回调函数 - 可以访问原生事件的$event
参数 - 使用.native
绑定到原生事件 - 通过$emit/$on
实现组件间通信它与React的差异主要在于:- Vue不需要在生命周期中添加/移除监听器 - Vue提供了$event
参数和.native
修饰符访问原生事件 - Vue支持组件间通过自定义事件实现通信
react条件渲染
元素变量
你可以使用变量来储存元素。它可以帮助你有条件的渲染组件的一部分,而输出的其他部分不会更改。
在下面的例子中,我们将要创建一个名为 LoginControl 的有状态的组件。
它会根据当前的状态来渲染 <LoginButton /> 或 <LogoutButton />,它也将渲染前面例子中的 <Greeting />。
React 实例
class LoginControl extends React.Component { constructor(props) { super(props); this.handleLoginClick = this.handleLoginClick.bind(this); this.handleLogoutClick = this.handleLogoutClick.bind(this); this.state = {isLoggedIn: false}; } handleLoginClick() { this.setState({isLoggedIn: true}); } handleLogoutClick() { this.setState({isLoggedIn: false}); } render() { const isLoggedIn = this.state.isLoggedIn; let button = null; if (isLoggedIn) { button = <LogoutButton onClick={this.handleLogoutClick} />; } else { button = <LoginButton onClick={this.handleLoginClick} />; } return ( <div> <Greeting isLoggedIn={isLoggedIn} /> {button} </div> ); } } ReactDOM.render( <LoginControl />, document.getElementById('example') );
与运算符 &
&
下面这段实例给我最用用的首先是{}存储变量,并且能嵌套(尽管这可能在设计上有些问题,让我联想到node的回调地狱,尽管我经不太清具体内容了🤨)
function Mailbox(props) { const unreadMessages = props.unreadMessages; return ( <div> <h1>Hello!</h1> {unreadMessages.length > 0 && <h2> 您有 {unreadMessages.length} 条未读信息。 </h2> } </div> ); } const messages = ['React', 'Re: React', 'Re:Re: React']; ReactDOM.render( <Mailbox unreadMessages={messages} />, document.getElementById('example') );
注意
在 JavaScript 中,true && expression 总是返回 expression,而 false && expression 总是返回 false。
因此,如果条件是 true,&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它。
三目运算符
中规中矩,感觉没啥特别需要区别的
条件渲染的另一种方法是使用 JavaScript 的条件运算符:
condition ? true : false。
在下面的例子中,我们用它来有条件的渲染一小段文本。
render() { const isLoggedIn = this.state.isLoggedIn; return (
The user is {isLoggedIn ? 'currently' : 'not'} logged in.
); } 同样它也可以用在较大的表达式中,虽然不太直观:
render() { const isLoggedIn = this.state.isLoggedIn; return ( <div> {isLoggedIn ? ( <LogoutButton onClick={this.handleLogoutClick} /> ) : ( <LoginButton onClick={this.handleLoginClick} /> )} </div> ); }
阻止组件渲染*
极少数情况下,你可能希望隐藏组件,即使它被其他组件渲染。
React 列表 & Keys
重构组件*
这个案例可以当成重构,封装组件的学习案例
我们可以使用 JavaScript 的 map() 方法来创建列表。
const numbers = [1, 2, 3, 4, 5]; const listItems = numbers.map((numbers) => <li>{numbers}</li> ); ReactDOM.render( <ul>{listItems}</ul>, document.getElementById('example') );
我们可以将以上实例重构成一个组件,组件接收数组参数,每个列表元素分配一个 key,不然会出现警告 a key should be provided for list items,意思就是需要包含 key:
function NumberList(props) { const numbers = props.numbers; const listItems = numbers.map((number) => <li key={number.toString()}> {number} </li> ); return ( <ul>{listItems}</ul> ); } const numbers = [1, 2, 3, 4, 5]; ReactDOM.render( <NumberList numbers={numbers} />, document.getElementById('example') );
Keys*
一般是数组中id作为key,没有的一般用index(索引)
key常见错误表达
key常见正确表达
元素的 key 在他的兄弟元素之间应该唯一
在 jsx 中嵌入 map()
React API
React api是React为开发者提供的一系列 API 函数和属性,用于构建 React 应用。主要包含以下几个方面:# 组件相关API- React.Component - 创建React组件类 - React.PureComponent - 创建纯组件类 - props - 组件接收属性 - state - 组件内部状态 - setState() - 更新组件状态 - forceUpdate() - 强制组件更新 - render() - 渲染组件# 元素相关API- React.createElement() - 创建 React 元素 - ReactDOM.render() - 渲染元素到 DOM - ReactDOM.hydrate() - 服务器端渲染时使用 - React.Children - 操作子元素# 生命周期钩子- componentDidMount() - componentWillUnmount() - getDerivedStateFromProps() - shouldComponentUpdate() 等# Context API- React.createContext() - 创建 Context对象 - <Context.Provider> - 提供Context对象 - <Context.Consumer> - 消费Context对象# 高阶组件(HOC)- withRouter - 给组件提供路由信息 - authoRize - 鉴权 - withTheme- 动态切换主题等# Fragments- <React.Fragment> - 包裹组件 - <> shorter syntax - 简写# 动画- CSSTransition - CSS 动画 - TransitionGroup - 列表动画 - Transition - 库抽象动画等等,这些都是React所提供的典型API。利用这些功能丰富的API,开发者就可以构建出强大的React应用。
react组件生命周期
建议看文档
挂载
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
-
constructor()
: 在 React 组件挂载之前,会调用它的构造函数。 -
getDerivedStateFromProps()
: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。 -
render()
: render() 方法是 class 组件中唯一必须实现的方法。 -
componentDidMount()
: 在组件挂载后(插入 DOM 树中)立即调用。
render() 方法是 class 组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。
这些方法的详细说明,可以参考官方文档。
更新
每当组件的 state 或 props 发生变化时,组件就会更新。
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
-
getDerivedStateFromProps()
: 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。 -
shouldComponentUpdate()
:当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。 -
render()
: render() 方法是 class 组件中唯一必须实现的方法。 -
getSnapshotBeforeUpdate()
: 在最近一次渲染输出(提交到 DOM 节点)之前调用。 -
componentDidUpdate()
: 在更新后会被立即调用。
render() 方法是 class 组件中唯一必须实现的方法,其他方法可以根据自己的需要来实现。
这些方法的详细说明,可以参考官方文档。
卸载
当组件从 DOM 中移除时会调用如下方法:
-
componentWillUnmount()
: 在组件卸载及销毁之前直接调用。
这些方法的详细说明,可以参考官方文档。
react ajax*(实战类待补充)
React 表单与事件(实战重点)
一个简单的实例
可对比vue的动态渲染(是这么个东西来着🤔)
在实例中我们设置了输入框 input 值 value = {this.state.data}。在输入框值发生变化时我们可以更新 state。我们可以使用 onChange 事件来监听 input 的变化,并修改 state。
实例
class HelloMessage extends React.Component { constructor(props) { super(props); this.state = {value: 'Hello Runoob!'}; this.handleChange = this.handleChange.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } render() { var value = this.state.value; return <div> <input type="text" value={value} onChange={this.handleChange} /> <h4>{value}</h4> </div>; } } ReactDOM.render( <HelloMessage />, document.getElementById('example') );
实例2*
需要思考一下
在以下实例中我们将为大家演示如何在子组件上使用表单。 onChange 方法将触发 state 的更新并将更新的值传递到子组件的输入框的 value 上来重新渲染界面。
你需要在父组件通过创建事件句柄 (handleChange) ,并作为 prop (updateStateProp) 传递到你的子组件上。
class Content extends React.Component { render() { return <div> <input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp} /> <h4>{this.props.myDataProp}</h4> </div>; } } class HelloMessage extends React.Component { constructor(props) { super(props); this.state = {value: 'Hello Runoob!'}; this.handleChange = this.handleChange.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } render() { var value = this.state.value; return <div> <Content myDataProp = {value} updateStateProp = {this.handleChange}></Content> </div>; } } ReactDOM.render( <HelloMessage />, document.getElementById('example') );
Select 下拉菜单
让我想起了学JavaScript时写的很多dom练习;操纵的window对象
在 React 中,不使用 selected 属性,而在根 select 标签上用 value 属性来表示选中项。
class FlavorForm extends React.Component { constructor(props) { super(props); this.state = {value: 'coconut'}; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert('Your favorite flavor is: ' + this.state.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> 选择您最喜欢的网站 <select value={this.state.value} onChange={this.handleChange}> <option value="gg">Google</option> <option value="rn">Runoob</option> <option value="tb">Taobao</option> <option value="fb">Facebook</option> </select> </label> <input type="submit" value="提交" /> </form> ); } } ReactDOM.render( <FlavorForm />, document.getElementById('example') );
多个表单
当你有处理多个 input 元素时,你可以通过给每个元素添加一个 name 属性,来让处理函数根据 event.target.name 的值来选择做什么
class Reservation extends React.Component { constructor(props) { super(props); this.state = { isGoing: true, numberOfGuests: 2 }; this.handleInputChange = this.handleInputChange.bind(this); } handleInputChange(event) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value }); } render() { return ( <form> <label> 是否离开: <input name="isGoing" type="checkbox" checked={this.state.isGoing} onChange={this.handleInputChange} /> </label> <br /> <label> 访客数: <input name="numberOfGuests" type="number" value={this.state.numberOfGuests} onChange={this.handleInputChange} /> </label> </form> ); } }
以下实例演示通过 onClick 事件来修改数据:
class HelloMessage extends React.Component { constructor(props) { super(props); this.state = {value: 'Hello Runoob!'}; this.handleChange = this.handleChange.bind(this); } handleChange(event) { this.setState({value: '菜鸟教程'}) } render() { var value = this.state.value; return <div> <button onClick={this.handleChange}>点我</button> <h4>{value}</h4> </div>; } } ReactDOM.render( <HelloMessage />, document.getElementById('example') );
当你需要从子组件中更新父组件的 state 时,你需要在父组件通过创建事件句柄 (handleChange) ,并作为 prop (updateStateProp) 传递到你的子组件上。实例如下:
class Content extends React.Component { render() { return <div> <button onClick = {this.props.updateStateProp}>点我</button> <h4>{this.props.myDataProp}</h4> </div> } } class HelloMessage extends React.Component { constructor(props) { super(props); this.state = {value: 'Hello Runoob!'}; this.handleChange = this.handleChange.bind(this); } handleChange(event) { this.setState({value: '菜鸟教程'}) } render() { var value = this.state.value; return <div> <Content myDataProp = {value} updateStateProp = {this.handleChange}></Content> </div>; } } ReactDOM.render( <HelloMessage />, document.getElementById('example') );
React Refs(谨慎使用)
React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。
这个特殊的属性允许你引用 render() 返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例。
使用方法
绑定一个 ref 属性到 render 的返回值上:
<input ref="myInput" />
在其它代码中,通过 this.refs 获取支撑实例:
var input = this.refs.myInput; var inputValue = input.value; var inputRect = input.getBoundingClientRect();
完整实例
你可以通过使用 this 来获取当前 React 组件,或使用 ref 来获取组件的引用,实例如下:
React 实例
class MyComponent extends React.Component { handleClick() { // 使用原生的 DOM API 获取焦点 this.refs.myInput.focus(); } render() { // 当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs return ( <div> <input type="text" ref="myInput" /> <input type="button" value="点我输入框获取焦点" onClick={this.handleClick.bind(this)} /> </div> ); } } ReactDOM.render( <MyComponent />, document.getElementById('example') );
实例中,我们获取了输入框的支撑实例的引用,子点击按钮后输入框获取焦点。
我们也可以使用 getDOMNode()方法获取DOM元素
vue中ref
Ref 在 Vue 和 React 中的意义和用法都比较相似。在 Vue 中,ref 也是用于引用渲染出来的元素或组件实例。使用 ref 的方式主要有:1. 字符串形式:
html <input ref="hello">
访问:
js this.$refs.hello
-
使用 ref 回调函数:
html <input ref="hello">
访问:
js this.hello = ref // 赋值给数据对象
-
给组件添加 ref:
html <ChildComponent ref="child" />
访问:
js this.$refs.child this.child //如果用了ref回调
-
给多元素添加同一个 ref:
html <input ref="allInputs"> <input ref="allInputs">
这时 $refs.allInputs
会返回一个数组。5. 在 Vue 里使用 ref 是异步的,需要使用 $nextTick
:
js this.$nextTick(() => { this.$refs.hello.focus() })
所以在 Vue 和 React 中:- ref 的作用都是引用渲染出来的元素或组件实例 - 使用方式基本类似,但在实现上存在差异 - 在 Vue 中,ref 是异步引用,需要使用 $nextTick
通过将后端数据渲染到前端,ref
在 Vue 和 React 中的使用场景应该是通用的。