React高阶组件(HOC)

news2024/11/16 10:55:24

高阶组件的基本概念

  • 高阶组件(HOC,Higher-Order Components)不是组件,而是一个函数,它会接收一个组件作为参数并返回一个经过改造的新组件:

const EnhancedComponent = higherOrderComponent(WrappedComponent);
  • 需要区分的是,组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。

  • 高阶组件是 React 中用于复用组件逻辑的一种高级技巧。

使用高阶组件的原因

  • 在业务开发中,虽然不掌握高阶组件也可以完成项目的开发,但是如果我们能够灵活地使用高阶组件,可以让项目代码变得更加优雅,同时增强代码的复用性和灵活性,提升开发效率。

  • 同时,了解高阶组件对我们理解各种 React.js 第三方库的原理很有帮助。

  • 关于高阶组件能解决的问题可以简单概括成以下三个方面:

    • 抽取重复代码,实现组件复用,常见场景:页面复用。

    • 条件渲染,控制组件的渲染逻辑(渲染劫持),常见场景:权限控制。

    • 捕获/劫持被处理组件的生命周期,常见场景:组件渲染性能追踪、日志打点。

  • 可见,高阶组件的作用十分强大,接下来,我将对高阶组件的实现方式进行介绍,从而加深大家对高阶组件作用的理解。

高阶组件的实现

  • 通常情况下,实现高阶组件的方式有以下两种:

    • 返回一个无状态(stateless)的函数组件

    • 返回一个 class 组件

    • 属性代理(Props Proxy)

    • 反向继承(Inheritance Inversion)

  • 高阶组件实现方式的差异性决定了它们各自的应用场景:一个 React 组件包含了 propsstateref、生命周期方法、static方法和React 元素树几个重要部分,所以我将从以下几个方面对比两种高阶组件实现方式的差异性:

    • 原组件能否被包裹

    • 原组件是否被继承

    • 能否读取/操作原组件的 props

    • 能否读取/操作原组件的 state

    • 能否通过 ref 访问到原组件的 dom 元素

    • 是否影响原组件某些生命周期等方法

    • 是否取到原组件 static 方法

    • 能否劫持原组件生命周期方法

    • 能否渲染劫持

属性代理

  • 属性代理是最常见的实现方式,它本质上是使用组合的方式,通过将组件包装在容器组件中实现功能。

  • 属性代理方式实现的高阶组件和原组件的生命周期关系完全是React父子组件的生命周期关系,所以该方式实现的高阶组件会影响原组件某些生命周期等方法。

操作 props
  • 最简单的属性代理实现代码如下:

// 返回一个无状态的函数组件
function HOC(WrappedComponent) {
  const newProps = { type: 'HOC' };
  return props => <WrappedComponent {...props} {...newProps}/>;
}

// 返回一个有状态的 class 组件
function HOC(WrappedComponent) {
  return class extends React.Component {
    render() {
      const newProps = { type: 'HOC' };
      return <WrappedComponent {...this.props} {...newProps}/>;
    }
  };
}
  • 从上面代码可以看到,通过属性代理方式实现的高阶组件包装后的组件可以拦截到父组件传递过来的 props,提前对 props 进行一些操作,比如增加一个 type 属性。

抽象 state
  • 需要注意的是,通过属性代理方式实现的高阶组件无法直接操作原组件的 state,但是可以通过 props 和回调函数对 state 进行抽象。️

  • 常见的例子是实现非受控组件到受控组件的转变:

// 高阶组件
function HOC(WrappedComponent) {
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        name: '',
      };
      this.onChange = this.onChange.bind(this);
    }
    
    onChange = (event) => {
      this.setState({
        name: event.target.value,
      })
    }
    
    render() {
      const newProps = {
        name: {
          value: this.state.name,
          onChange: this.onChange,
        },
      };
      return <WrappedComponent {...this.props} {...newProps} />;
    }
  };
}

// 使用
@HOC
class Example extends Component {
  render() {
    return <input name="name" {...this.props.name} />;
  }
}

获取 refs 引用
  • 为了访问 DOM element (focus事件、动画、使用第三方 DOM 操作库),有时我们会用到组件的 ref 属性,关于refs 的介绍详见官方文档。

  • ref 属性只能声明在 class 类型的组件上,而无法声明在函数类型的组件上(因为无状态组件没有实例)。

  • 通过属性代理方式实现的高阶组件无法直接获取原组件的 refs 引用,但是可以通过在原组件的ref回调函数中调用父组件传入的 ref 回调函数来获取原组件的refs 引用。

  • 假设有一个 User 组件(原组件),它的代码如下:

