react多组件出错其他正常显示

news2024/11/19 15:23:29

问题:一个组件内部有很多个子组件,其中一个出错,怎么实现其他组件可以正常显示,而不是页面挂掉?

一、错误边界

可以捕获发生在其子组件树任何位置的 JavaScript 错误,并打印这些错误,同时展示降级 UI,错误边界可以捕获发生在整个子组件树的渲染期间、生命周期方法以及构造函数中的错误。

错误边界无法捕获以下场景中产生的错误:

  • 事件处理(了解更多)
  • 异步代码(例如 setTimeout 或 requestAnimationFrame 回调函数)
  • 服务端渲染
  • 它自身抛出来的错误(并非它的子组件)

错误边界的工作方式类似于 JavaScript 的 catch {},不同的地方在于错误边界只针对 React 组件。只有 class 组件才可以成为错误边界组件。大多数情况下, 你只需要声明一次错误边界组件, 并在整个应用中使用它。

注意错误边界仅可以捕获其子组件的错误,它无法捕获其自身的错误。如果一个错误边界无法渲染错误信息,则错误会冒泡至最近的上层错误边界,这也类似于 JavaScript 中 catch {} 的工作机制。

如果一个 class 组件中定义了 static getDerivedStateFromError() 或 componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。当抛出错误后,请使用 static getDerivedStateFromError() 渲染备用 UI ,使用 componentDidCatch() 打印错误信息。

错误边界应该放置在哪?

错误边界的粒度由你来决定,可以将其包装在最顶层的路由组件并为用户展示一个 “Something went wrong” 的错误信息,就像服务端框架经常处理崩溃一样。你也可以将单独的部件包装在错误边界以保护应用其他部分不崩溃

static getDerivedStateFromError(error)

此生命周期会在后代组件抛出错误后被调用。 它将抛出的错误作为参数,并返回一个值以更新 state,在渲染阶段调用,因此不允许出现副作用。 如遇此类情况,请用 componentDidCatch()

componentDidCatch(error, info)

此生命周期在后代组件抛出错误后被调用。 它接收两个参数:

  1. error —— 抛出的错误。
  2. info —— 带有 componentStack key 的对象,其中包含有关组件引发错误的栈信息。

在“提交”阶段被调用,因此允许执行副作用。

注意:如果发生错误,你可以通过调用 setState 使用 componentDidCatch() 渲染降级 UI,但在未来的版本中将不推荐这样做。 可以使用静态 getDerivedStateFromError() 来处理降级渲染。

1、基本使用

如下:若是没有ErrorBoundary组件,则组件内部报错整个页面会挂掉, 最顶层使用ErrorBoundary,那么一个组件报错整个页面UI会降级显示,若是每个子组件都包裹一层ErrorBoundary,那么一个组件出错,其他可以正常显示,出错的那个组件位置显示降级UI除非return null什么都不显示

import ErrorBoundary from "./components/ErrorBoundary";
import Child1 from "./test/Child1";
import Child2 from "./test/Child2";
import Child3 from "./test/Child3";

const Child = function () {
  return (
    <ErrorBoundary>
      <Child1 />
    </ErrorBoundary>
  );
};

//父组件中含多个子组件,若一个组件内部出问题,其他组件可以正常显示=》每个子组件包括一层ErrorBoundary进行UI降级或直接return null
function App() {
  return (
    <div className="App">
      <ErrorBoundary>
        <Child />
        {/* <Child1 /> */}
        <Child2 />
        <Child3 />
      </ErrorBoundary>
    </div>
  );
}

export default App;
const d: any = {};
const Child1 = memo((props) => {
  console.log(d.d.y);
  return <p>this is Child1</p>;
});
export default Child1;

const Child2 = (props) => {
  const [count, setCount] = useState<number>(0);

  return (
    <div>
      <p>this is Child2</p>
      <p>count:{count}</p>
      <button onClick={() => setCount((prev) => prev + 1)}>click me</button>
    </div>
  );
};
export default Child2;


const Child3 = (props) => {
  return <p>this is Child3</p>;
};
export default Child3;
import React from "react";

interface Props {
  children: React.ReactNode; //ReactElement只能一个根元素  多个用ReactNode
}
interface State {
  hasError: boolean;
}

