1. React 组件介绍

了解 React 组件作用,和创建组件的方式
官方解释:

  • 组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。

前端开发:

  • 将页面按照界面功能进行拆分,每一块界面都拥有自己的独立逻辑,这样可以提高项目代码的可维护性。

image.png
React 创建组件:

  1. 使用函数 function
  2. 使用类 class

    2. 函数组件

    目标:掌握在 React 中使用函数创建组件,使用组件。
    大致步骤:
  • 什么是函数组件
  • 定义函数组件
  • 使用函数组件

具体内容:
1)什么是函数组件?

  • 使用 JS 函数(普通,箭头)创建的组件

2)定义函数组件

  • 语法约定
    • 函数名称首字母必需大写,React 据此来区分组件和 HTML 元素
    • 函数必须有返回值,表示该组件的 UI 结构,如果不渲染任何内容可返回null

// 普通函数
function Header() {
return

头部组件
;
}
// 箭头函数
const Footer = () => {
return
底部组件
;
};
// 不渲染内容
const Loading = () => {
const loading = false;
return loading ?
正在加载…
: null;
};
3)使用组件

  • 函数的名称就是组件名称,使用组件就是把组件名称当标签使用即可。

import ReactDom from ‘react-dom’;

// 普通函数
function Header() {
return

头部组件
;
}

// 箭头函数
const Footer = () => {
return
底部组件
;
};

// 加载组件,不渲染内容
const Loading = () => {
const loading = false;
return loading ?
正在加载…
: null;
};

// 根组件
const App = () => {
return (
<>



</>
);
};

ReactDom.render(, document.getElementById(‘root’));
总结:

  • 创建函数组件,首字母大写,需要返回值,不渲染就返回 null
  • 使用函数组件,组件名称当作标签使用即可

    3. 类组件-复习 class 语法

    回顾 class 语法定义类属性和函数,回顾 extends 语法继承父类。
    大致步骤:

  • 掌握 class 定义类,定义属性,定义函数。

  • 掌握 extends 继承父类。

具体内容:

  1. 掌握 class 定义类,定义属性,定义函数

// 动物
class Animal {
address = ‘地球’;
eat() {
console.log(‘吃’);
}
}

  1. 掌握 extends 继承父类

// 猫
class Cat extends Animal {
run() {
console.log(‘跑’);
}
}

const cat = new Cat();
cat.run(); // 跑
cat.eat(); // 吃
console.log(cat.address); // 地球
总结: class创建类,extends继承类,可以使用父类的属性和函数。

4. 类组件-基本使用步骤

掌握 React 的类组件写法
大致步骤

  • 什么是类组件
  • 定义类组件
  • 使用类组件

具体内容

  1. 什么是类组件?
    • 使用class语法创建的组件就是类组件
  2. 定义类组件
    • 约定:类名首字母必需大写
    • 约定:必须继承React.Component父类
    • 约定:必需有render函数,返回 UI 结构,无渲染可返回 null

import { Component } from ‘react’;

class Header extends Component {
render() {
return

头部组件
;
}
}

  1. 使用类组件
    • 类名称就是组件名称,使用组件就是把组件名称当标签使用即可。

import { Component } from ‘react’;
import ReactDom from ‘react-dom’;

// 头部
class Header extends Component {
render() {
return

头部组件
;
}
}

// 根组件
class App extends Component {
render() {
return (
<>

</>
);
}
}

ReactDom.render(, document.getElementById(‘root’));
总结:

  • 使用class定义类,使用extends继承React.Component完成类组件定义
  • 类名首字母大写,必须有render函数返回 UI 结构,无渲染可返回null
  • 使用的时候把类名当作标签使用即可

    5. 类组件-组件抽离

    理解组件抽离目的,掌握抽离组件方式。
    大致步骤:

  • 思考,如果所有组件写在一个文件好维护吗?

    • 不好维护,代码写在一起。
  • 抽离组件
    • 定义一个js或者jsx文件定义组件默认导出
    • 使用组件导入即可,当作标签使用。

具体操作:

  1. 新建 src/components/Header.jsx 类组件,新建 src/components/Footer.jsx 函数组件

