React
1. 脚手架的安装
下载:
npm install -g create-react-app
可能会报错,有可能是因为没有权限,以管理员身份运行,就可以了 。
C:\Windows\system32>npm install -g create-react-app
D:\TEND\node\create-react-app -> D:\TEND\node\node_modules\create-react-app\index.js
+ create-react-app@4.0.1
added 67 packages from 25 contributors in 4.77s
创建项目:
// 创建文件夹
mkdir ReactDemo
// 创建项目
create-react-app demo01
// 进入项目
cd demo01
// 运行项目
npm start
项目目录:
node_modules 包管理文件
public
--- favicon.ico 项目图标
--- index.html 项目页面
--- manifest.json 移动端适配文件
src
--- index.js 项目入口文件
--- index.css 项目的全局样式文件
--- App.js 组件(模块化开发)
--- App.css 组件的样式文件
--- reportWebVitals.js 用于移动端的(离线缓存功能)
--- setupTests.js DOM 断言的适配器
--- App.test.js
.gitignore git 省略文件
package-lock.json 包版本和下载地址管理文件
package.json 项目管理文件
README.md 项目说明文件
你可以在 src 中创建子目录。 为了加快重新构建的速度,Webpack 只处理 src 中的文件。 你需要将任何 JS 和 CSS 文件放在 src 中,否则 Webpack 将发现不了它们。
2. Hello react
1,删除 src 中的所有文件,从头开始写:
2,创建项目的入口文件: index.js
import React from 'react'
import ReactDOM from 'react-dom'
// 导入 App 组件,
import App from './App'
// 把 组件 放到 页面模板上,id 为 root 的元素上
ReactDOM.render(<APP />, document.getElementById('root'))
3,创建 App.js 组件,组件必须以大写开头;
import React, { Component } from 'react'
// 等于以下代码
// import React from 'react'
// const Component = React.Component
class App extends Component {
render () {
return (
<div>Hello zyf</div>
)
}
}
export default App
3. jsx
1. 概念:
js 和 xml 结合,遇到 < 就编译为 html 语法, 遇到 {} 就编译为 js 语法;
这个语法,让 react 可以更方便创建 虚拟DOM,创建html结构;
没有 jsx 之前,react 创建虚拟DOM 的方法:
class App extends Component {
render () {
var child1 = React.createElement('li', null, '你好')
var child2 = React.createElement('li', null, '我是 zyf啊')
var root = React.createElement('ul', {className: 'my-list'}, child1, child2)
}
}
有 jsx 后就简单的:
class App extends Component {
render () {
// jsx: js 和 xml 结合,遇到 < 就编译为 html 语法, 遇到 {} 就编译为 js 语法;
return (
<div>
<div>Hello zyf</div>
<ul>
<li>{false ? '你好' : 'false哦'}</li>
<li>我是 zyf啊</li>
</ul>
</div>
)
}
}
2. () 的作用:
有了 () 我们就可以使 html 结构换行显示;
3. Frangment 标签:
在 React 要求必须在组件的最外层进行包裹,可以使用 div 进行包裹 或使用 teact 的 标签;
使用 Fragment 标签后,他不会进行渲染;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ol3d6IVr-1608447252144)(H:\笔记本\images\React\使用 Fragment 标签.png)]
以下是代码, Frangment 标签需要从 react 中导入:
// App 组件
import React, { Component, Fragment } from 'react'
import './App.css'
class App extends Component {
render () {
// jsx: js 和 xml 结合,遇到 < 就编译为 html 语法, 遇到 {} 就编译为 js 语法;
return (
<Fragment>
<div>Hello zyf</div>
<ul>
<li>{false ? '你好' : 'false哦'}</li>
<li className="my-mane">我是 zyf啊</li>
</ul>
</Fragment>
)
}
}
export default App
4. 样式使用:
**在 jax 中的 class 样式名称不是 lcass="xxx",而是 className="xxx";**因为这个 class 名字可以会和 创建 组件 时的 Class 混乱了 。
// App 组件
import React, { Component, Fragment } from 'react'
import './App.css'
class App extends Component {
render () {
return (
<Fragment>
<div>Hello zyf</div>
<ul>
<li>{false ? '你好' : 'false哦'}</li>
<li className="my-mane">我是 zyf啊</li>
</ul>
</Fragment>
)
}
}
export default App
5. jsx 代码注释:
JSX中的代码注释是非常有讲究的 ,具体的方法是这样的 {/* xx */}, 而不是 // 或者 /**/:
快捷键是:Ctrl + / (可能不同的编辑器的快捷键不同,可以百度一下);
看代码咯:
<ul>
{
this.state.list.map((item, index) => {
return (
<li key={item+index}>
// 内容 => 错误的
{item}
{/* 删除按钮 */} => 正确的
<button
onClick={this.deleteList.bind(this, index)}>
删除
</button>
</li>
)
})
}
</ul>
注意:
- 可能要在
<Frangment>标签中才能生效;
6. 解析 html 标签:
如果想要把 html 标签解析成 和在html页面上的标签一致的效果,就可以使用 dangerouslySetInnerHTML
<ul>
{
this.state.list.map((item, index) => {
return (
<li key={item+index} className="d-flex">
{/* 内容 */}
<span dangerouslySetInnerHTML={{__html: item}}></span>
{/* 删除按钮 */}
<button
onClick={this.deleteList.bind(this, index)}>
删除
</button>
</li>
)
})
}
</ul>
7. label 的区别:
label 的作用是:用户点击 labe 中的文字时, 焦点可以自动的定位到 input 元素上面 。
我们按照平时的 html 语法是这样写的:
render () {
return (
<Fragment>
<label for="inputId">添加学习忍术:</label>
<input
id="inputId"
value={this.state.inputValue}
onChange={this.inputChange.bind(this)}
/>
<button onClick={this.addList.bind(this)}>学习忍术</button>
}
}
但这时,控制台会报错,说要把 for 这个属性改成 htmlFor 这种格式:
<label htmlFor="inputId">添加学习忍术:</label>
<input
id="inputId"
value={this.state.inputValue}
onChange={this.inputChange.bind(this)}
/>
4. 数据绑定 和 响应式设计
1. 数据绑定
在 组件 class 中使用 constructor 来存放数据,vue 为 data:
主要代码:
constructor (props) {
super(props) // 调用父类的构造函数,固定写法
this.state = { // 存放数据
xxxx: 'xxxx',
list: []
}
}
创建好数据后,为 输入框 绑定数据,他不和 vue 一样是 双向数据绑定的, react 是 单向数据流;
在 input 中使用
<input value={ this.state.inputValue }/>
和 this.state 中的数据进行绑定;
但是会报错,说这样在 input 中,这个数据只能只读,不能修改 。 大概是这个元素,有绑定数据,通过事件实现 数据的双向绑定;
index.js:1 Warning: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.
2. 响应式设计
所以,我们要为 input 进行绑定事件,然后把 input 的值,赋值到 this.state 中:
<input value={this.state.inputValue} onChange={this.inputChange}/>
事件处理函数 写在 class Naruto extends Component 中:
class Naruto extends Component {
inputChange (e) {
// 获取 input 中输入的数据
const data = e.target.value
}
}
然后,我们要把获取的值,赋值到数据中:
要注意格式,和微信小程序一样的
inputChange (e) {
// 获取 input 中输入的数据
const data = e.target.value
// 赋值到 this.start 中
this.setState({
inputValue: data
})
}
不能是这样:错误错误错误 。
inputChange (e) {
// 获取 input 中输入的数据
const data = e.target.value
// 赋值到 this.start 中
this.state.inputValue = data;
}
还会报错,因为 函数中的 this 为 undefined,所以我们要传递 this 过去:
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)}/>
在函数中,所以 this 就是指向 input 了 。
以下代码:
// 鸣人组件
import React, { Component, Fragment } from 'react'
class Naruto extends Component {
// 继承父类的 props 参数,Naruto 继承了 Component
constructor (props) {
super(props) // 调用父类的构造函数,固定写法
// 存放数据
this.state = {
inputValue: '你好',
list: []
}
}
// jsx 语法 ( 模板
render () {
return (
<Fragment>
<input value={ this.state.inputValue }/>
<button>学习忍术</button>
<ul>
<li>隐分身</li>
<li>螺旋丸</li>
</ul>
</Fragment>
)
}
}
export default Naruto
5. react 中的事件
注意:
1. 绑定事件:
-
要添加
on, 如onChange,on后面必须 大写; -
进行事件绑定时,不用双引号,事件名用 大括号 括起来;
<input onChange={this.inputChange} /> -
在
class Naruto extends Component写方法事件 和 数据 , jsx 同级,他们不需要使用 逗号分隔class Naruto extends Component { // 数据 constructor (props) { ..... } // jsx 语法 ( 模板 render () { return ( ..... ) } // 事件 inputChange (e) { console.log(e) } }
2. this指向:
-
触发事件中的
this指向, 他可能为undefined,所以在 绑定事件时,可以使用bind()修改事件中的this指向 。<input value={this.state.inputValue} onChange={this.inputChange.bind(this)}/> inputChange (e) { // 获取 input 中输入的数据 const data = e.target.value console.log(this) }

3. 修改数据:
修改 react 中的数据:和微信小程序一样 。
this.setState({
xxxx: xx
})
还有一个要注意的是 修改数据时,不能直接修改里面的值(不能像 vue 一样);
他要拿到值,然后修改,然后再放到 数据里面 ;
记住React是禁止直接操作state的 。
class xx extends Component {
constructor (props) {
super(props)
this.state = {
list: ['xxx', 'xxx']
}
}
xxx () {
// 错误
this.setState({
list: this.state.list.splice(1,1);
})
// 正确
const lists = this.state.list
list.splice(1,1)
this.setState({
list: lists
})
}
}
4. 获取数据:
获取 react 中的数据,要注意一下,不能和 vue 一样
class Naruto extends Component {
// 数据
constructor (props) {
super(props) // 调用父类的构造函数,固定写法
this.state = {
inputValue: '',
list: ['隐分身', '螺旋丸']
}
}
}
// 正确
this.state.inputValue
// 错误
this.inputValue
5. 获取函数对象中的参数:
inputChange (e) {
// e.target.value 元素内容
}
6. 传递参数:
函数传递参数,都携带在 bind() 的第二个参数中,然后在处理函数中接收 。
<ul>
{
this.state.list.map((item, index) => {
return (
<li key={item+index}>
{item}
<button
onClick={this.deleteList.bind(this, index)}>
删除
</button>
</li>
)
})
}
</ul>
// 点击删除
deleteList (index) {
// react 不能直接修改 state 中的数据,要拿出来后,再进行修改,在赋值进去
// 获取值
const lists = this.state.list;
// 修改值
lists.splice(index, 1);
// 把处理后的值,放到 state 中
this.setState({
list: lists
})
}
1. onChange
概念:
input 输入触发事件;
6. react 的循环渲染
1. map() 循环
在 jsx 中循环渲染数据,不能像 vue 一样,使用 if 循环出来,因为在 react 中的 jsx 的 js 语法要写在 {} 花括号中,这样使用 if 循环,就很难写;
我们可以使用 map 来进行渲染,然后要写在 {} 花括号里面
{
this.state.list.map((item, index) => {
return <li>{item}</li>
})
}
2. key
如果在循环中,没有写 key,会报错的,所以呢!
要注意 react 和 vue 一样,需要 key ,然后不能单单使用 index 来作为 kye 的值,因为页面中可能有多个循环,就有多个相同的 key 值了啊,懂了吗?
所以,我们将 item + index 组成 key 的值 。
key={item+index}
以下代码:
class Naruto extends Component {
// 数据
constructor (props) {
super(props) // 调用父类的构造函数,固定写法
this.state = {
inputValue: '',
list: ['隐分身', '螺旋丸']
}
}
// 结构
render () {
return (
<Fragment>
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)}/>
<button onClick={this.addList.bind(this)}>学习忍术</button>
<ul>
{
this.state.list.map((item, index) => {
return <li key={item+index}>{item}</li>
})
}
</ul>
</Fragment>
)
}
}
ey` 值了啊,懂了吗?
所以,我们将 item + index 组成 key 的值 。
key={item+index}
以下代码:
class Naruto extends Component {
// 数据
constructor (props) {
super(props) // 调用父类的构造函数,固定写法
this.state = {
inputValue: '',
list: ['隐分身', '螺旋丸']
}
}
// 结构
render () {
return (
<Fragment>
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)}/>
<button onClick={this.addList.bind(this)}>学习忍术</button>
<ul>
{
this.state.list.map((item, index) => {
return <li key={item+index}>{item}</li>
})
}
</ul>
</Fragment>
)
}
}
7. 插件
1. React 快速生成语法插件
Simple React Snippets 为 vscode 中的插件,以下是快速生成的语法:
1,cc 快速生成 组件的结构;
2,imrc 导入 React 的基本文件;
| Snippet | Renders |
|---|---|
imr | Import React |
imrc | Import React / Component |
imrs | Import React / useState |
imrse | Import React / useState useEffect |
impt | Import PropTypes |
impc | Import React / PureComponent |
cc | Class Component |
ccc | Class Component With Constructor |
cpc | Class Pure Component |
sfc | Stateless Function Component |
cdm | componentDidMount |
uef | useEffect Hook |
cwm | componentWillMount |
cwrp | componentWillReceiveProps |
gds | getDerivedStateFromProps |
scu | shouldComponentUpdate |
cwu | componentWillUpdate |
cdu | componentDidUpdate |
cwu | componentWillUpdate |
cdc | componentDidCatch |
gsbu | getSnapshotBeforeUpdate |
ss | setState |
ssf | Functional setState |
usf | Declare a new state variable using State Hook |
ren | render |
rprop | Render Prop |
hoc | Higher Order Component |
8. 组件的拆分
1. 创建组件
创建 js 文件,然后导出,注意创建的组件文件必须首字母大写哦 !
// 列表组件
import React, { Component } from 'react';
class Lists extends Component {
render() {
return (
<li>你好</li>
)
}
}
export default Lists;
2. 导入组件
在页面上导入组件,然后根据导入的组件,在页面填写 组件标签;
关键代码:
<Lists />
全部代码:
// 鸣人组件
import React, { Component, Fragment } from 'react'
import './App.css'
import Lists from './Lists'
class Naruto extends Component {
// 数据
constructor (props) {
super(props) // 调用父类的构造函数,固定写法
this.state = {
inputValue: '',
list: ['隐分身', '螺旋丸']
}
}
// jsx 语法 ( 模板
render () {
return (
<Fragment>
<label htmlFor="inputId">添加学习忍术:</label>
<input id="inputId" value={this.state.inputValue} onChange={this.inputChange.bind(this)}/>
<button onClick={this.addList.bind(this)}>学习忍术</button>
<ul>
{
this.state.list.map((item, index) => {
return (
<Lists key={item + index} />
)
})
}
</ul>
</Fragment>
)
}
}
export default Naruto
9. 父子组件的传值
1. 自定义属性的方法:
在子组件中可以使用
this.props.父组件传递的属性名
来获取父组件传递过来的值;
成员: 父组件(Naruto.js), 子组件(items.js);
界面是这样子的,我们把界面分为2个组件

1. 父向子传值
1,父组件要传递列表的内容给子组件,让子组件渲染内容;
2,在父组件中引入 子组件;
Naruto.js
// 鸣人组件
import React, { Component, Fragment } from 'react'
import Items from './Items'
3, 就可以对应的写上 子组件的标签名了,和引入名称一致;
<Items />
4, 使用 自定义属性 传递数据给子组件;
<ul>
{
// 循环渲染内容
this.state.list.map((item, index) => {
return (
// 使用组件
<Items
key={item + index}
// 传递 每项的内容 给子组件
content={item}
/>
)
})
}
</ul>
自定义属性的值,就是传递给子组件的数据,我们在子组件中获取数据;
5, 子组件 获取父组件传递的数据 this.props.xxx;
<li>{this.props.content}</li>
就可以了。
2. 子向父传值
其实就是,子组件调用父组件的方法;
实现:点击子组件中的 li 要删除 父组件 中对应的数据;
1,在 子组件 的 li 绑定事件,让事件顺利触发:
Item.js
import React, { Component, Fragment } from 'react';
class Item extends Component {
render() {
return (
<Fragment>
<li onClick={this.delete.bind(this)}>
{this.props.content}
</li>
</Fragment>
)
}
delete () {
console.log(1)
}
}
2,在父组件 中传递 删除 li 的函数给 子组件:
<ul>
{
this.state.list.map((item, index) => {
return (
<Lists
key={item + index}
content={item}
// 传递索引
index={index}
// 传递删除事件
deleteList={this.deleteList.bind(this)}
/>
)
})
}
</ul>
3,子元素在 li 的点击函数中调用传递过来的事件:
Items.js
class Lists extends Component {
render() {
return (
<Fragment>
<li onClick={this.delete.bind(this)}>{this.props.content}</li>
</Fragment>
)
}
delete () {
// 接收父组件传递过来的函数,并传入当前点击的 li 的索引, index 是父组件传递过来的;
this.props.deleteList(this.props.index)
}
}
10. React 的特性
1. 单向数据流
父组件 传递给 子组件 的数据,在子组件中不能修改 父元素传递过来的数据;只能读取数据;
2. 搭配 jq
React 只对 index.html 中 id 为 root 的 div 起作用;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<title>React 例子</title>
</head>
<body>
<!-- js 文件出错或为加载完毕,就会提示这句话,可以加强程序的健壮性 -->
<noscript>js 文件出错</noscript>
<div id="root"></div>
<div>我在 react 的控制范围外</div>
</body>
</html>

3. 函数式编程
函数式编程的优点:
1,可以让代码更清晰,每一个功能都是一个函数;
2,更方便 代码的测试,容易实现 前端 自动化测试;
11. 调试工具
React developer tools
React developer tools有三种颜色,三种颜色代表三种状态:
- 灰色: 这种就是不可以使用,说明页面不是又React编写的。
- 黑色: 说明页面是用React编写的,并且处于生成环境当中。
- 红色: 说明页面是用React编写的,并且处于调试环境当中。
12. PropTypes 校验传递值
在 React 中父子组件传值时,给 传递的数据 进行校验,可以减少 bug 的出现,我们可以使用 PropTypes 来进行校验 。
1. 引入 prop-types
import PropTypes from 'prop-types'
2. 添加校验规则:
注意:要写在 class 外面 。
class Lists extends Component {
..........
}
// 校验:在 class 外,进行校验, 校验从父组件传递过来的数据
Lists.propTypes = {
接收数据: PropTypes.数据类型,
content: PropTypes.string
}
注意大小写:
Lists.propTypes = { // 小写
接收数据: PropTypes.数据类型, // 大写
content: PropTypes.string
}
例子:
// 列表组件
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types'
class Lists extends Component {
render() {
return (
<Fragment>
{this.props.date} + {this.props.content}
</Fragment>
)
}
}
// 校验:在 class 外,进行校验, 校验从父组件传递过来的数据
Lists.propTypes = {
content: PropTypes.string,
index: PropTypes.number,
deleteList: PropTypes.func,
date: PropTypes.string.isRequired,
}
export default Lists;
更多的规则可以查看 查看更多
常用规则:
-
接收数据: PropTypes.数据类型- 数据类型支持: 常见的类型
string, number, func, bool, object, symbol
- 数据类型支持: 常见的类型
-
是否必传:
接收数据: PropTypes.数据类型.isRequired;- 默认为否;
3. 添加默认值:
设置如果传递的参数,没有传递数据进来,就默认输出这个值;
类名.defaultProps = {
接收数据: 'xxx'
}
例子:
class Lists extends Component {
render() {
return (
<Fragment>
{this.props.level}
</Fragment>
)
}
// 默认值
Lists.defaultProps = {
level: '高级'
}
13. React 的 ref
1. 基本使用:
React 的 ref 他和 vue 中的 ref 有点不一样;虽然他可以获取 页面上的 DOM 元素;但操作方法不一样;
React 的 ref:
1,在标签上使用 ref 标记元素
<input
value={this.state.inputValue}
onChange={this.inputChange.bind(this)}
ref={(input) => { this.input = input }}
/>
使用 ref 来传递标签元素,在使用箭头函数中给 this 添加一个属性,
属性值就为 标签名,就是 标签元素(DOM);
2,在函数中可以获取该元素
inputChange () {
// 获取 ref 的值
console.log(this.input);
}
在控制台中输出为:

作用:
可以更语义化的表明操作的值
// 使用前
inputChange (e) {
// 获取 input 中输入的数据
const data = e.target.value
// 赋值到 this.start 中
this.setState({
inputValue: data
})
}
// 使用后
inputChange () {
// 获取 ref 中的 input
const data = this.input.value
// 赋值到 this.start 中
this.setState({
inputValue: data
})
}
Vue 的 ref:
<input ref="inputRef"/>
<script>
new Vue({
data: {
return {}
},
methods: {
fn () {
// 输出 input 这个 DOM 元素
console.log(this.$refs.inputRef);
}
}
})
</script>
2. 坑
如果我们想要在修改 数据后,进行某些操作,比如
我们想要点击按钮时,输出 子节点的数量(包括现在添加的子节点)

我们使用 ref 来获取 ul 中的 li 个数;
1,在 ul 标签使用 ref 标记;
2,在点击按钮中,输出 ul 中的 li 个数;
// 鸣人组件
import React, { Component, Fragment } from 'react'
import './App.css'
import Lists from './Lists'
class Naruto extends Component {
// 数据
constructor (props) {
super(props) // 调用父类的构造函数,固定写法
this.state = {
inputValue: '',
list: ['隐分身', '螺旋丸']
}
}
// jsx 语法 ( 模板
render () {
return (
<Fragment>
<label htmlFor="inputId">添加学习忍术:</label>
<input
id="inputId"
value={this.state.inputValue}
onChange={this.inputChange.bind(this)}
ref={(input) => { this.input = input }}
/>
<button onClick={this.addList.bind(this)}>学习忍术</button>
<ul ref={(ul) => { this.ul = ul }}>
{
this.state.list.map((item, index) => {
return (
<Lists
key={item + index}
content={item}
index={index}
deleteList={this.deleteList.bind(this)}
date={'4个月'}
/>
)
})
}
</ul>
</Fragment>
)
}
inputChange () {
// 获取 ref 的值
const data = this.input.value
// 赋值到 this.start 中
this.setState({
inputValue: data
})
}
// 点击按钮添加数据
addList () {
// 修改数据
this.setState({
list: [...this.state.list, this.state.inputValue],
inputValue: ''
})
// 使用 ref 来获取 ul 的子元素数量(主要是这个)
console.log(this.ul.querySelectorAll('li').length)
}
}
export default Naruto
3,出问题了,点击输出的 li 个数为之前的个数,不包含 新添加的 li;
4,这个是因为 this.setState 操作异步的,所以在 this.setState 还没有执行时, consle.log() 就输出了。
5,在 tis.setState 有一个回调函数,在 修改数据后,才执行,就可以顺利输出;
// 点击按钮添加数据
addList () {
this.setState({
list: [...this.state.list, this.state.inputValue],
inputValue: ''
}, () => {
console.log(this.ul.querySelectorAll('li').length)
})
}
14. React 组件的生命周期
因为 React 中的所有文件都是 组件,所有在所有 文件中,都适用;
现在时间为 2020.12.23 :
官网文档位置: API REFERENCE > React > React.Component 。
语法废用:
React16新的生命周期还没有弃用,17 就不用了
componentWillMount、
componentWillReceivePorps,
componentWillUpdate
新增了
getDerivedStateFromProps、
getSnapshotBeforeUpdate
来代替弃用的三个钩子函数
生命周期图:
首先 框架的语法,都会随着版本的修改,而发送改变,以下的图是 React ^16.4 版本的:

声明周期的 3 个阶段:
- 挂载阶段
constructor()构造函数;static getDerivedStateFromProps()render()初始化 state 中的数据;componentDidMount()完成 DOM 的挂载;
- 更新阶段
static getDerivedStateFromProps()shouldComponentUpdate()render()当 state 或 props 数据发送变化触发;getSnapshotBeforeUpdate()componentDidUpdate()
- 销毁阶段
1. 挂载阶段
constructor()
概念:
在 React 组件挂载之前,会调用它的构造函数。 组件中的数据 state 或 propo 发送变化时会再次执行 。
语法: (完整写法)
constructor(props) {
// 获取父组件传递的数据
super(props)
// 初始化内部的 state(数据), vue 中的 data
this.state = {
xxx: xxx
}
// 修改组件中函数的 this 指向
this.xxx = this.xxx.bind(this)
}
如果 不初始化 state 或不进行方法的绑定,则不需要为 React 组件实现 comstructor 构造函数;(就是如果组件中 不写 this.state 进行管理数据 和 不用进行函数的 this 指向修改,就可以不写这个生命周期函数,但是要接收父组件的数据还是要写的)。
分析:
super(props) : 如果不写他,在组件中使用 this.props 获取不到父组件传递的数据;
注意:
修改 this.state 中的数据,需要调用 this.setState() 方法;
获取 this.state 中的数据,只需调用 this.state.xxx 来获取数据;
避免将 props 的值 赋给 this.state 中的数据;
constructor (props) {
super(props)
this.state = {
color: props.color
}
}
static getDerivedStateFromProps()
概念:
会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
语法:
static getDerivedStateFromProps(props, state) {
// props: 父组件传递的数据
// state: this.state 中的数据
return state; // 返回值必须要有值
}
componentDidMount()
概念:
在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。
语法:
componentDidMount() {
// dom 节点已进行了挂载
// 发送网络请求
}
render()
概念:
当 state 和 props 更新时触发;render() 方法是 class 组件中唯一必须实现的方法。
当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:
- React 元素;
- 数组 或 fragments;
- Portals;
- 字符串 或 数值类型;
- 布尔值类型 或 null;
语法:
render () {
return (
<Fragment>
xxxxxxxxx
</Fragment>
)
}
注意:
如果 shouldComponentUpdate() 返回 false,则不会调用 render()。
2. 更新阶段
shouldComponentUpdate()
概念:
函数会在组件更新之前,自动被执行。
他可以 根据返回返回值:
true,就同意组件更新;false,就反对组件更新。
语法:
shouldComponentUpdate(nextProps, nextState) {
xxxxx
return true / false
}
注意:
- 它要求返回一个布尔类型的结果,必须有返回值,这里就直接返回一个
true了(真实开发中,这个是有大作用的)。
使用:
父组件的数据改变,会导致 子组件 在没有必要的情况下从新加载页面,所以可以使用 该周期函数,进行判断,看看子组件是否要进行更新;(写在子组件中),但目前判断的结果是 一致的,就是不管怎么弄, nextProps 和 this.props 的数据是一样的,导致无法判断;
shouldComponentUpdate (nextProps, nextState, nextContent) {
console.log(nextProps)
console.log(this.props)
if (nextProps.content !== this.props.content) {
console.log('不一样')
return true;
}
console.log('一样')
return false
}
getSnapshotBeforeUpdate()
概念:
组件的值发送变化后,但还没有更新到 DOM 之前触发,他可以 从数据中获取以下信息,传递给下一个生命周期 componentDidUpdate();
语法:
getSnapshotBeforeUpdate(prevProps, prevState) {
// prevProps 父组件传递的值
// prevState 组件state 中的值
return snapshot / null;
}
返回的值在 componentDidUpdata 生命周期中的第三个参数,可以获取到;
componentDidUpdate()
概念:
在组件更新之后执行,它是组件更新的最后一个环节。首次渲染不会执行此方法。
语法:
componentDidUpdate(prevProps, prevState, snapshot) {
// prevProps: 父组件传递的值
// prevState: 组件的数据
// snapshot: 上个生命周期传递过来的值
}
注意:
如果组件实现了 getSnapshotBeforeUpdate() 生命周期(不常用),则它的返回值将作为 componentDidUpdate() 的第三个参数 “snapshot” 参数传递。否则此参数将为 undefined 。
3. 销毁阶段
componentWillUnmount()
概念:
会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,
这个函数时组件从页面中删除的时候执行,例如我们删除组件中的内容时,会触发
语法:
componentWillUnmount () {
xxx
}
15. 动画钩子
1. 下载
React 和 Vue 一样,提供了一系列的 动画钩子函数,可以实现 进出场动画;
下载:
npm install react-transition-group --save
组成:
- Transition
- CSSTransition : css 动画钩子函数
- SwitchTransition
- TransitionGroup
步骤:
1,导入需要的包;
2,写必要的标签,包裹需要实现动画的元素;
3,写需要的参数;
4,书写 css 样式;
CSSTransition 参数介绍:
| in | 开始动画的开关,一般为某个控制动画的参数 |
|---|---|
| timeout | 实现动画的时间(毫秒) |
| classNames | 动画钩子函数的开头名字 (.xx-enter-done) |
| key | 当实现循环中的元素的动画时,就需要写 key 值; |
动画钩子函数:
.xx-enter | 入场前 |
|---|---|
.xx-enter-active | 入场时 |
.xx-enter-done | 入场后 |
.xx-exit | 出场前 |
.xx-exit-active | 出场时 |
.xx-exit-done | 出场后 |
如果不设置 出场后 的样式,动画会结束后会变成 开始的样式;不能保存出场的状态;
2. 单元素使用
单个元素使用,只要导入 CSSTransition 就可以了;
Move.js
import React, { Component, Fragment } from 'react'
// 1. 导入所需的文件
import { CSSTransition } from 'react-transition-group'
import './App.css'
class Move extends Component {
constructor (props) {
super(props)
this.state = {
isShow: true
}
this.moveInfo = this.moveInfo.bind(this)
}
render () {
return (
<Fragment>
// 2. 使用标签包裹
<CSSTransition
in={this.state.isShow}
timeout={2000}
classNames="move">
<div>
我有动画效果
</div>
</CSSTransition>
<input onClick={this.moveInfo} type="button" value="我是按钮" />
</Fragment>
)
}
moveInfo () {
let isShow = this.state.isShow
this.setState({
isShow: !isShow
})
}
}
export default Move
3,动画钩子函数
App.css
/* 入场前 */
.move-enter { opacity: 0; }
/* 入场时 */
.move-enter-active { opacity: 1; transition: opacity 2s; }
/* 入场后 */
.move-enter-done { opacity: 1; }
.move-exit { opacity: 1; }
.move-exit-active { opacity: 0; transition: opacity 2s; }
.move-exit-done { opacity: 0; }
3. 多个元素使用
多个元素就要导入 TransitionGroup(包裹整个元素) 和 CSSTransition(包裹单个元素)了。
Naruto.js
import React, { Component, Fragment } from 'react'
import './App.css'
import Lists from './Lists'
import Move from './Move'
// 1. 导入包
import { CSSTransition, TransitionGroup } from 'react-transition-group'
class Naruto extends Component {
// 数据
constructor (props) {
super(props) // 调用父类的构造函数,固定写法
this.state = {
inputValue: '',
list: []
}
}
// jsx 语法 ( 模板
render () {
return (
<Fragment>
<label htmlFor="inputId">添加学习忍术:</label>
<input
id="inputId"
value={this.state.inputValue}
onChange={this.inputChange.bind(this)}
ref={(input) => { this.input = input }}
/>
<button onClick={this.addList.bind(this)}>学习忍术</button>
<ul ref={(ul) => { this.ul = ul }}>
// 2. 使用标签包裹整个实现动画的元素
<TransitionGroup>
{
this.state.list.map((item, index) => {
return (
// 3. 使用标签,包裹实现动画的元素,并设置参数
<CSSTransition
timeout={2000}
classNames="move"
key={item + index}>
<Lists
key={item + index}
content={item}
index={index}
deleteList={this.deleteList.bind(this)}
date={'4个月'}
/>
</CSSTransition>
)
})
}
</TransitionGroup>
</ul>
<Move />
</Fragment>
)
}
inputChange () {
// 获取 ref 的值
const data = this.input.value
// 赋值到 this.start 中
this.setState({
inputValue: data
})
}
// 点击按钮添加数据
async addList () {
// 如果为空,提醒添加内容
if (!this.state.inputValue) {
return alert('请输入内容')
}
await this.setState({
list: [...this.state.list, this.state.inputValue],
inputValue: ''
})
}
// 点击删除
deleteList (index) {
// react 不能直接修改 state 中的数据,要拿出来后,再进行修改,在赋值进去
// 获取值
const lists = this.state.list;
// 修改值
lists.splice(index, 1);
// 把处理后的值,放到 state 中
this.setState({
list: lists
})
}
}
export default Naruto
4,设置动画钩子函数,和 List.js 是一样的,就不写了。
注意:
这个方法是比较适合添加订阅的地方。如果添加了订阅,请不要忘记在 componentWillUnmount() 里取消订阅 。
学习视频:技术胖哔哩哔哩视频
Redux
1. 基础:
概念:
Redux 是一个用来 管理管理数据状态 和 UI状态 的JavaScript 应用工具。随着JavaScript单页应用(SPA)开发日趋复杂,JavaScript需要管理比任何时候都要多的state(状态),Redux就是降低管理难度的。
工作流程:
图片来自 技术胖 的视频中:

React Components: 组件;
ActionCreaturs: 操作数据的管理人员;
Store: 数据存放中心;
Reducers:数据管理 / 查找中心;
2. 创建一个基础的 Redux 仓库
安装 Redux:
npm i --save redux
创建 store 仓库:
在项目 src 文件夹下创建一个 store 的文件夹,存放 redux 相关的文件;
index.js (项目的 store 文件)是文件夹的入口文件:
src > store > index.js
// 导入 redux 方法
import { createStore } from 'redux'
// 创建数据存储的仓库
const store = createStore()
export default store
创建 reducer 管理功能模块:
概述:
他有管理 store 中数据的功能,但只是的功能模块,需要有人去使用它;
在 store文件夹 下创建 reducer.js 文件:
src > store > reducer.js
// 默认数据
const defaultState = {}
// state 是项目管理的数据信息,一开始没有,使用一个默认值
const fn = (state = defaultState, action) => {
return state
}
/*
如果直接暴露个箭头函数,会出现以下警告,所以我改成把 箭头函数赋给变量,然后导出变量
Assign arrow function to a variable before exporting as module default import/no-anonymous-default-export
*/
export default fn
将 reducer 导入 store 中,进行挂载
src > store > index.js
// 导入 redux 方法
import { createStore } from 'redux'
// 导入 reducer.js
import reducer from './reducer'
// 创建数据存储的仓库
const store = createStore(reducer)
export default store
在 组件中获取数据:
存放数据:我们把数据存放到 reducer.js 中,然后在组件中获取:
src > store > reducer.js
// 管理 redux 功能模块,有管理数据的功能
// 默认数据
const defaultState = {
inputValue: '请输入内容',
list: [
'学习技术胖的Redux视频啊',
'看面试题,看到 作用域链 和 闭包'
]
}
// 暴露方法函数
const fn = (state = defaultState, action) => {
// state 是整个项目中需要管理的数据,暂时没有数据,用 空对象 代替
return state
}
export default fn
获取数据:然后在 组件 中导入 store 文件:
src > App.js
import React, { Component, Fragment } from 'react';
import './App.css'
import 'antd/dist/antd.css'
import { Button, Input, List } from 'antd'
// 1. 导入
import store from './store/index'
class App extends Component {
constructor (props) {
super(props)
// 2. 赋值给 this.state
this.state = store.getState()
}
render() {
return (
<Fragment>
<div className="content">
<div className="header">
<Input placeholder={this.state.inputValue} />
<Button type="primary">添加任务</Button>
</div>
<div className="list">
<List
bordered
dataSource={this.state.list}
renderItem={item => (
<List.Item>
{item}
</List.Item>
)}
/>
</div>
</div>
</Fragment>
);
}
}
export default App
在控制台打印出来为:

总结:
- 创建 store 和 reducer 文件;
- 然后在 store 中挂载 reducer;(挂载功能)
- 然后在 reducer 中存放数据;
- 在 组件 中导入文件;
- 然后获取数据
store.getState()。
谷歌浏览器 Redux DevTools 插件:
可以看到项目中的 redux ,安装后,要进行配置,把
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
复制到项目中的 store 文件中
src > store > index.js
// redux 数据存放中心
import { createStore } from 'redux'
// 导入 reducer 把他传入 store 中
import reducer from './reducer'
// 创建数据存储仓库
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
export default store
3. 修改 Redux 中的数据
上面我们在 组件 中获取到 redux 中的数据,现在我们要通过 组件 修改 redux 中的数据:
1. 组件发送数据到 store 中:
1,给 组件 绑定事件,获取 input 输入框的数据,然后发送 数据给 store:
我们创建了一个 管理人员 action 来携带数据给 store,
class App extends Component {
constructor (props) {
}
render() {
<Fragment>
<Input
placeholder={this.state.inputValue}
onChange={this.changeInputValue}
/>
</Fragment>
}
changeInputValue (e) {
// 获取 input 中的字符串
const data = e.target.value
// 1. 创建Action, store 的管理人员
const action = {
type: 'changeInputValue', // action 的名字,标记数据
value: data
}
// 2. 发送数据到 store 中
store.dispatch(action)
}
}
最好是要设置一个 防抖设置,因为你在 input 中每点击键盘就会触发事件,进行操作 store
这样会很耗费性能的,所以我们可以在 change 事件中设置一个定时器,每次触发事件 change时,先清空定时器,带从新开启一个定时器 。
2. store 自动推送数据到 reducer 中:
store 只是一个仓库,他并没有管理能力,他会把接收到的 state 数据自动转发给 Reducer。
我们可以在 Reducer 中输出看看:
src > store > reducer.js
const fn = (state = defaultState, action) => {
/*
state: 指的是原始仓库里的状态。
action:指的是 action 新传递的状态 */
console.log(state)
console.log(action)
return state
}
export default fn
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0pWWQmtX-1609134648795)(H:\笔记本\images\React\red 中输入数据.png)]](/https://i-blog.csdnimg.cn/blog_migrate/2b049ed1dd97c646ee089c606773210a.png#pic_center)
注意: 记住:reducer 里只能接收 state,不能改变 state 。
所以,我们接收到数据后,不对原数据进行修改,我们可以 拷贝一份,然后进行修改:
src > store > reducer.js
const fn = (state = defaultState, action) => {
/* store 没有处理数据的功能,他会自动转发到 reducer 中。
注意:Reducer里只能接收state,不能改变state。) */
// 判断接收的类型是否是我们传递过来的类型
if (action.type === 'changeInputValue') {
// 把原始数据拷贝一份
let newState = JSON.parse(JSON.stringify(state))
// 修改拷贝数据的值 = 传递 input 中的值
newState.inputValue = action.value
return newState
}
return state
}
3. 组件获取新的 store 数据:
在组件中创建一个函数,获取 store 中的数据,使用 store.subscribe(函数名) 触发函数;就像 vue 中的 订阅消息,然后等待 消息发布,就会触发;
src > store > App.js
1,创建函数
storeChange () {
// 修改组件中的数据
this.setState(store.getState())
}
2,进行消息的订阅
constructor (props) {
super(props)
this.setState = this.setState.bind(this)
// 订阅
store.subscribe(this.setState)
}
本文详细介绍了React的基础知识,包括脚手架安装、Hello React、JSX语法、数据绑定与响应式设计、事件处理、循环渲染、组件拆分、父子组件通信以及React的生命周期等核心概念。此外,还探讨了Redux的基础用法和数据管理,以及如何调试React应用。通过本文,读者可以系统地学习React开发。

1253

被折叠的 条评论
为什么被折叠?