class ErrorBoundary extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error: string) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true };
  }

  componentDidCatch(error: any, errorInfo: any) {
    // 你同样可以将错误日志上报给服务器
    // logErrorToMyService(error, errorInfo);
    console.log("componentDidCatch: ", error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 你可以自定义降级后的 UI 并渲染
      // return null
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

2、可配置的错误边界

将日志上报的方法以及显示的 UI 通过接受传参的方式进行动态配置,对于传入的UI,我们可以设置以react组件的方式 或 是一个React Element进行接受,而且通过组件的话,我们可以传入参数,这样可以在兜底 UI 中拿到具体的错误信息。

import React from "react";

interface FallbackRenderProps {
  error: Error;
}
interface Props {
  children: React.ReactNode; //ReactElement只能一个根元素  多个用ReactNode
  onError?: (error: Error, errorInfo: string) => void;
  fallback?: React.ReactElement;
  FallbackComponent?: React.FunctionComponent<FallbackRenderProps>;
}
interface State {
  hasError: boolean;
  error: Error | null;
}

class ErrorBoundary extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, error: null};
  }

  static getDerivedStateFromError(error: Error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    if (this.props.onError) {
      //上报日志通过父组件注入的函数进行执行
      this.props.onError(error, errorInfo.componentStack);
    }
  }

  render() {
    const { fallback, FallbackComponent } = this.props;
    const { error } = this.state;
    if (error) {
      const fallbackProps = { error };
      //判断是否为React Element
      if (React.isValidElement(fallback)) {
        return fallback;
      }
      //组件方式传入
      if (FallbackComponent) {
        return <FallbackComponent {...fallbackProps} />;
      }

      throw new Error("ErrorBoundary 组件需要传入兜底UI");
    }
    return this.props.children;
  }
}

export default ErrorBoundary;

使用:

import ErrorBoundary from "./components/ErrorBoundary";
import ErrorBoundaryWithConfig from "./components/ErrorBoundaryWithConfig";
import Child1 from "./test/ErrorTest/Child1";
import Child2 from "./test/ErrorTest/Child2";
import Child3 from "./test/ErrorTest/Child3";

interface IErrorUIprops {
  error: Error;
}
const ErrorUI: React.FC<IErrorUIprops> = ({ error }) => {
  return (
    <div>
      <p>出错了....</p>
      <p>
        错误信息:
        {JSON.stringify(error, ["message", "arguments", "type", "name"])}
      </p>
    </div>
  );
};

const Child = function () {
  const onError = (error: Error, errorInfo: string) => {
    console.log("Child error ", error);
    console.log("Child errorInfo ", errorInfo);
  };

  return (
    <ErrorBoundaryWithConfig onError={onError} FallbackComponent={ErrorUI}>
      <Child1 />
    </ErrorBoundaryWithConfig>
  );
};


function App() {
  return (
    <div className="App">
      <ErrorBoundary>
        <Child />
        {/* <Child1 /> */}
        <Child2 />
        <ErrorBoundaryWithConfig fallback={<p>出错了....</p>}>
          <Child3 />
        </ErrorBoundaryWithConfig>
      </ErrorBoundary>
    </div>
  );
}

export default App;

进一步优化:有时候会遇到这种情况:服务器突然 503、502 了,前端获取不到响应,这时候某个组件报错了,但是过一会又正常了。比较好的方法是用户点一下被ErrorBoundary封装的组件中的一个方法来重新加载出错组件,不需要重刷页面,这时候需要兜底的组件中应该暴露出一个方法供ErrorBoundary进行处理。

 

import React from "react";

interface FallbackRenderProps {
  error: Error;
  resetErrorBoundary?: () => void;
}

interface Props {
  children: React.ReactNode; //ReactElement只能一个根元素  多个用ReactNode
  onError?: (error: Error, errorInfo: string) => void;
  fallback?: React.ReactElement;
  FallbackComponent?: React.FunctionComponent<FallbackRenderProps>;
  onReset?: () => void;
  fallbackRender?: (
    fallbackRenderProps: FallbackRenderProps
  ) => React.ReactElement;
}
interface State {
  hasError: boolean;
  error: Error | null;
}