import { Component } from ‘react’;
class Header extends Component {
render() {
return

头部组件
;
}
}
export default Header;
const Footer = () => {
return
头部组件
;
};
export default Footer;

  1. 新建 src/App.jsx 组件, 导入Header Footer组件使用。

import { Component } from ‘react’;
import Header from ‘./components/Header.jsx’;
import Footer from ‘./components/Footer.jsx’;
class App extends Component {
render() {
return (
<>


内容

</>
);
}
}

  1. index.js 使用 App 根组件

import ReactDom from ‘react-dom’;
import App from ‘./App.jsx’;
ReactDom.render(, document.getElementById(‘root’));
总结:

  • 建议定义组件使用单独的文件
  • 思考,何时使用类组件,何时使用函数组件?

    6. 类组件-无状态组件和有状态组件和

    理解无状态组件和有状态组件概念
    大致步骤:

  • 理解什么是无状态组件

  • 理解什么是有状态组件
  • 它们的区别是什么,如何选择使用

具体内容:

  1. 无状态组件
    • 组件本身不定义状态,没有组件生命周期,只负责 UI 渲染。
    • React16.8之前的函数组件都是无状态组件,Hooks 出现后函数组件也可以有状态。
  2. 有状态组件
    • 组件本身有独立数据,拥有组件生命周期,存在交互行为。
    • class 组件可以定义组件自己的状态,拥有组件生命周期,它是有状态组件。
  3. 它们的区别
    • 无状态组件由于没有维护状态只做渲染,性能较好。有状态组件提供数据和生命周期,能力更强。
  4. 如何去选择
    • React16.8之前,组件不需要维护数据只渲染就使用函数组件,有数据和交互使用类组件。你需要去判断,有心智负担。
    • React16.8之后,Hooks出现给函数提供状态,建议使用函数组件即可。

大家可以思考下面页面各个组件使用什么类型组件:
image.png
总结:

  • 组件本身没有状态就是无状态组件,组件本身提供状态就是有状态组件。
  • 16.8 之前,无状态组件使用函数组件,有状态组件使用类组件。16.8 之后,统一可使用函数组件。
  • React 没有说完全取代类组件,老项目中还是类组件居多,我们有必要学习下它的具体用法。

    7. 类组件-定义状态

    掌握类组件中状态的定义与使用
    大致步骤:

  • 定义state属性定义组件状态,属于组件自己的数据,它的值是个对象。

  • 使用state的时候通过this去访问即可,例如:this.state.xxx。
  • 数据发生变化,驱动视图更新。

具体代码:
import { Component } from ‘react’;

class App extends Component {
// 状态
state = {
title: ‘数码产品’,
list: [‘电脑’, ‘手机’, ‘相机’],
};
render() {
return (
<>

{this.state.title}



    {this.state.list.map((item) => {
    return
  • {item}
  • ;
    })}

</>
);
}
}
export default App;
数据驱动视图演示:
React 组件 - 图3
总结:

  • 定义state属性,值是对象存储数据,this.state.xxx使用数据,数据驱动试题更新。

    8. 类组件-绑定事件

    掌握类组件中绑定事件的方式,和获取事件对象的方式。
    大致步骤:

  • 在类中声明事件处理函数,在标签上使用on+事件名称={处理函数}的方式绑定事件,事件名称需要遵循大驼峰规则。

  • 处理函数默认的参数就是事件对象,可以使用事件对象处理默认行为和事件冒泡。

具体代码:
import { Component } from ‘react’;

class App extends Component {
// 状态
state = {
count: 0,
};
// 事件处理函数
handleClick(e) {
// 默认行为
e.preventDefault();
// 事件冒泡
e.stopPropagation();
console.log(‘handleClick’);
}
handleMouseEnter() {
console.log(‘handleMouseEnter’);
}
render() {
return (
<>


计数器:{this.state.count}


</>
);
}
}
export default App;
总结:

  • 绑定事件的方式和原生的方式一致,使用 on+事件名称={处理函数} 方式绑定
  • 事件名称使用大驼峰规则,例如:onClick onMouseEnter , 处理函数默认传参为事件对象。

    9. 类组件-发现 this 问题

    发现事件处理函数中 this 获取不到问题和原因
    大致步骤:

  • 在事件处理函数中打印 this.state.count 发现报错,this 是个undefined。

  • 演示函数调用对 this 指向的影响,得出函数谁调 this 就执行谁。
  • 找出原因:处理函数不是通过组件去调用的,导致出现 this 不是组件问题。