import * as React from 'react';
import * as styles from './index.module.less';

interface IProps {
  name: string;
  age: number;
  inputRef?: any;
}
class User extends React.Component<IProps> {
  private inputElement: any ;

  static sayHello () {
    console.error('hello world'); // tslint:disable-line
  }

  constructor (props: IProps) {
    super(props);
    this.focus = this.focus.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  state = {
    name: '',
    age: 0,
  };

  componentDidMount () {
    this.setState({
      name: this.props.name,
      age: this.props.age,
    });
  }

  onChange = (e: any) => {
    this.setState({
      age: e.target.value,
    });
  }

  focus () {
    this.inputElement.focus();
  }

  render () {
    return (
      <div className={styles.wrapper}>
        <div className={styles.nameWrapper}>姓名:{this.state.name}</div>
        <div className={styles.ageWrapper}>
          年龄:
            <input
              className={styles.input}
              value={this.state.age}
              onChange={this.onChange}
              type="number"
              ref={input => {
                if (this.props.inputRef) {
                  this.props.inputRef(input); // 调用父组件传入的ref回调函数
                }
                this.inputElement = input;
              }}
            />
        </div>
        <div>
          <button
            className={styles.button}
            onClick={this.focus}
          >
            获取输入框焦点
          </button>
        </div>
      </div>
    );
  }
}

export default User;

  • 通过属性代理方式实现的能获取原组件 refs 引用的高阶组件代码如下:

import * as React from 'react';
import * as styles from './index.module.less';

function HOC (WrappedComponent: any) {
    let inputElement: any = null;

    function handleClick () {
      inputElement.focus();
    }

    function wrappedComponentStaic () {
      WrappedComponent.sayHello();
    }

    return (props: any) => (
      <div className={styles.hocWrapper}>
        <WrappedComponent
          inputRef={(el: any) => { inputElement = el; }}
          {...props}
        />
        <input
          type="button"
          value="获取子组件输入框焦点"
          onClick={handleClick}
          className={styles.focusButton}
        />
        <input
          type="button"
          value="调用子组件static"
          onClick={wrappedComponentStaic}
          className={styles.callButton}
        />
      </div>
    );
}

export default HOC;

  • 使用:

import React from 'react';
import HOC from '../../components/OperateRefsHOC';
import User from '../../components/User';

const EnhanceUser = HOC(User);

class OperateRefs extends React.Component<any> {
  render () {
    return <EnhanceUser name="小明" age={12} />;
  }
}

export default OperateRefs;

  • 通过高阶组件包装以后的 EnhanceUser 组件可以可以访问到 User 组件中的 input 元素:

 

获取原组件的 static 方法
  • 当待处理组件为 class 组件时,通过属性代理实现的高阶组件(无论是返回一个函数组件 还是返回一个 class 组件,均)可以获取到原组件的 static 方法,如上面给出的高阶组件的代码,核心代码如下:

import * as React from 'react';
import * as styles from './index.module.less';

function HOC (WrappedComponent: any) {
    /* 省略无关代码... */

    function wrappedComponentStaic () {
      WrappedComponent.sayHello();
    }

    return (props: any) => (
      <div className={styles.hocWrapper}>
        <WrappedComponent
          inputRef={(el: any) => { inputElement = el; }}
          {...props}
        />
        /* 省略无关代码... */
        <input
          type="button"
          value="调用子组件static"
          onClick={wrappedComponentStaic}
          className={styles.callButton}
        />
      </div>
    );
}

export default HOC;

  • 效果如下:

 

通过 props 实现条件渲染
  • 通过属性代理方式实现的高阶组件无法直接实现对原组件进行渲染劫持(即对原组件内部 render 的控制并不是很强),但可以通过 props 来控制是否渲染及传入数据:

import * as React from 'react';
import * as styles from './index.module.less';

function HOC (WrappedComponent: any) {
    /* 省略无关代码... */

    function wrappedComponentStaic () {
      WrappedComponent.sayHello();
    }

    return (props: any) => (
      <div className={styles.hocWrapper}>
        {
          props.isShow ? (
            <WrappedComponent
              {...props}
            />
          ) : <div>暂无数据</div>
        }
      </div>
    );
}

export default HOC;
用其他元素包裹传入的组件
  • 我们可以通过类似下面的方式将原组件包裹起来,从而实现布局或者是样式的目的:

function withBackgroundColor(WrappedComponent) {
    return class extends React.Component {
        render() {
            return (
                <div style={{ backgroundColor: '#ccc' }}>
                    <WrappedComponent {...this.props} {...newProps} />
                </div>
            );
        }
    };
}

反向继承