class ErrorBoundary extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false, error: null };
  }

  static getDerivedStateFromError(error: Error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    if (this.props.onError) {
      //上报日志通过父组件注入的函数进行执行
      this.props.onError(error, errorInfo.componentStack);
    }
  }

  resetErrorBoundary = () => {
    if (this.props.onReset) this.props.onReset();
    this.setState({ hasError: false, error: null });
  };

  render() {
    const { fallback, FallbackComponent, fallbackRender } = this.props;
    const { error } = this.state;
    if (error) {
      const fallbackProps = {
        error,
        resetErrorBoundary: this.resetErrorBoundary,
      };

      //判断是否为React Element
      if (React.isValidElement(fallback)) {
        return fallback;
      }

      //函数方式传入
      if (typeof fallbackRender === "function") {
        return fallbackRender(fallbackProps);
      }

      //组件方式传入
      if (FallbackComponent) {
        return <FallbackComponent {...fallbackProps} />;
      }

      throw new Error("ErrorBoundary 组件需要传入兜底UI");
    }

    return this.props.children;
  }
}

export default ErrorBoundary;

如上:正常是显示children,children里面报错就会被捕获到,之后进行UI降级, 重置也是使其显示children,若是没有错误了那么正常显示,若是还是有错误还会被捕获到。

import { useState } from "react";
import ErrorBoundary from "./components/ErrorBoundary";
import ErrorBoundaryWithConfig from "./components/ErrorBoundaryWithConfig";
// import Home from "./test/home";
import Child1 from "./test/ErrorTest/Child1";
import Child2 from "./test/ErrorTest/Child2";
import Child3 from "./test/ErrorTest/Child3";

interface IErrorUIprops {
  error: Error;
  resetErrorBoundary?: () => void;
}
const ErrorUI: React.FC<IErrorUIprops> = ({ error, resetErrorBoundary }) => {
  return (
    <div>
      <p>出错了....</p>
      <p>
        错误信息:
        {JSON.stringify(error, ["message", "arguments", "type", "name"])}
      </p>
      {resetErrorBoundary && (
        <button onClick={resetErrorBoundary}>Try again</button>
      )}
    </div>
  );
};

function App() {
  const [count, setCount] = useState(0);

  const onReset = () => setCount(0); //点击重置时进行的回调

  const onError = (error: Error, errorInfo: string) => {
    console.log("Child error ", error);
    console.log("Child errorInfo ", errorInfo);
  };

  // fallback 组件的渲染函数
  const renderFallback = (props: IErrorUIprops) => {
    return <ErrorUI {...props} />;
  };

  return (
    <div className="App">
      <ErrorBoundary>
        <section>
          <button onClick={() => setCount((count) => count + 1)}>+</button>
          <button onClick={() => setCount((count) => count - 1)}>-</button>
        </section>
        <hr />
        {/* <Child1 /> */}
        <ErrorBoundaryWithConfig
          onError={onError}
          onReset={onReset}
          fallbackRender={renderFallback}
          /*FallbackComponent={ErrorUI}*/
        >
          <Child1 count={count} />
        </ErrorBoundaryWithConfig>

        <Child2 />
        <ErrorBoundaryWithConfig fallback={<p>出错了....</p>}>
          <Child3 count={count} />
        </ErrorBoundaryWithConfig>
      </ErrorBoundary>
    </div>
  );
}

export default App;

注意:点击+,当达到2时Child1报错( if (count === 2) throw new Error("count is two");),UI降级,继续点击+,为3时Child3报错(if (count === 3) throw new Error("count is three");)UI降级,此时Child1有重置,点击重置按钮onReset把count重置为0了,Child1 UI也重置了显示正常,而Child3之前显示了降级UI,没有重置或不刷新,页面即使数据正常了UI不会更新,还是降级UI,所以重置按钮在不刷新页面情况下可以解决此类问题。

局限性:触发重置的动作只能在 fallback 里面。假如我的重置按钮不在 fallback 里呢?或者 onReset 函数根本不在这个 App 组件下那怎么办呢?难道要将 onReset 像传家宝一路传到这个 App 再传入 ErrorBoundary 里?

思路1:能不能监听状态的更新,只要状态更新就重置,反正就重新加载组件也没什么损失,这里的状态完全用全局状态管理,放到 Redux 中。

思路2:上面的思路听起来不就和 useEffect 里的依赖项 deps 数组一样嘛,不妨在 props 提供一个 resetKeys 数组,如果这个数组里的东西变了,ErrorBoundary 就重置,这样一控制是否要重置就更灵活了。