具体代码:

  1. 发现this是undefined

import { Component } from ‘react’;

class App extends Component {
// 状态
state = {
count: 0,
};
// 事件处理函数
handleClick(e) {
console.log(e);
// Uncaught TypeError: Cannot read properties of undefined (reading ‘state’)
console.log(this.state.count);
}
render() {
return (
<>

计数器:{this.state.count}




</>
);
}
}
export default App;

  1. 演示处理函数调用对 this 的影响

const obj = {
name: ‘tom’,
say() {
console.log(this);
},
};
obj.say(); // 打印:{name: ‘tom’, say: function}
const say = obj.say;
say(); // 打印:window对象 严格模式

  1. 问题原因
    • 类组件声明的处理函数,赋值给 on+事件名称 属性,调用的时候不是通过组件调用的。

总结:

  • 这个 this 问题是 JS 原生就存在,要解决这个问题需要通过 JS 的方式。

    10. 类组件-处理 this 问题

    掌握通过 绑定箭头函数 bind 声明箭头函数 三种方式解决 this 问题
    大致步骤:

  • 通过绑定箭头函数解决 this 问题

  • 通过 bind 解决 this 问题
  • 通过声明箭头函数解决 this 问题

具体内容:

  1. 通过绑定箭头函数解决 this 问题

import { Component } from “react”;

class App extends Component {
// 状态
state = {
count: 0,
};
// 事件处理函数
handleClick(e) {
console.log(e)
console.log(this.state.count)
}
render() {
return (
<>

计数器:{this.state.count}


+

</>
);
}
}
export default App;

  1. 通过 bind 解决 this 问题

import { Component } from “react”;

class App extends Component {
// 状态
state = {
count: 0,
};
// 事件处理函数
handleClick(e) {
console.log(e)
console.log(this.state.count)
}
render() {
return (
<>

计数器:{this.state.count}


+

</>
);
}
}
export default App;

  1. 通过声明箭头函数解决 this 问题

import { Component } from “react”;

class App extends Component {
// 状态
state = {
count: 0,
};
// 事件处理函数
+ handleClick = (e) => {
console.log(e)
console.log(this.state.count)
}
render() {
return (
<>

计数器:{this.state.count}




</>
);
}
}
export default App;
总结:

  • 三种方式任何一种都可行,建议使用第三种,定义类中的事件处理函数统一使用箭头函数。

    11. 类组件-setState 使用

    掌握使用 setState 函数修改组件状态
    大致步骤:

  • React 类组件提供一个函数setState({需修改数据}),可以更新数据和视图。

  • 直接修改 state 中的数据是不会更新视图,演示简单数据,数组,对象的正确修改方式。

具体代码:

  1. 通过setState的来修改数据更新视图

import { Component } from ‘react’;

class App extends Component {
state = {
count: 0,
};
handleClick = () => {
// 修改数据
this.setState({
// key是要修改的数据名称,value是对应的新值
count: this.state.count + 1,
});
};
render() {
return (
<>

计数器:{this.state.count}




</>
);
}
}
export default App;

  1. 修改数组和修改对象的正确姿势

import { Component } from ‘react’;

class App extends Component {
state = {
count: 0,
user: {
name: ‘jack’,
age: 18,
},
list: [‘电脑’, ‘手机’],
};
handleClick = () => {
// 修改数据
this.setState({
// key是要修改的数据名称,value是对应的新值
count: this.state.count + 1,
});
};
updateList = () => {
// 修改列表
this.setState({
list: […this.state.list, ‘相机’],
});
};
updateUser = () => {
// 修改对象
this.setState({
user: {
…this.state.user,
name: ‘tony’,
},
});
};
render() {
return (
<>

计数器:{this.state.count}






商品:{this.state.list.join(‘,’)}





姓名:{this.state.user.name},年龄:{this.state.user.age}


</>
);
}
}
export default App;
总结:

  • 修改状态数据,使用setState修改可更新视图。

    12. 类组件-受控组件

    理解受控组件概念,掌握动态绑定表单元素。
    大致步骤:

  • 什么是受控组件?

  • 如何绑定表单元素