  • 反向继承指的是使用一个函数接受一个组件作为参数传入,并返回一个继承了该传入组件的类组件,且在返回组件的 render() 方法中返回 super.render() 方法,最简单的实现如下:

const HOC = (WrappedComponent) => {
  return class extends WrappedComponent {
    render() {
      return super.render();
    }
  }
}

  • 相较于属性代理方式,使用反向继承方式实现的高阶组件的特点是允许高阶组件通过 this 访问到原组件,所以可以直接读取和操作原组件的 state/ref/生命周期方法。

  • 反向继承方式实现的高阶组件可以通过 super.render() 方法获取到传入组件实例的 render 结果,所以可对传入组件进行渲染劫持(最大特点),如:

    • 有条件地展示元素树(element tree

    • 操作由 render() 输出的 React 元素树

    • 在任何由 render() 输出的 React 元素中操作 props

    • 用其他元素包裹传入组件的渲染结果

劫持原组件生命周期方法
  • 因为反向继承方式实现的高阶组件返回的新组件是继承于传入组件,所以当新组件定义了同样的方法时,将会会覆盖父类(传入组件)的实例方法,如下面代码所示:

function HOC(WrappedComponent){
  // 继承了传入组件
  return class HOC extends WrappedComponent {
    // 注意:这里将重写 componentDidMount 方法
    componentDidMount(){
      ...
    }

    render(){
      //使用 super 调用传入组件的 render 方法
      return super.render();
    }
  }
}

  • 虽然生命周期重写会被覆盖,但我们可以通过其他方式来劫持生命周期:

function HOC(WrappedComponent){
  const didMount = WrappedComponent.prototype.componentDidMount;
  
  // 继承了传入组件
  return class HOC extends WrappedComponent {
    componentDidMount(){
      // 劫持 WrappedComponent 组件的生命周期
      if (didMount) {
        didMount.apply(this);
      }
      ...
    }

    render(){
      //使用 super 调用传入组件的 render 方法
      return super.render();
    }
  }
}

读取/操作原组件的 state
  • 反向继承方式实现的高阶组件中可以读取、编辑和删除传入组件实例中的 state,如下面代码所示:

function HOC(WrappedComponent){
  const didMount = WrappedComponent.prototype.componentDidMount;
  // 继承了传入组件
  return class HOC extends WrappedComponent {
    async componentDidMount(){
      if (didMount) {
        await didMount.apply(this);
      }
      // 将 state 中的 number 值修改成 2
      this.setState({ number: 2 });
    }

    render(){
      //使用 super 调用传入组件的 render 方法
      return super.render();
    }
  }
}

渲染劫持
条件渲染
  • 条件渲染指的是我们可以根据部分参数去决定是否渲染组件(与属性代理方式类似),如:

const HOC = (WrappedComponent) =>
  class extends WrappedComponent {
    render() {
      if (this.props.isRender) {
        return super.render();
      } else {
        return <div>暂无数据</div>;
      }
    }
  }

修改 React 元素树
  • 我们还可以通过 React.cloneElement 方法修改由 render 方法输出的 React 组件树:

// 例子来源于《深入React技术栈》
function HigherOrderComponent(WrappedComponent) {
  return class extends WrappedComponent {
    render() {
      const tree = super.render();
      const newProps = {};
      if (tree && tree.type === 'input') {
        newProps.value = 'something here';
      }
      const props = {
        ...tree.props,
        ...newProps,
      };
      const newTree = React.cloneElement(tree, props, tree.props.children);
      return newTree;
    }
  };
}

属性代理和反向继承的对比

  • 上面两个小节分别介绍了属性代理和反向继承两种方式实现的高阶组件:

    • 属性代理是从“组合”的角度出发,这样有利于从外部去操作 WrappedComponent,可以操作的对象是 props,或者在 WrappedComponent 外面加一些拦截器,控制器等。

    • 反向继承则是从“继承”的角度出发,是从内部去操作 WrappedComponent,也就是可以操作组件内部的 state ,生命周期,render函数等等。

  • 为了方便对比,对两种方式实现的高阶组件所具有的功能列表如下:

  • 可以看到,通过反向继承方法实现的高阶组件相较于属性代理实现的高阶组件,功能更强大,个性化程度更高,因此能适应更多的场景。

具体实践

  • 本文将介绍高阶组件在业务场景中的一些实践 。

页面复用

  • 前面提到,属性代理是最常见的高阶组件实现方式,它本质上是使用组合的方式,通过将组件包装在容器组件中实现组件逻辑复用的功能。 因此,如果想实现页面复用,可以使用属性代理方式实现的高阶组件。

  • 假设我们项目中有 pageA 和 pageB 两个 UI 交互完全相同的电影列表页,但由于属于不同的电影类别,数据来源及部分文案有所不同,普通写法可能是这样:

// views/PageA.js
import React from 'react';
import fetchMovieListByType from '../lib/utils';
import MovieList from '../components/MovieList';

class PageA extends React.Component {
  state = {
    movieList: [],
  }
  /* ... */
  async componentDidMount() {
    const movieList = await fetchMovieListByType('comedy');
    this.setState({
      movieList,
    });
  }
  render() {
    return <MovieList data={this.state.movieList} emptyTips="暂无喜剧"/>
  }
}
export default PageA;

// views/PageB.js
import React from 'react';
import fetchMovieListByType from '../lib/utils';
import MovieList from '../components/MovieList';

class PageB extends React.Component {
  state = {
    movieList: [],
  }
  // ...
  async componentDidMount() {
    const movieList = await fetchMovieListByType('action');
    this.setState({
      movieList,
    });
  }
  render() {
    return <MovieList data={this.state.movieList} emptyTips="暂无动作片"/>
  }
}
export default PageB;

  • 通过观察发现,两个页面的代码有很多相同的代码,可能一开始觉得可以得过且过。但随着业务的进展,需要上线的越来越多类型的电影,每写一个新的页面就会新增一些重复的代码,这样明显是不合理的,所以我们需要对页面中的重复逻辑进行提取:

// HOC
import React from 'react';
const withFetchingHOC = (WrappedComponent, fetchingMethod, defaultProps) => {
  return class extends React.Component {
    async componentDidMount() {
      const data = await fetchingMethod();
      this.setState({
        data,
      });
    }
    
    render() {
      return (
        <WrappedComponent 
          data={this.state.data} 
          {...defaultProps} 
          {...this.props} 
        />
      );
    }
  }
}

// 使用:
// views/PageA.js
import React from 'react';
import withFetchingHOC from '../hoc/withFetchingHOC';
import fetchMovieListByType from '../lib/utils';
import MovieList from '../components/MovieList';
const defaultProps = {emptyTips: '暂无喜剧'}

export default withFetchingHOC(MovieList, fetchMovieListByType('comedy'), defaultProps);

// views/PageB.js
import React from 'react';
import withFetchingHOC from '../hoc/withFetchingHOC';
import fetchMovieListByType from '../lib/utils';
import MovieList from '../components/MovieList';
const defaultProps = {emptyTips: '暂无动作片'}

export default withFetchingHOC(MovieList, fetchMovieListByType('action'), defaultProps);;

// views/PageOthers.js
import React from 'react';
import withFetchingHOC from '../hoc/withFetchingHOC';
import fetchMovieListByType from '../lib/utils';
import MovieList from '../components/MovieList';
const defaultProps = {...}

export default withFetchingHOC(MovieList, fetchMovieListByType('some-other-type'), defaultProps);

  • 可以发现,上面设计的高阶组件 withFetchingHOC,把变的部分(组件和获取数据的方法) 抽离到外部作为传入,从而实现页面的复用。

 权限控制

 

  • 假设现在有这样一个场景:最近有一个新功能要上线,包含了一系列新开发的页面。现在需要对其中几个页面增加白名单功能,如果不在白名单中的用户访问这些页面只进行文案提示,不展示相关业务数据。一周(功能验收完成)后去掉白名单,对全部用户开放。

  • 以上场景中有几个条件:

    • 多个页面鉴权:鉴权代码不能重复写在页面组件中;

    • 不在白名单用户只进行文案提示:鉴权过程业务数据请求之前;

    • 一段时间后去掉白名单:鉴权应该完全与业务解耦,增加或去除鉴权应该最小化影响原有逻辑。

  • 思路:封装鉴权流程,利用高阶组件的条件渲染特性,鉴权失败展示相关文案,鉴权成功则渲染业务组件。由于属性代理和反向继承都可以实现条件渲染,下面我们将使用比较简单的属性代理方式实现的高阶组件来解决问题:

import React from 'react';
import { whiteListAuth } from '../lib/utils'; // 鉴权方法

/**
 * 白名单权限校验
 * @param WrappedComponent
 * @returns {AuthWrappedComponent}
 * @constructor
 */
function AuthWrapper(WrappedComponent) {
  return class AuthWrappedComponent extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        permissionDenied: -1,
      };
    }
    
