一路繁花似锦绣前程
失败的越多,成功才越有价值

导航

 

九、高阶组件和组件补充

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>)
  }
}
posted on 2022-11-09 23:27  一路繁花似锦绣前程  阅读(25)  评论(0编辑  收藏  举报