具体内容:

  1. 什么事受控组件
    • 表单元素的值被 React 中state控制,这个表单元素就是受控组件。
  2. 如何绑定表单元素,如:input:text input:checkbox

import { Component } from ‘react’;

class App extends Component {
state = {
mobile: ‘13811112222’,
isAgree: true,
};

changeMobile = (e) => {
this.setState({
mobile: e.target.value,
});
};

changeAgree = (e) => {
this.setState({
isAgree: e.target.checked,
});
};

render() {
return (
<>


value={this.state.mobile}
onChange={this.changeMobile}
type=”text”
placeholder=”请输入手机号”
/>


checked={this.state.isAgree}
onChange={this.changeAgree}
type=”checkbox”
/>
同意用户协议和隐私条款

</>
);
}
}
export default App;
总结:

  • 使用state的数据赋值给表单原生,通过onChange监听值改变修改 state 数据,完成表单元素的绑定。
  • 这种表单元素称为受控组件。

    13. 类组件-非受控组件

    理解非受控组件概念,掌握通过 ref 获取元素。
    大致步骤:

  • 什么是非受控组件?

  • 通过 ref 获取表单元素获取非受控组件的值

具体内容:

  1. 什么是非受控组件?
    • 没有通过 state 控制的表单元素,它自己控制自身的值,就是非受控组件
  2. 通过 ref 获取表单元素获取非受控组件的值

import { Component, createRef } from ‘react’;

class App extends Component {
// 获取非受控组件的值
// 1. 通过createRef创建一个ref对象
// 2. 给元素绑定ref属性值为创建的ref对象
// 3. 通过ref对象的current获取元素,再获取它的值
mobileRef = createRef();

getMobile = () => {
console.log(this.mobileRef.current.value);
};

render() {
return (
<>


{/ 没有被state控制的表单原生认为是非受控组件 /}



</>
);
}
}
export default App;

14. 类组件-综合案例-准备

能够把案例静态组件、样式文件、基础数据准备好
完整案例:

评论

0/100
发表评论

热门评论(3)

  • __RichManimage.png2021/10/12 10:10:23这阵容我喜欢😍靳东&闫妮,就这俩名字,我就知道是良心剧集…锁了🔒
  • 糖蜜甜筒颖2021/09/23 15:12:44突围神仙阵容 人民的名义第三部来了 靳东陈晓闫妮秦岚等众多优秀演员实力派 守护人民的财产 再现国家企业发展历程
  • 清风徐来image.png2021/07/01 00:30:51删除第一集看的有点费力,投入不了,闫妮不太适合啊,职场的人哪有那么多表情,一点职场的感觉都没有

大致步骤:

  • 准备样式文件
  • 准备静态组件
  • 在 App 中使用

具体代码:
index.css 样式
body {
margin: 0;
}
.comments {
background-color: #121212;
color: #eee;
padding: 24px;
width: 1000px;
margin: 0 auto;
}
.comm-head {
color: #eee;
font-size: 24px;
line-height: 24px;
margin-bottom: 24px;
}
.comm-head sub {
font-size: 14px;
color: #666;
margin-left: 6px;
bottom: 0.2em;
position: relative;
}

.comm-head span {
display: inline-block;
line-height: 1;
padding: 5px 16px;
font-size: 14px;
font-weight: normal;
border-radius: 12px;
background-color: rgba(255, 255, 255, 0.1);
color: #999;
cursor: pointer;
margin-left: 30px;
}
.comm-head span:hover,
.comm-head span.active {
color: #61f6ff;
}

.comm-list {
list-style: none;
padding: 0;
}
.comm-item {
display: flex;
margin-bottom: 24px;
}
.comm-item .avatar {
width: 48px;
height: 48px;
line-height: 48px;
border-radius: 24px;
display: inline-block;
cursor: pointer;
background-position: 50%;
background-size: 100%;
background-color: #eee;
}
.comm-item .info {
padding-left: 16px;
}
.comm-item .info p {
margin: 8px 0;
}
.comm-item .info p.name {
color: #999;
}
.comm-item .info p.vip {
color: #ebba73;
}
.comm-item .info p.vip img {
width: 14px;
vertical-align: baseline;
margin-left: 5px;
}
.comm-item .info p.time {
color: #666;
font-size: 14px;
display: flex;
align-items: center;
}