    async componentDidMount() {
      try {
        await whiteListAuth(); // 请求鉴权接口
        this.setState({
          permissionDenied: 0,
        });
      } catch (err) {
        this.setState({
          permissionDenied: 1,
        });
      }
    }
    
    render() {
      if (this.state.permissionDenied === -1) {
        return null; // 鉴权接口请求未完成
      }
      if (this.state.permissionDenied) {
        return <div>功能即将上线,敬请期待~</div>;
      }
      return <WrappedComponent {...this.props} />;
    }
  }
}

export default AuthWrapper;
  • 对于需要加权限控制的页面,只需要将页面组件作为参数传给高阶组件 AuthWrapper 即可。

  • 通过使用高阶组件,使得鉴权与业务完全解耦,也避免了鉴权失败时多余的业务数据请求,只需要增加/删除少量代码,即可增加/去除用户白名单的控制,原有业务组件的逻辑也不会受到影响。

组件渲染性能追踪 

  • 前面介绍的两个例子都是使用属性代理的方式实现高阶组件,本小节介绍的,则是使用反向继承方式实现的高阶组件完成组件渲染性能的追踪。

  • 前面提到 ,反向继承方式实现的高阶组件能否劫持原组件生命周期方法,因此,利用该特性,我们可以方便的对某个组件的渲染时间进行记录:

import React from 'react';
// Home 组件
class Home extends React.Component {
  render () {
    return (<h1>Hello World.</h1>);
  }
}

// HOC
function withTiming (WrappedComponent: any) {
  let start: number, end: number;

  return class extends WrappedComponent {
    constructor (props: any) {
      super(props);
      start = 0;
      end = 0;
    }
    componentWillMount () {
      if (super.componentWillMount) {
        super.componentWillMount();
      }
      start = +Date.now();
    }
    componentDidMount () {
      if (super.componentDidMount) {
        super.componentDidMount();
      }
      end = +Date.now();
      console.error(`${WrappedComponent.name} 组件渲染时间为 ${end - start} ms`);
    }
    render () {
      return super.render();
    }
  };
}

export default withTiming(Home);

 

Hook 会替代高阶组件吗? 

  • Hook 是 React 16.8 的新增特性,它可以让我们在不编写 class 的情况下使用 state 以及其他的 React 特性(关于 Hook 的相关介绍可阅读官方文档)。

  • Hook 的出现使得原本许多很别扭的写法变得轻松,最典型的就是它可以取代掉 class 生命周期中大多数的功能,把更相关的逻辑放在一起,而非零散在各个生命周期实例方法中。

  • 虽然 Hook 能解决许多难题,但这显然并不意味着 Hook 就能取代高阶组件,因为它们其实还是有着各自的优势所在:

    • 高阶组件可以做到很轻松地外部协议化注入功能到一个基础 Component 中,所以可以用来做插件,如 react-swipeable-views中的 autoPlay 高阶组件,通过注入状态化的 props 的方式对组件进行功能扩展,而不是直接将代码写在主库中。对于 Hook 来说,其中间处理过程一定会与目标组件强依赖(不是 Hook 的缺陷,只是 Hook 显然并不是设计来解决插件注入的问题的)。

    • Hook 更多可以看作是对高阶组件方案的补充,填补了高阶组件不擅长的部分。Hook 的写法可以让代码更加紧凑,更适合做 Controller 或者需要内聚的相关逻辑。

    • 目前 Hook 还处于早期阶段(React 16.8.0 才正式发布Hook 稳定版本),一些第三方的库可能还暂时无法兼容 Hook

  • React 官方还没有把 class 从 React 中移除的打算,class 组件和 Hook 完全可以同时存在。官方也建议避免任何“大范围重构”,毕竟 Hook 是一个非常新的特性,如果你喜欢它,可以在新的非关键性的代码中使用Hook