假如是由于网络波动引发的异常,那页面当然会显示 fallback 了,如果用上面直接调用 props.resetErrorBoundary 方法来重置,只要用户不点“重置”按钮,那块地方永远不会被重置。又由于是因为网络波动引发的异常,有可能就那0.001 秒有问题,别的时间又好了,所以如果我们将一些变化频繁的值放到 resetKeys 里就很容易自动触发重置。例如,报错后,其它地方的值变了从而更改了 resetKeys 的元素值就会触发自动重置。对于用户来说,最多只会看到一闪而过的 fallback,然后那块地方又正常了。

// 本组件 ErrorBoundary 的 props
interface Props{
  ...
  resetKeys?: Array<unknown>;
  onResetKeysChange?: (
    prevResetKey: Array<unknown> | undefined,
    resetKeys: Array<unknown> | undefined,
  ) => void;
}

// 检查 resetKeys 是否有变化
const changedArray = (a: Array<unknown> = [], b: Array<unknown> = []) => {
  return (
    a.length !== b.length || a.some((item, index) => !Object.is(item, b[index]))
  );
};

class ErrorBoundary extends React.Component<Props, State> {
  ...

  componentDidUpdate(prevProps: Readonly<React.PropsWithChildren<Props>>) {
    const { resetKeys, onResetKeysChange } = this.props;

    // 只要 resetKeys 有变化,直接 reset
    if (changedArray(prevProps.resetKeys, resetKeys)) {
      if (onResetKeysChange) {
        onResetKeysChange(prevProps.resetKeys, resetKeys);
      }

      // 重置 ErrorBoundary 状态,并调用 onReset 回调
      this.reset();
    }
  }

  resetErrorBoundary = () => {
    if (this.props.onReset) this.props.onReset();
    this.reset();
  };

  reset = () => {
    this.setState({ hasError: false, error: null });
  }; 

  render() {
    ...
  }
}

上面存在问题:假如某个 key 是触发 error 的元凶,那么就有可能触发二次 error 的情况:

  1. xxxKey 触发了 error,组件报错
  2. 组件报错导致 resetKeys 里的一些东西改了
  3. componentDidUpdate 发现 resetKeys 里有东西更新了,不废话,马上重置
  4. 重置完了,显示报错的组件,因为 error 还存在(或者还未解决),报错的组件又再次触发了 error
  5. ...

如下:假如接口请求失败导致组件报错即xxkey触发组件错误:render渲染children,children报错被getDerivedStateFromError等捕获UI降级,捕获到错误后重新请求导致resetKeys里面的请求状态又发生改变,componentDidUpdate就会重置,重置后组件还是报错,就会出现如上循环。=》包括下面案例只是简单举例便于理解,应用场景不符合

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    if (this.props.onError) {
      //上报日志通过父组件注入的函数进行执行
      this.props.onError(error, errorInfo.componentStack);
    }
  }


 const onError = (error: Error, errorInfo: string) => {
    setCount(Math.random() * 3);
  };


const Child1: FC<Props> = memo(({ count }) => {
  if (count < 3) throw new Error("count is two");

  return <p>this is Child1</p>;
});

export default Child1;

 这样的情况下就会被该ErrorBoundary的上层错误边界捕获,导致整体UI降级。

 优化:有错误才重置,且不是因为错误导致后续连续

 componentDidUpdate(prevProps: Readonly<React.PropsWithChildren<Props>>, preState: State) {
    const {error} = this.state;
    const {resetKeys, onResetKeysChange} = this.props;
    
    // 已经存在错误,并且是第一次由于 error 而引发的 render/update,那么设置 flag=true,不会重置 
    if (error !== null && !this.updatedWithError) {
      this.updatedWithError = true;
      return;
    }

    // 已经存在错误,并且是普通的组件 render,则检查 resetKeys 是否有改动,改了就重置
    if (error !== null && preState.error !== null && changedArray(prevProps.resetKeys, resetKeys)) {
      if (onResetKeysChange) {
        onResetKeysChange(prevProps.resetKeys, resetKeys);
      }

      this.reset();
    }
  }
  1. 用 updatedWithError 作为 flag 判断是否已经由于 error 出现而引发的 render/update
  2. 如果当前没有错误,无论如何都不会重置
  3. 每次更新:当前存在错误,且第一次由于 error 出现而引发的 render/update,则设置 updatedWithError = true,不会重置状态
  4. 每次更新:当前存在错误,且如果 updatedWithError 为 true 说明已经由于 error 而更新过了,以后的更新只要 resetKeys 里的东西改了,都会被重置