.comm-item .info .iconfont {
margin-left: 20px;
position: relative;
top: 1px;
cursor: pointer;
}
.comm-item .info .iconfont.icon-collect-sel {
color: #ff008c;
}
.comm-item .info .del {
margin-left: 20px;
cursor: pointer;
}
.comm-item .info .del:hover {
color: #ccc;
}

.comm-input {
border-radius: 6px;
padding: 18px;
background-color: #25252b;
}
.comm-input textarea {
border: 0;
outline: 0;
resize: none;
background: transparent;
color: #999;
width: 100%;
font-family: inherit;
height: auto;
overflow: auto;
}
.comm-input .foot {
display: flex;
justify-content: flex-end;
justify-items: center;
}
.comm-input .foot .word {
line-height: 36px;
margin-right: 10px;
color: #999;
}
.comm-input .foot .btn {
background-color: #ff008c;
font-size: 14px;
color: #fff;
line-height: 36px;
text-align: center;
border-radius: 18px;
padding: 0 24px;
cursor: pointer;
user-select: none;
}
src/components/Comment.jsx 评论组件
import React, { Component } from ‘react’;
import ‘./index.css’;

export default class Comment extends Component {
render() {
return (


评论





0/100

发表评论




热门评论(5)




);
}
}
src/App.jsx 根组件
import { Component } from ‘react’;
import Comment from ‘./components/Comment.jsx’;
class App extends Component {
render() {
return (
<>

</>
);
}
}
export default App;
总结:

  • 按照类组件的方式准备好基础结构和样式

    15. 类组件-综合案例-渲染

    在类组件 Comment.jsx 中完成初始化渲染
    大致步骤

  • 准备初始化数据:评论列表

  • 完成组件渲染

落地代码:

  1. 定义状态存储基础列表数据

state = {
// 当前用户
user: {
name: ‘清风徐来’,
vip: true,
avatar: ‘https://static.youku.com/lvip/img/avatar/310/6.png‘,
},
// 评论列表
comments: [
{
id: 100,
name: ‘__RichMan’,
avatar: ‘https://r1.ykimg.com/051000005BB36AF28B6EE4050F0E3BA6‘,
content:
‘这阵容我喜欢😍靳东&闫妮,就这俩名字,我就知道是良心剧集…锁了🔒’,
time: ‘2021/10/12 10:10:23’,
vip: true,
collect: false,
},
{
id: 101,
name: ‘糖蜜甜筒颖’,
avatar:
https://image.9xsecndns.cn/image/uicon/712b2bbec5b58d6066aff202c9402abc3370674052733b.jpg‘,
content:
‘突围神仙阵容 人民的名义第三部来了 靳东陈晓闫妮秦岚等众多优秀演员实力派 守护人民的财产 再现国家企业发展历程’,
time: ‘2021/09/23 15:12:44’,
vip: false,
collect: true,
},
{
id: 102,
name: ‘清风徐来’,
avatar: ‘https://static.youku.com/lvip/img/avatar/310/6.png‘,
content:
‘第一集看的有点费力,投入不了,闫妮不太适合啊,职场的人哪有那么多表情,一点职场的感觉都没有’,
time: ‘2021/07/01 00:30:51’,
vip: true,
collect: false,
},
],
};

  1. 完成列表渲染

render() {
const {comments, user} = this.state
return (


评论





0/100

发表评论



热门评论({comments.length})



    {comments.map(item=>{
    return (




  • {item.name}
    {item.vip?React 组件 - 图6: null}



    {item.time}
    {item.name === user.name ? 删除: null}


    {item.content}




  • )
    })}


);
}
总结:

  • 先在 state 中准备好初始化数据,然后 render 中使用数据渲染即可,注意下删除按钮权限控制。

    16. 类组件-综合案例-评论

    完成发表评论功能,输入字数限制。
    大致步骤:

  • 把输入框改成受控组件

  • 展示输入字数,和限制长度 100
  • 点击按钮进行发布,清空内容