总结 

  • 高阶组件不是组件,它是一个将某个组件转换成另一个组件的纯函数。

  • 高阶组件的主要作用是实现代码复用和逻辑抽象、对 state 和 props 进行抽象和操作、对组件进行细化(如添加生命周期)、实现渲染劫持等。在实际的业务场景中合理的使用高阶组件,可以提高开发效率和提升代码的可维护性。

  • 高阶组件的实用性使其频繁地被大量 React.js 相关的第三方库,如 React-Redux的 connect 方法、React-Loadable等所使用,了解高阶组件对我们理解各种 React.js 第三方库的原理很有帮助。

  • 高阶组件有两种实现方式,分别是属性代理和反向继承。它可以看作是装饰器模式在 React 中的实现:在不修改原组件的情况下实现组件功能的增强。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1542380.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小游戏-扫雷

扫雷大多人都不陌生&#xff0c;是一个益智类的小游戏&#xff0c;那么我们能否用c语言来编写呢&#xff0c; 我们先来分析一下扫雷的运行逻辑&#xff0c; 首先&#xff0c;用户在进来时需要我们给与一个菜单&#xff0c;以供用户选择&#xff0c; 然后我们来完善一下&#…

解决方案Please use Oracle(R) Java(TM) 11, OpenJDK(TM) 11 to run Neo4j.

文章目录 一、现象二、解决方案 一、现象 当安装好JDK跟neo4j&#xff0c;用neo4j.bat console来启动neo4却报错&#xff1a; 部分报错信息&#xff1a; Starting Neo4j. WARNING! You are using an unsupported Java runtime. Please use Oracle Java™ 11, OpenJDK™ 11 t…

Rust下载安装、卸载、版本切换、创建项目(包含指定版本的)

先声名一下&#xff0c;下面所说的版本号为xxxxx-x86_64-unknown-linux-gnu中xxxxx的部分。 下载安装 下载最新版本的Rust&#xff1a; curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh info: downloading installer重启shell 或者 按照提示 执行命令让环境变…

Day56-LNMP架构扩展为集群模式实战精讲

Day56-LNMP架构扩展为集群模式实战精讲 1. 企业级标准部署知乎产品wecenter1.1 部署知乎软件Wecenter 2. 企业级迁移数据库到独立服务器2.1 为什么要进行数据库的拆分2.2 数据库拆分架构演变过程&#xff0c;如下图所示2.3 数据库拆分环境规划2.4 数据库拆分架构详细步骤2.4 we…

Kafka broker

1. zk中存储的kafka信息 /kafka/brokers/ids存储了在线的broker id。 /kafka/brokers/topics/xxx/partitions/n/state存储了Leader是谁以及isr队列 /kafka/controller辅助Leader选举&#xff0c;每个broker都有一个controller&#xff0c;谁先在zk中注册上&#xff0c;谁就辅助…

第八节:深入讲解SMB中的Http组件

一、概述 Http组作是SMB中的核心组件之一&#xff0c;在第七节中讲解了如何简洁的进行web程序部署和运行&#xff0c;这只是它的功能之一。在本节中&#xff0c;我们将介绍Http组件的重要属性。 二、请求头Request 1、支持方法 支持POST、GET、PUT、DELETE、OPTIONS等方法&a…

二十、软考-系统架构设计师笔记-真题解析-2020年真题

软考-系统架构设计师-2020年上午选择题真题 考试时间 8:30 ~ 11:00 150分钟 1.按照我国著作权法的权利保护期&#xff0c;&#xff08; &#xff09;受到永久保护。 A.发表权 B.修改权 C.复制权 D.发行权 解析&#xff1a; 答案&#xff1a; 2.假设某计算机的字长为32位&a…

爬虫入门系列-HTML基础语法

&#x1f308;个人主页&#xff1a;会编辑的果子君 &#x1f4ab;个人格言:“成为自己未来的主人~” HTML基础语法 bs4解析比较简单&#xff0c;但是呢&#xff0c;首先你需要了解一丢丢的html知识&#xff0c;然后再去使用bs4去提取&#xff0c;逻辑和编写难度就会非常简…

