目录
九、高阶组件和组件补充
1、认识高阶组件
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './layout/App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App name="黄婷婷"/>);
import React from "react";
/**
* 1、高阶函数的维基百科定义:至少满足以下条件之一
* - 接收一个或多个函数作为输入
* - 输出一个函数
* 2、那么什么是高阶组件呢
* - 高阶组件的英文是Higher-Order Components,简称为HOC
* - 官方的定义:高阶组件是参数为组件,返回值为新组件的函数
* - 我们可以进行如下的解析
* ~ 首先,高阶组件本身不是一个组件,而是一个函数
* ~ 其次,这个函数的参数是一个组件,返回值也是一个组件
* 3、高阶组件的定义
* - 高阶组件的调用过程类似于这样:
* const EnhancedComponent = higherOrderComponent(WrappedComponent)
* 4、devtools的组件展示名称默认取class后的类名,如果class后没有类名则取父类class的类名。
* 也可以手动取组件的展示名称:
* - class App {};App.displayName = '展示名称';
* - const App = class {};App.displayName = '展示名称';
*/
class App extends React.PureComponent {
render() {
return (<div>
姓名:{this.props.name}
</div>)
}
}
function enhanceComponent(WrappedComponent) {
return class NewComponent extends React.PureComponent {
render() {
return <WrappedComponent {...this.props}/>
}
}
}
const EnhanceComponent = enhanceComponent(App)
export default EnhanceComponent
2、高阶组件应用-增强props
import React from "react";
export default class App extends React.PureComponent {
render() {
return (<EnhanceComponent name="阿尔萨斯"/>)
}
}
class Home extends React.PureComponent {
render() {
return (<div>
<div>姓名:{this.props.name}</div>
<div>年龄:{this.props.age}</div>
</div>)
}
}
function enhanceComponent(WrappedComponent) {
return props => {
return (<Home {...props} age={18}/>)
}
}
const EnhanceComponent = enhanceComponent(App)
import React from "react";
const UserContext = React.createContext({
name: "希尔瓦娜斯",
age: 19
})
export default class App extends React.PureComponent {
render() {
return (<EnhanceComponent/>)
}
}
class Home extends React.PureComponent {
render() {
return (<div>
<div>姓名:{this.props.name}</div>
<div>年龄:{this.props.age}</div>
</div>)
}
}
function enhanceComponent(WrappedComponent) {
return props => {
return (<UserContext.Consumer>
{
value => {
return <Home {...props} {...value}/>
}
}
</UserContext.Consumer>)
}
}
const EnhanceComponent = enhanceComponent(App)
3、高阶组件应用-登录鉴权操作
import React from "react";
function withAuth(WrappedComponent) {
return class AuthCartPage extends React.PureComponent {
render() {
const {isLogin} = this.props
if (isLogin) {
// 注意:组件名一定要大写开头
return <WrappedComponent {...this.props}/>
} else {
return <LoginPage {...this.props}/>
}
}
}
}
class CartPage extends React.PureComponent {
render() {
return (<div>CartPage</div>)
}
}
const AuthCartPage = withAuth(CartPage);
class LoginPage extends React.PureComponent {
render() {
return (<div>LoginPage</div>)
}
}
export default class App extends React.PureComponent {
render() {
return (<div>
<AuthCartPage isLogin={false}/>
</div>)
}
}
4、高阶组件应用-生命周期劫持
import React from "react";
function withRenderTime(WrappedComponent) {
return class extends React.PureComponent {
UNSAFE_componentWillMount() {
this.beginTime = Date.now()
}
render() {
return (<WrappedComponent {...this.props}/>)
}
componentDidMount() {
this.endTime = Date.now()
const interval = this.endTime - this.beginTime
console.log(`${WrappedComponent.name}渲染时间:${interval}`)
}
}
}
class Home extends React.PureComponent {
render() {
return (<div>Home</div>)
}
}
class About extends React.PureComponent {
render() {
return (<div>About</div>)
}
}
const TimeHome = withRenderTime(Home);
const TimeAbout = withRenderTime(About);
export default class App extends React.PureComponent {
render() {
return (<div>
<TimeHome/>
<TimeAbout/>
</div>)
}
}
5、ref的转发
import React from "react";
class Home extends React.PureComponent {
render() {
return (<div>Home</div>)
}
}
const Profile = React.forwardRef(function (props, ref) {
return (<div ref={ref}>Profile</div>)
})
export default class App extends React.PureComponent {
constructor(props) {
super(props);
this.titleRef = React.createRef()
this.homeRef = React.createRef()
this.profileRef = React.createRef()
}
render() {
return (<div>
<div ref={this.titleRef}>hello world</div>
<Home ref={this.homeRef}/>
<Profile ref={this.profileRef}/>
<button onClick={e => this.printRef()}>打印ref</button>
</div>)
}
printRef() {
console.log(this.titleRef.current)
console.log(this.homeRef.current)
console.log(this.profileRef.current)
}
}
6、Portals的使用
import React from "react";
import ReactDOM from "react-dom"
// ui框架dialog的实现原理
class Modal extends React.PureComponent {
render() {
const div = document.createElement("div");
Object.assign(div.style, {
position: "fixed",
left: "50%",
top: "50%",
transform: "translate(-50%,-50%)"
})
document.body.appendChild(div)
return ReactDOM.createPortal(
this.props.children,
div
)
}
}
class Home extends React.PureComponent {
render() {
return (<div>
<div>Home</div>
<Modal>
<div>Title</div>
</Modal>
</div>)
}
}
export default class App extends React.PureComponent {
render() {
return (<div>
<Home/>
</div>)
}
}
十、组件化补充和React中样式
1、fragments的使用
import React, {Fragment} from "react";
export default class App extends React.PureComponent {
render() {
// 避免渲染根节点
/*
return (<Fragment className="container">
<div>姓名:帕克</div>
</Fragment>)*/
// 短语法,Fragment无需引入,但是要添加className等属性时不可省略
return (<>
<div>姓名:恶魔猎手</div>
</>)
}
}
2、StrictMode
import React, {PureComponent, StrictMode, Fragment} from "react";
class Home extends PureComponent {
constructor(props) {
super(props);
console.log("Home constructor")
}
UNSAFE_componentWillMount() {
console.log("Home UNSAFE_componentWillMount")
}
render() {
return (<div ref="title">
Home
</div>)
}
}
class Profile extends PureComponent {
constructor(props) {
super(props);
console.log("Profile constructor")
}
UNSAFE_componentWillMount() {
console.log("Profile UNSAFE_componentWillMount")
}
render() {
return (<div ref="title">
Profile
</div>)
}
}
/**
* 1、StrictMode是一个用来突出显示应用程序中潜在问题的工具
* - 与Fragment一样,StrictMode不会渲染任何可见UI
* - 它为其后代元素触发额外的检查和警告
* - 严格模式检查仅在开发模式下运行;它们不会影响生产构建
* 2、严格模式检查的是什么
* - 识别不安全的生命周期
* - 使用过时的ref api
* - 检查意外的副作用
* ~ 这个组件的constructor会被调用两次
* ~ 这是严格模式下故意进行的操作,让你来查看在这里写的一些逻辑代码被多次调用时,是否会产生一些副作用
* ~ 在生产环境中,是不会被调用两次的
* - 使用废弃的findDOMNode方法
* ~ 在之前的React API中,可以通过findDOMNode来获取DOM,不过已经不推荐使用了,可以自行学习演练一下
* - 检测过时的context API
* ~ 早期的Context是通过static属性声明Context对象属性,通过getChildContext返回Context对象等方式来使用Context的
* ~ 目前这种方式已经不推荐使用,大家可以自行学习了解一下它的用法
*/
export default class App extends PureComponent {
render() {
return (<Fragment>
<StrictMode>
<Home/>
</StrictMode>
<Profile/>
</Fragment>)
}
}
3、React中的样式
* 在组件化中选择合适的css解决方案应该符合以下条件
- 可以编写局部css:css具备自己的局部作用域,不会随意污染其他组件内的元素
- 可以编写动态的css:可以获取当前组件的一些状态,根据状态的变化生成不同的css样式
- 支持所有的css特性:伪类、动画、媒体查询等
- 编写起来简洁方便、最好符合一贯的css风格特点
- 等等...
* 在这一点上,Vue做的要确实要好于React
- Vue通过在.vue文件中编写<style></style>标签来编写自己的样式
- 通过是否添加scoped属性来决定编写的样式是全局有效还是局部有效
- 通过lang属性来设置你喜欢的less、scss等预处理器
- 通过内联样式风格的方式来根据最新状态设置和改变css
- 等等...
* 内联样式
- 优点
~ 内联样式,样式之间不会有冲突
~ 可以动态获取当前state中的状态
- 缺点
~ 写法上都需要使用驼峰标识
~ 某些样式没有提示
~ 大量的样式,代码混乱
~ 某些样式无法编写(比如伪类/伪元素)
* 普通的css
- 普通的css我们通常会编写到一个单独的文件,之后再进行引入(import "./index.css")
- 这样的编写方式和普通的网页开发中编写方式是一致的
~ 如果我们按照普通的网页标准去编写,那么也不会有太大的问题
~ 但是组件化开发中我们总是希望组件是一个独立的模块,即便是样式也只是在自己内部生效,不会相互影响
~ 但是普通的css都属于全局的css,样式之间会相互影响
- 这种编写方式最大的问题是样式之间会相互层叠掉
4、css modules
- src/layout/App.js
import React, {PureComponent, Fragment} from "react";
import style from "./style.module.css"
export default class App extends PureComponent {
/**
* 1、控制台查看元素class="style_text__uEqSY"
* - style是.module.css之前的
* - text是class
* - uEqSY是随机生成
* 2、React的脚手架已经内置了css modules的配置
* - .css/.less/.scss等样式文件都修改成.module.css/.module.less/.module.scss等
* - 之后就可以引用并且进行使用了
* 3、但是这种方案也有自己的缺陷
* - 引用的类名,不能使用连接符(.home-title),在javascript中是不识别的
* - 所有的className都必须使用(style.className)的形式来编写
* - 不方便动态来修改某些样式,依然需要使用内联样式的方式
*/
render() {
return (<Fragment>
<div className={style.text}>兽人永不为奴</div>
</Fragment>)
}
}
- src/layout/style.module.css
.text {
color: red;
}
5、模板字符串的使用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react</title>
</head>
<body>
<script>
// 1、模板字符串的基本使用
const name = "食人魔法师"
const message = `人物:${name}`
console.log(message)
// 2、补充
function arrArg(...args) {
// 数组
console.log(args)
}
arrArg('骷髅王', '水晶侍女')
function objArg({...args}) {
// 对象
console.log(args)
}
objArg({name: '潮汐猎人', friend: '复仇之魂'})
// 3、标签模板字符串:可以通过模板字符串的方式对一个函数进行调用
function templateStringFun(...args) {
// [['英雄:', '技能:', ''], '死灵法师', '竭心光环']
console.log(args)
}
templateStringFun`英雄:${'死灵法师'}技能:${'竭心光环'}`
</script>
</body>
</html>
6、css in js(认识styled-components)
import React, {PureComponent} from "react";
import styled from "styled-components"
/**
* 1、css in js是react编写css最为受欢迎的一种解决方案
* - css in js通过javascript来为css赋予一些能力,包括类似于css预处理器一样的
* 样式嵌套、函数定义、逻辑复用、动态修改状态等等
* - 虽然css预处理器也具备某些能力,但是获取动态状态依然是一个不好处理的点
* - styled-components目前是社区最流行的css in js库
* 2、安装styled-components
* - npm i styled-components
* 3、定义div标签
* - 也可以是span等其它语义标签
* - 也可定义于其它子标签
* - 每次定义会生成唯一的class名(同一个定义多处使用class名是一致的)
*/
const AppStyle = styled.div`
display: flex;
> span{
flex: 1;
user-select: none;
&.active{
color: red;
}
&:hover{
color:green;
}
}
`
const SpanStyle = styled.span`
&::after{
content: '(+1)';
}
`
export default class App extends PureComponent {
render() {
return (<AppStyle>
<SpanStyle>敌法师</SpanStyle>
<SpanStyle className='active'>水人</SpanStyle>
<SpanStyle>剑圣</SpanStyle>
</AppStyle>)
}
}
7、styled-components的用法
import React, {PureComponent, Fragment} from "react";
import styled from "styled-components"
/**
* 1、特点
* - props穿透
* - attrs的使用
* - 传入state作为props属性
*/
const InputStyle = styled.input.attrs({
placeholder: "小鱼人",
bgColor: "green"
})`
background-color: ${props => props.bgColor};
border-color: ${props => props.borColor};
`
export default class App extends PureComponent {
constructor(props) {
super(props);
this.state = {
borColor: "red"
}
}
render() {
return (<Fragment>
<InputStyle text="password" borColor={this.state.borColor}/>
</Fragment>)
}
}
8、styled-components的继承和设置主题
import React, {PureComponent} from "react";
import styled, {ThemeProvider} from "styled-components"
const DivBaseStyle = styled.div`
background-color: yellow;
`
/**
* 1、继承:标签上会有两个类名,一个是DivStyle的,一个是DivBaseStyle的
* 2、设置主题:通过ThemeProvider的theme属性设置主题,通过props获取属性值
*/
const DivStyle = styled(DivBaseStyle)`
font-size: 32px;
color: ${props => props.theme.themeColor};
`
export default class App extends PureComponent {
render() {
return (<ThemeProvider theme={{themeColor: 'red'}}>
<DivStyle>巨魔战将</DivStyle>
</ThemeProvider>)
}
}