落地代码:

  1. 把输入框改成受控组件

state = {
+ // 评论内容
+ content: ‘’,
changeContent = (e) => {
this.setState({
content: e.target.value,
});
};
value={content}
onChange={this.changeContent}
placeholder=”爱发评论的人,运气都很棒”
>

  1. 展示字数,限制输入


+
{content.length}/100

发表评论


changeContent = e => {
+ const value = e.target.value
+ if ( value.length <= 100 ) {
this.setState({
content: e.target.value.trim()
})
+ }
}

  1. 进行发布,完成后清空输入


{content.length}/100

+
发表评论


submit = () => {
if (this.state.content) {
// 准备评论对象
const item = {
id: Math.random(),
…this.state.user,
content: this.state.content,
time: new Date().toLocaleString(),
collect: false,
};
// 追加列表,清空输入
this.setState({
comments: [item, …this.state.comments],
content: ‘’,
});
}
};
总结:

  • 先通过受控组件绑定数据
  • 显示内容长度,在修改数据的时候限制长度
  • 通过修改setState追加评论,清除输入

    17. 类组件-综合案例-删除

    完成评论删除功能
    大致步骤

  • 绑定点击删除按钮的事件,定义删除处理函数。

  • 传入 ID,通过setState删除数组其中一项

落地代码:

  1. 绑定点击删除按钮的事件,定义删除处理函数


{item.time}
+ {item.name === user.name ? 删除: null}


delComment = () => {};

  1. 传入 ID 给处理函数,进行删除


{item.time}
+ {item.name === user.name ? this.delComment(item.id)} className=”del”>删除: null}


delComment = (id) => {
this.setState({
comments: this.state.comments.filter((item) => item.id !== id),
});
};
总结:

  • 传递额外参数可以使用箭头函数
  • 修改值,是删除一项后的新数组,一定要是新的值。

    18. 类组件-综合案例-收藏

    实现收藏评论功能
    大致步骤:

  • 引入字体图标

  • 准备收藏按钮和样式
  • 绑定点击处理函数,完成收藏逻辑

落地代码:

  1. 引入字体图标

public/index.html
href=”https://at.alicdn.com/t/font_2998849_vtlo0vj7ryi.css
rel=”stylesheet”
/>

  1. 准备收藏按钮和样式


{item.time}
+

  1. 绑定点击处理函数,完成收藏逻辑

onClick={() => this.collectComment(item.id)}
className={iconfont icon-collect${item.collect ? '-sel' : ''}}
>
// 收藏评论&取消收藏
collectComment = (id) => {
this.setState({
comments: this.state.comments.map((item) => {
if (item.id === id) {
return { …item, collect: !item.collect };
} else {
return item;
}
}),
});
};
总结:

  • 通过 id 修改对应评价的收藏状态

    19. 今日总结

    回顾当天知识
  1. 什么函数式组件?
    • 通过函数创建的组件,在 16.8 之前没有状态
  2. 什么是类组件?
    • 通过class创建的组件,可以定义状态,拥有声明周期。
  3. 什么是无状态组件和有状态组件?
    • 16.8 之前的函数组件都是无状态组件适合做单纯渲染,有状态组件适合做交互。
  4. 如何在类组件中定义状态?
    • 通过 state 属性进行定义,它是一个对象可以定义多个状态。
  5. 如何绑定事件?
    • 通过 on+事件名称={处理函数} 来绑定,事件名称遵循大驼峰
  6. 如果解决处理函数中的 this 问题?
    • 三种方式:绑定位置使用箭头函数包裹处理函数,绑定位置使用 bind 绑定 this,定义位置使用箭头函数。
  7. 如果修改 state 状态数据?
    • 使用 setState 函数进行修改,遵循用新的值去覆盖原有的值。
  8. 什么是受控组件?
    • 表单元素的值被state数据控制,值改变需要通过onChange去监听,然后修改 state 的值
  9. 什么是非受控组件?
    • 表单元素的值它自己控制,值需要通过 ref 进行获取元素后获取
  10. 安装 react 开发插件
    • fmkadmapgofadopljbjfkapdkoienihi_4.20.1_chrome.zzzmh.cn.crx