简单案例:

function App() {
  const [explode, setExplode] = React.useState(false)

  return (
    <div>
      <button onClick={() => setExplode(e => !e)}>toggle explode</button>
      <ErrorBoundary
        FallbackComponent={ErrorFallback}
        onReset={() => setExplode(false)}
        resetKeys={[explode]}
      >
        {explode ? <Bomb /> : null}
      </ErrorBoundary>
    </div>
  )
}

注意执行逻辑:

初次渲染,执行render,渲染children报错,getDerivedStateFromError捕获错误,导致state变化,重新执行render,UI降级,初次渲染不执行componentDidUpdate。在错误捕获时执行了componentDidCatch,导致resetkey变化,props变化重新执行render,遇到判断error存在还是显示降级UI,那么就不不会再执行getDerivedStateFromError和componentDidCatch,但是props变化了会执行componentDidUpdate,在这里判断若是由于出错导致更新跳过重置=》整体就是出错导致UI降级。

非初次渲染:如初次渲染页面正常,父组件某些操作导致状态变化影响到resetkey变化,props改变执行render,渲染children报错,getDerivedStateFromError捕获错误,导致state变化,重新执行render,UI降级,但此时不是初次渲染props改变要执行componentDidUpdate,在上面判断了判断若是由于出错导致更新跳过重置。但是componentDidCatch执行时又改变resetkey,props改变执行render,此时error存在直接显示降级UI不会再触发getDerivedStateFromError和componentDidCatch,但是props变化了要执行componentDidUpdate,此时已经不是错误导致的更新,componentDidUpdate执行重置(保证重置没有问题否则又会从开始非初次渲染循环,此时可以在componentDidCatch设置this.updatedWithError = false,但是这样就没有意义了)

在 componentDidUpdate 里,只要不是由于 error 引发的组件渲染或更新,而且 resetKeys 有变化了,那么直接重置组件状态来达到自动重置=》只适用于某些场景,使用时注意。

至此,我们拥有了两种可以实现重置的方式了:

方法触发范围使用场景思想负担
手动调用 resetErrorBoundary一般在 fallback 组件里用户可以在 fallback 里手动点击“重置”实现重置最直接,思想负担较轻
更新 resetKeys哪里都行,范围更广用户可以在报错组件外部重置、resetKeys 里有报错组件依赖的数据、渲染时自动重置间接触发,要思考哪些值放到 resetKeys 里,思想负担较重

以上ErrorBoundary的使用可以整体封装成HOC:

import ErrorBoundary from "../components/ErrorBoundaryWithConfig";
import { Props as ErrorBoundaryProps } from "../components/ErrorBoundaryWithConfig";

/**
 * with 写法
 * @param Component 业务组件
 * @param errorBoundaryProps error boundary 的 props
 */

function withErrorBoundary<P = {}>(
  Component: React.ComponentType<P>,
  errorBoundaryProps: ErrorBoundaryProps
): React.ComponentType<P> {
  const Wrapped: React.ComponentType<P> = (props) => {
    return (
      <ErrorBoundary {...errorBoundaryProps}>
        <Component {...props} />
      </ErrorBoundary>
    );
  };

  // DevTools 显示的组件名
  const name = Component.displayName || Component.name || "Unknown";
  Wrapped.displayName = `withErrorBoundary(${name})`;

  return Wrapped;
}

export default withErrorBoundary;

在使用错误边界组件处理普通组件时,错误边界无法捕获异步代码、服务端错误、事件内部错误以及自己错误,所以遇到这种情况可以使用try catch或者异步操作自身的catch捕获,或者直接抛出异常,封装如下:

function useErrorHandler(givenError?: unknown): (error: unknown) => void {
  const [error, setError] = React.useState<unknown>(null)
  if (givenError != null) throw givenError
  if (error != null) throw error
  return setError
}

 使用:

import { useErrorHandler } from 'react-error-boundary'

function Greeting() {
  const [greeting, setGreeting] = React.useState(null)
  const handleError = useErrorHandler()

  function handleSubmit(event) {
    event.preventDefault()
    const name = event.target.elements.name.value
    fetchGreeting(name).then(
      newGreeting => setGreeting(newGreeting),
      handleError,
    )
  }

  return greeting ? (
    <div>{greeting}</div>
  ) : (
    <form onSubmit={handleSubmit}>
      <label>Name</label>
      <input id="name" />
      <button type="submit">get a greeting</button>
    </form>
  )
}