Git的原理和使用(四)

目录 远程操作 理解分布式版本控制系统 远程仓库 新建远程仓库 克隆远程仓库 向远程仓库推送 拉取远程仓库 配置Git 忽略特殊文件 为命令配置别名 标签管理 理解标签 创建标签 操作标签 远程操作 理解分布式版本控制系统 1、每个人的电脑上都是一个完整的版本库…

BUG未解之谜01-指针引用之谜

在leetcode里面刷题出现的问题&#xff0c;当我在sortedArrayToBST里面给root赋予初始值NULL之后&#xff0c;问题得到解决&#xff01; 理论上root是未初始化的变量&#xff0c;然后我进入insert函数之后&#xff0c;root引用的内容也是未知值&#xff0c;因此无法给原来的二叉…

如何使用半群、群论及格理论研究人机协同

在数学中&#xff0c;半群、群论和格理论都是重要的代数结构和数学分支&#xff0c;它们分别研究了不同类型的代数系统和结构。简单介绍一下它们的基本概念&#xff1a; 1、半群&#xff08;Semigroup&#xff09;&#xff1a; 半群是一个集合&#xff0c;配备了一个二元运算&a…

Linux:文件增删 文件压缩指令

Linux&#xff1a;文件增删 & 文件压缩指令 文件增删touch指令mkdir指令cp指令rm指令rmdir指令 文件压缩zip & unzip 指令tar指令 文件增删 touch指令 功能&#xff1a;touch命令参数可更改文档或目录的日期时间&#xff0c;包括存取时间和更改时间&#xff0c;或者新…

UG NX二次开发(C#)-通过曲线组生成NURBS曲面

文章目录 1、前言2、UG NX中通过曲线组生成NURBS曲面的操作3、采用NXOpen C#方法的源代码1、前言 在UG NX中,曲线、曲面的操作使用比较多,对于创建NURBS曲面,可以通过曲线组来生成,本文以NXOpen C#的方法实现通过曲线组生成NURBS曲面的功能。对于UG NX二次开发感兴趣或者有…

【JAVA】通过JAVA实现用户界面的登录

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-wyCvaz0EBNwHcwsi {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

代码学习记录25---回溯算法最后一天

随想录日记part25【很难】 t i m e &#xff1a; time&#xff1a; time&#xff1a; 2024.03.21 主要内容&#xff1a;回溯算法在之前的学习中已经熟练掌握&#xff0c;今天对其进行挑战并进行总结&#xff1a;1&#xff1a;重新安排行程 &#xff1b;2.N皇后 &#xff1b;3.解…

SpringJPA 做分页条件查询

前言: 相信小伙伴们的项目很多都用到SpringJPA框架的吧,对于单表的增删改查利用jpa是很方便的,但是对于条件查询并且分页 是不是很多小伙伴不经常写到. 今天我整理了一下在这里分享一下. 话不多说直接上代码: Controller: RestController public class ProductInstanceContr…

Java基础-正则表达式

文章目录 1.基本介绍2.正则底层实现1.matcher.find()完成的任务2.matcher.group(0)分析1.源代码2.解释&#xff08;不分组&#xff09;3.解释&#xff08;分组&#xff09; 3.总结 3.正则表达式语法1.基本介绍2.元字符的转义符号1.基本介绍2.代码实例 3.字符匹配符1.基本介绍2.…

洛谷day3

B2053 求一元二次方程 - 洛谷 掌握printf用法&#xff1b; #include <iostream> #include <cmath> using namespace std; double a,b,c; double delta; double x1,x2;int main() {cin>>a>>b>>c;delta b*b-4*a*c;if(delta>0){x1 (-bsqrt…

【前端寻宝之路】JavaScript初学之旅

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-azUa9yH16cRXQUxE {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

生产力工具|安装更新R软件(R、studio)

内容介绍&#xff1a; 安装R软件&#xff1a; 下载 R X64 3.5.1: 访问官方R网站 https://cran.r-project.org/。选择适合Windows版本的安装包。将安装包下载到您的计算机。 本地安装: 运行下载的“R-3.5.1-win.exe”文件。按照安装向导&#xff0c;选择安装路径&#xff0c;取消…