react 组件化
Ant-Design
yarn add antd -S
基础使用:
import React, { Component } from 'react'
import { Button } from 'antd'
import 'antd/dist/reset.css'
export default class Ant extends Component {
render() {
return (
<div>
<Button type="primary">123</Button>
</div>
)
}
}
效果:
高级设置
按需引入
聪明组件和傻瓜组件
也可以认为是容器组件和展示组件
组件化设计的优点:
- 逻辑和内容分离
- 重用性高
- 复用性高
- 容易测试
:::tip
类型需要对应起来
this.state = {
comments: [],
}
this.setState({
comments: [
{
id: 1,
author: 'facebook',
content: 'react非常好',
},
{
id: 2,
author: '尤雨溪',
content: '我觉得vue更好',
},
],
})
:::
组件的使用和傻瓜组件及聪明组件的使用:
import React, { Component } from 'react'
import Comment from './Comment'
export default class CommentList extends Component {
constructor(props) {
super(props)
this.state = {
comments: [],
}
}
componentDidMount() {
setTimeout(() => {
this.setState({
comments: [
{
id: 1,
author: 'facebook',
content: 'react非常好',
},
{
id: 2,
author: '尤雨溪',
content: '我觉得vue更好',
},
],
})
}, 1000)
}
render() {
return (
<div>
{this.state.comments.map((item, index) => {
return (
<Comment key={index} comment={item.content}></Comment>
)
})}
</div>
)
}
}
import React, { Component } from 'react'
export default class Comment extends Component {
constructor(props) {
super(props)
}
render() {
return <div>{this.props.comment}</div>
}
}
上面的 Comment 就是一个傻瓜式组件
而 CommentList 则是一个聪明式组件。
前者只负责展示 -> 按道理只需要使用 function 组件就行
而后者需要改变 state ,所以最好使用 类组件。
组件渲染的优化方案
react 并不会帮你判断新的内容和旧的内容是不是一样的,所以我们需要手动帮助 react 来优化性能。
比如使用shouldComponentUpdate
来帮助react判断是否需要更新。
此外,我们可以使用 PureComponent
,但注意一个问题,PureComponent
使用的是浅比较,无法比较数组或者对象之类。
解决方法:
字段多的时候:
高阶组件的方法:
组件组合而非继承
react : 我们推荐使用组件的组合来实现组件的重用
import { Button } from 'antd'
import React, { Component } from 'react'
function Dialog(props) {
// 匿名插槽
return (
<div style={{ border: `3px solid ${props.color || 'red'}` }}>
{props.children}
{props.btn}
</div>
)
}
function WelcomeDialog() {
const WelBtn = <Button type="primary">欢迎!</Button>
return (
<Dialog color="blue" btn={WelBtn}>
<h3>welcome</h3>
<p>ljj</p>
</Dialog>
)
}
export default class Compond extends Component {
render() {
return (
<div>
<WelcomeDialog />
</div>
)
}
}
WelcomeDialog
就是整合了 Dialog
从而实现了组件的复用。
效果:
我们可以在使用到 Dialog
的地方一直重复使用,从而达到优化代码的目的。
HOC高阶组件
高阶组件 -> 高阶函数:接受一个组件或者多个组件,返回一个组件
组件设计的目的:保证组件功能上的单一性了
高阶组件是为了解决代码的重用性!
y = kx + b
// y 就是高阶组件 x 就是普通组件
案例1:
案例2:
import React, { Component } from 'react'
class Hoc extends Component {
render() {
return (
<div>
<h3>
{this.props.type}
{this.props.name}
</h3>
</div>
)
}
}
// 高阶组件
const highOC = (Comp) => {
const NewCom = (props) => {
const attr = { type: '高阶组件' }
return <Comp {...props} {...attr}></Comp>
}
return NewCom
}
export default highOC(Hoc)
案例3:
与此相关的有高阶函数:
就是传入的是一个函数或者返回的是一个函数
本质上高阶组件就是一个高阶函数
详细讲解高阶组件
关于高阶组件的讲解:
-
我们为什么需要使用高阶组件?
react 使用高阶组件能够帮助我们写出易于维护、高复用性的代码
-
高阶组件到底是什么?
传入一个组件,返回一个组件(高级)
-
如何实现高阶组件?
方法一:属性代理
使用属性代理的好处: 将常用的方法独立出来并且复用
方法二:反向继承
- 高阶组件的应用
- 代码复用
- 权限的控制
- 打印日志 -> 在每一个方法调用的时候添加日志
高阶组件 — 打印日志
只要某个组件需要打印公共的日志,就可以使用此组件。
import React, { Component } from 'react'
class Hoc extends Component {
render() {
return (
<div>
<h3>
{this.props.type}
{this.props.name}
</h3>
</div>
)
}
}
// 高阶组件
const highOC = (Comp) => {
const NewCom = (props) => {
const attr = { type: '高阶组件' }
return <Comp {...props} {...attr}></Comp>
}
return NewCom
}
// 打印日志的高阶组件
const withLog = (Comp) => {
console.log(Comp.name + '渲染了')
const NewHoc = (props) => {
return <Comp {...props}></Comp>
}
return NewHoc
}
// 链式调用
export default highOC(withLog(withLog(Hoc)))
结果:
装饰器处理链式调用
使用 vite 创建的项目可以进行如下配置达到使用装饰器的效果:
第一步:
pnpm add @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties -D
第二步:
在vite.config.js
中加入:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react({
babel: {
plugins: [
["@babel/plugin-proposal-decorators", { legacy: true }],
["@babel/plugin-proposal-class-properties", { loose: true }],
],
},
}),
],
});
第三步:
在jsconfig.json
中加入:
{
"compilerOptions": {
"experimentalDecorators": true,
}
}
然后即可使用装饰器,ts 中同理。
高阶组件 — 页面复用
案例:
MovieA:
import React, { Component } from 'react'
import { withFetch } from '../HOC/WithFetch'
import MovieList from './MovieList'
class MovieA extends Component {
render() {
return (
<div>
<MovieList movies={this.props.data}></MovieList>
</div>
)
}
}
export default withFetch('A')(MovieA)
MovieB:
import React, { Component } from 'react'
import { withFetch } from '../HOC/WithFetch'
import MovieList from './MovieList'
class MovieB extends Component {
render() {
return (
<div>
<MovieList movies="this.props.data"></MovieList>
</div>
)
}
}
export default withFetch('B'(MovieB))
MovieList:
import React, { Component } from 'react'
export default class MovieList extends Component {
render() {
return (
<div>
<ul>
{this.props.movies.map((item, index) => {
return <li key={index}>item</li>
})}
</ul>
</div>
)
}
}
高阶组价 — 权限控制
案例:
PageA:
import React, { Component } from 'react'
import { withAdminAuth } from '../HOC/withAdminAuth'
@withAdminAuth
class PageA extends Component {
render() {
return <div>PageA</div>
}
}
export default PageA
PageB:
import React, { Component } from 'react'
export default class PageB extends Component {
render() {
return <div>PageB</div>
}
}
withAdminAuth:
import React, { Component } from 'react'
export const withAdminAuth = (Comp) => {
return class extends React.Component {
constructor(props) {
super(props)
this.state = {
isAdmin: true,
}
}
componentDidMount() {
// 从后端获取用户的权限
const currentRole = 'Admin'
this.setState({
isAdmin: currentRole === 'Admin',
})
}
render() {
if (this.props.isAdmin) {
return <Comp {...this.props}></Comp>
} else {
return <div>您不是管理员~</div>
}
}
}
}
结果:
组件通信Context
之前我们学习过父子之间的通信,可以复习一下。
但是这样传递有一个缺陷:就是每次传递都需要依赖 props
,这样只能一层一层地传递。
而利用 Context
我们不需要为每一层组件手动添加 props
,就可以实现在组件之间传递数据。
Context 设计主要是为了 全局 的数据传递。 -> 相当于 vue 的 provide 和 inject
案例:原来的情况下我们将数据传递的话,是需要按照下面的方式传递的:
import React, { Component } from 'react'
function Toolbar(props) {
return <TimeBtn name={props.name}></TimeBtn>
}
function TimeBtn(props) {
return <div>{props.name}</div>
}
export default class LearningContext extends Component {
render() {
return <Toolbar name="react"></Toolbar>
}
}
但是我们可以使用 Context
直接使得 TimeBtn
获得我们想要传递的内容。
1.函数式组件代码:
- 使用
createContext()
- 使用
{useContext}
import React, { Component, useContext } from 'react'
const ThemeContext = React.createContext()
function Toolbar(props) {
return <TimeBtn name={props.name}></TimeBtn>
}
function TimeBtn(props) {
const context = useContext(ThemeContext)
console.log(context)
return <div>{context}</div>
}
export default class LearningContext extends Component {
render() {
return (
<ThemeContext.Provider value="情报">
<Toolbar name="react"></Toolbar>
</ThemeContext.Provider>
)
}
}
2.类式组件代码:
- 使用
createContext()
- 使用
static contextType =
import React, { Component } from "react";
const ThemeContext = React.createContext()
class TimeBtn extends Component {
static contextType = ThemeContext;
render() {
const value = this.context;
return <div>{value}</div>;
}
}
3.使用 Consumer
- 只使用
createContext()
import React, { Component, useContext } from 'react'
const ThemeContext = React.createContext()
function Toolbar(props) {
return <TimeBtn name={props.name}></TimeBtn>
}
function TimeBtn(props) {
return (
<ThemeContext.Consumer>
{(value) => {
return <div>{value}</div>
}}
</ThemeContext.Consumer>
)
}
export default class LearningContext extends Component {
render() {
return (
<ThemeContext.Provider value="情报">
<Toolbar name="react"></Toolbar>
</ThemeContext.Provider>
)
}
}
上面的三个结果都是:
高阶组件抽离Context
代码:
import React, { Component } from 'react'
const ThemeContext = React.createContext()
// Provider
const withProvider = (Comp) => {
return class extends Component {
render() {
return (
<ThemeContext.Provider value="react">
<Comp></Comp>
</ThemeContext.Provider>
)
}
}
}
// Consumer
const withConsumer = (Comp) => {
return class extends Component {
render() {
return (
<ThemeContext.Consumer>
{(value) => {
return <Comp value={value}></Comp>
}}
</ThemeContext.Consumer>
)
}
}
}
export { withConsumer, withProvider }
import React, { Component, useContext } from 'react'
import { withConsumer, withProvider } from '../HOC/withContext'
@withConsumer
class TimeBtn extends Component {
render() {
return <div>123 {this.props.value}</div>
}
}
@withProvider
class LearningContext extends Component {
render() {
return <TimeBtn></TimeBtn>
}
}
export default LearningContext
效果:
antd表单组件基础使用
高性能表单控件,自带数据域管理。包含数据录入、校验以及对应样式。
import React from 'react'
import { Button, Checkbox, Form, Input } from 'antd'
const Antdform = () => {
const onFinish = (values) => {
console.log('Success:', values)
}
const onFinishFailed = (errorInfo) => {
console.log('Failed:', errorInfo)
}
return (
<Form
name="basic"
labelCol={{
span: 8,
}}
wrapperCol={{
span: 16,
}}
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
autoComplete="off"
>
<Form.Item
label="Username"
name="username"
rules={[
{
required: true,
message: 'Please input your username!',
},
]}
>
<Input />
</Form.Item>
<Form.Item
label="Password"
name="password"
rules={[
{
required: true,
message: 'Please input your password!',
},
]}
>
<Input.Password />
</Form.Item>
<Form.Item
name="remember"
valuePropName="checked"
wrapperCol={{
offset: 8,
span: 16,
}}
>
<Checkbox>Remember me</Checkbox>
</Form.Item>
<Form.Item
wrapperCol={{
offset: 8,
span: 16,
}}
>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
)
}
export default Antdform
根据实际情况我们要使用高阶组件催表单进行封装,以防重复使用。