参考: 

​​​​​​​第三方库:react-error-boundary

GitHub - haixiangyan/my-react-error-bounday: 手把手教你实现 react-error-boundary

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

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

相关文章

CC攻击和DDOS攻击哪个对服务器影响更大

互联网企业&#xff0c;不管是小企业&#xff0c;还是大企业&#xff0c;大多数企业网站都遭受过攻击&#xff0c;而我们时不时的也能在网上看见某大型企业网站被攻击&#xff0c;崩溃的新闻&#xff0c;网络攻击可以说是屡见不鲜了。攻击力最常见的就是DDOS攻击和CC攻击&#…

使用HTML+CSS技术制作篮球明星介绍网站

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

2022年数维杯国际数模赛浅评

今日数维杯国际大学生数学建模挑战赛将要开赛&#xff0c;为了更好的帮助大家整理了以下注意事项&#xff0c; 竞赛开始时间&#xff1a;北京时间2022年11月17日08:00&#xff08;周四&#xff09; 竞赛结束时间&#xff1a;北京时间2022年11月21日08&#xff1a;00&#xff…

ffmpeg视频编解码 demo初探(二)(包含下载指定windows版本ffmpeg)将YUV图片序列作为流读入,编码封装成x264 MP4视频

参考文章&#xff1a;【FFmpeg编码实战】&#xff08;1&#xff09;将YUV420P图片集编码成H.264视频文件 文章目录第二个项目&#xff1a;将YUV图片序列作为流读入&#xff0c;编码封装成x264 MP4视频将YUV图片序列编码成.h264文件将YUV图片序列编码成mp4文件第二个项目&#x…

艾美捷测序级 II,纯化胰蛋白酶化验程序文献参考

胰蛋白酶是一种基于带正电荷的赖氨酸和精氨酸侧链的底物特异性胰丝氨酸蛋白酶&#xff08;Brown and Wold 1973&#xff09;。这种酶由胰腺排出&#xff0c;参与食物蛋白质的消化和其他生物过程。胰蛋白酶是一种中等大小的球状蛋白&#xff0c;作为一种无活性的胰蛋白酶原产生&…

甘露糖-顺铂mannose-cisplatin|甘露糖-聚乙二醇-顺铂cisplatin-PEG-mannose

甘露糖-顺铂mannose-cisplatin|甘露糖-聚乙二醇-顺铂cisplatin-PEG-mannose 顺铂&#xff0c;又名顺式-二氯二氨合铂&#xff0c;是一种含铂的药物&#xff0c;呈橙黄色或黄色结晶性粉末&#xff0c;微溶于水、易溶于二甲基甲酰胺&#xff0c;在水溶液中可逐渐转化成反式和水解…

基于Feign接口的全链路拦截器

1、前言 单体应用时&#xff0c;我们经常会把一些共享数据&#xff0c;比如登录信息等放在session里面&#xff0c;当然也可以放在ThreadLocal里面。随着业务复杂度的提高&#xff0c;分布式应用越来越主流。单机的存储的思想已经不适用了&#xff0c;共享session应运而生&…

如何度量预测用户付费的误差

在广告&#xff0c;电商&#xff0c;游戏等行业中&#xff0c;预测用户付费是核心的业务场景&#xff0c;能直接帮助提升收入&#xff0c;利润等核心业务指标&#xff0c;堪称预测中的明星。在预测用户付费的系列文章中&#xff0c;结合作者理论和工程实践经验&#xff0c;深入…

C++ make_heap等堆函数的使用

一、介绍 C的STL提供了make_heap、push_heap、pop_heap、sort_heap等算法&#xff0c;它们用来将一个随机存储的数组或者容器等转换为一个heap。这里所说的转换为heap意思是将原来的存储顺序改变&#xff0c;将转换成的堆层序遍历后所得到的元素顺序作为数组或者容器新的元素顺…

用HarmonyOS ArkUI调用三方库PhotoView实现图片的联播、缩放

本文演示如果用HarmonyOS的ArkUI来调用已经上架到三方库中心的社区库。体验HarmonyOS 3最新的API 9&#xff0c;欢迎大家一起参与构建这个万物互联的时代&#xff01; 活动主页 HarmonyOS线上Codelabs挑战赛已经开启&#xff0c;该系列挑战赛将围绕HarmonyOS的基础组件和容器…

ABAQUS计算不收敛问题,排查方法和解决方案都在这儿了

在进行有限元仿真计算时&#xff0c;常常会遇到计算不收敛的问题&#xff0c;而且导致求解不收敛的原因也是多种多样的&#xff0c;处理起来也是相当的麻烦。特别是在利用隐式算法的求解非线性问题时&#xff0c;对静态平衡方程进行迭代求解时极易出现计算的不收敛问题&#xf…

JVM垃圾回收——垃圾收集器(一)

目录 一、垃圾收集器 二、Serial/Serial Old 三、ParNew 收集器 四、Parallel Scavenge收集器 五、Parallel Old收集器 一、垃圾收集器 现阶段可以作为商用的垃圾收集器大概以上几种&#xff0c;ZGC还正在实验阶段&#xff0c;如果两个收集器之间有连线那么表示他们可搭配…

【Linux初阶】Linux环境下的 git 使用 | git的add/commit/push/log/pull/mv/rm/status

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;详细阐述git是什么&#xff0c;git的发展脉络&#xff0c;还有Linux环境下git工具的具体使用方法 &#x1f6a…

关于如何导入OTWB 7型项目

一、简介&#xff1a; 公司最新的 OTWB 7.0 产品&#xff0c;均使用最新版开发开发框架 ThornForest&#xff0c;简称 TF。相较于 Thorn 框架&#xff0c;新版的 TF 框架&#xff0c;在页面编译的方式有比较大的变化。其中最主要的变化是&#xff0c;TF 的编译需要根据不同部署…

深入理解JavaScript——执行上下文与调用栈

前言 在说一个概念前&#xff0c;我们需要确定它的前提&#xff0c;此文以 ECMAScript5 为基础撰写 一句话解释 执行上下文就是一段代码执行时所带的所有信息 执行上下文是什么 《重学前端》的作者 winter 曾经对什么是执行上下文做过这样的解释&#xff1a; JavaScript 标…

基于基于全局差错能量函数的双目图像立体匹配算法matlab仿真,并提取图像的深度信息

目录 1.算法概述 2.仿真效果预览 3.核心MATLAB代码预览 4.完整MATLAB程序 1.算法概述 全局的能量函数公式如下: E(f)Edata(f)Esmooth(f) 其中,Edata 表示能量函数的数据项,意为该像素只考虑自身的视差值的倾向,不考虑 邻域内其他像素的影响;N 表示匹配聚合时的支持窗口;p 表…

应用层-HTTP协议

HTTP概述 HTTP(HyperTextTransferProtocol)是Web应用的应用层协议&#xff0c;定义浏览器如何向Web服务器发送请求以及Web服务器如何向浏览器进行响应。目前主要使用的HTTP/1.0 和HTTP/1.1&#xff0c;尤其以HTTP/1.1 为主流。 HTTP连接 浏览器在向服务器发送请求之前&#…

全日制和非全日制之争,看完六年前的这个文件心里就有数了

在每年的报考咨询中&#xff0c;都能接触到不少关于非全日制硕士的质疑&#xff0c;最大的争议点无非在于社会含金量的问题。其实很多年以前是没有非全日制这一说法的&#xff0c;早些年很多学员也是在职周末读的双证MBA/MPA/MEM这些专业&#xff0c;但证书一律是全日制标识&am…

Oracle Primavera Unifier进度管理器(Schedule Manager)

目录 功能介绍 功能包括 功能介绍 在进度管理器中&#xff0c;Primavera Unifier 用户可以在项目/外壳和项目群级别创建和管理进度表。他们可以创建根据项目或外壳的需求自定义的项目/外壳计划表。当他们为项目/外壳创建第一个时间表表时&#xff0c;Primavera Unifier 会自…

【软考】系统集成项目管理工程师(九)项目成本管理

这里写目录标题 一、项目成本管理概述二、项目成本管理子过程1. 规划成本2. 成本估算3. 制定预算4. 控制成本一、项目成本管理概述 成本 即项目的全过程中所耗用的各种成本,它们的总和为项目成本。成本管理 是在预算范围内确保项目团队完成一个项目所需要开展的管理过程,项目…