React16源码: React中的updateClassComponent的源码实现

本文主要是介绍React16源码: React中的updateClassComponent的源码实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ClassComponent 的更新


1 ) 概述

  • 在 react 中 class component,是一个非常重要的角色
  • 它承担了 react 中 更新整个应用的API
    • setState
    • forceUpdate
  • 在react当中,只有更新了state之后,整个应用才会重新进行渲染
  • 在 class component 中, 它的逻辑相对复杂

2 )源码

在 packages/react-reconciler/src/ReactFiberBeginWork.js

// 这个方法就是更新 ClassComponent 组件的一个过程
function updateClassComponent(current: Fiber | null,workInProgress: Fiber,Component: any,nextProps,renderExpirationTime: ExpirationTime,
) {// Push context providers early to prevent context stack mismatches.// During mounting we don't know the child context yet as the instance doesn't exist.// We will invalidate the child context in finishClassComponent() right after rendering.// 先跳过 context 相关的逻辑let hasContext;if (isLegacyContextProvider(Component)) {hasContext = true;pushLegacyContextProvider(workInProgress);} else {hasContext = false;}prepareToReadContext(workInProgress, renderExpirationTime);// instance 就是我们 class component,通过new这个class获取的一个对象const instance = workInProgress.stateNode;// 在这里面,它声明了一个 shouldUpdate 的一个属性let shouldUpdate; // 先判断instance是否存在, 比如说我们第一次通过 ReactDOM.render 进行渲染的过程当中// 在从上往下第一次渲染的过程当中,第一次更新到 class component 的时候// 它的instance肯定是不存在的,因为它还没有被更新过,所以它的节点肯定是没有被创建的if (instance === null) {// 接下去, 判断一下current是否等于null// current等于null的情况是代表在进入第一次渲染,因为current它还不存在// 如果current不等于null, 代表我们至少已经经历过一次渲染了// 这时候 instance 不存在,而 current 存在,说明第一次渲染的时候没有创建这个instance// 同样说明这个组件是被 suspended 的,一个组件处于suspended的状态// 在这里react认为它相当于是第一次被渲染, 在初次渲染的时候,只是抛出了一个promise// 并没有真正的渲染出它的子节点, 抛出了一个promise之后,接下去的渲染就结束了if (current !== null) {// An class component without an instance only mounts if it suspended// inside a non- concurrent tree, in an inconsistent state. We want to// tree it like a new mount, even though an empty version of it already// committed. Disconnect the alternate pointers.current.alternate = null;workInProgress.alternate = null;// Since this is conceptually a new fiber, schedule a Placement effectworkInProgress.effectTag |= Placement;}// In the initial pass we might need to construct the instance.// 对于没有instance的一个情况,需要去创建 class instanceconstructClassInstance(workInProgress,Component,nextProps,renderExpirationTime,);// 并且要mount这个 class instancemountClassInstance(workInProgress,Component,nextProps,renderExpirationTime,);// 在第一次渲染的时候,会执行上述两个方法,并且 shouldUpdate = true;shouldUpdate = true;} else if (current === null) {// In a resume, we'll already have an instance we can reuse.// 在 有instance 和 没有 current 的情况下,这种是之前被中断的// 在执行 class component 的 render 方法的时候 报错,但 instance 已被创建// 代表着可以复用 instanceshouldUpdate = resumeMountClassInstance(workInProgress,Component,nextProps,renderExpirationTime,);} else {// 对于又有 current 又有 instance的情况,说明组件已经被重新渲染了// 这个 updateClassInstance 方法和 上述 resumeMountClassInstance 类似// 最大的区别是 内部判断 comonentDidUpdate shouldUpdate = updateClassInstance(current,workInProgress,Component,nextProps,renderExpirationTime,);}// 这里最终调用 finishClassComponentreturn finishClassComponent(current,workInProgress,Component,shouldUpdate,hasContext,renderExpirationTime,);
}
  • 进入 constructClassInstance

    function constructClassInstance(workInProgress: Fiber,ctor: any,props: any,renderExpirationTime: ExpirationTime,
    ): any {// 先跳过 context 相关let isLegacyContextConsumer = false;let unmaskedContext = emptyContextObject;let context = null;const contextType = ctor.contextType;// if (typeof contextType === 'object' && contextType !== null) {// 跳过 DEVif (__DEV__) {if (contextType.$$typeof !== REACT_CONTEXT_TYPE &&!didWarnAboutInvalidateContextType.has(ctor)) {didWarnAboutInvalidateContextType.add(ctor);warningWithoutStack(false,'%s defines an invalid contextType. ' +'contextType should point to the Context object returned by React.createContext(). ' +'Did you accidentally pass the Context.Provider instead?',getComponentName(ctor) || 'Component',);}}context = readContext((contextType: any));} else {unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);const contextTypes = ctor.contextTypes;isLegacyContextConsumer =contextTypes !== null && contextTypes !== undefined;context = isLegacyContextConsumer? getMaskedContext(workInProgress, unmaskedContext): emptyContextObject;}// Instantiate twice to help detect side-effects.if (__DEV__) {if (debugRenderPhaseSideEffects ||(debugRenderPhaseSideEffectsForStrictMode &&workInProgress.mode & StrictMode)) {new ctor(props, context); // eslint-disable-line no-new}}// ctor 是 construct 的一个缩写 这个 ctor 就是在 ReactElement当中// 存在的那个type,也就是 class component 定义的那个class// 传入 props和context,对应于 ReactBaseClasses.js 里面,Component 函数 接收的这两个参数// 第三个参数 updater 后面在 adoptClassInstance 里面设置const instance = new ctor(props, context);// 获取 stateconst state = (workInProgress.memoizedState =instance.state !== null && instance.state !== undefined? instance.state: null);adoptClassInstance(workInProgress, instance);if (__DEV__) {if (typeof ctor.getDerivedStateFromProps === 'function' && state === null) {const componentName = getComponentName(ctor) || 'Component';if (!didWarnAboutUninitializedState.has(componentName)) {didWarnAboutUninitializedState.add(componentName);warningWithoutStack(false,'`%s` uses `getDerivedStateFromProps` but its initial state is ' +'%s. This is not recommended. Instead, define the initial state by ' +'assigning an object to `this.state` in the constructor of `%s`. ' +'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.',componentName,instance.state === null ? 'null' : 'undefined',componentName,);}}// If new component APIs are defined, "unsafe" lifecycles won't be called.// Warn about these lifecycles if they are present.// Don't warn about react-lifecycles-compat polyfilled methods though.if (typeof ctor.getDerivedStateFromProps === 'function' ||typeof instance.getSnapshotBeforeUpdate === 'function') {let foundWillMountName = null;let foundWillReceivePropsName = null;let foundWillUpdateName = null;if (typeof instance.componentWillMount === 'function' &&instance.componentWillMount.__suppressDeprecationWarning !== true) {foundWillMountName = 'componentWillMount';} else if (typeof instance.UNSAFE_componentWillMount === 'function') {foundWillMountName = 'UNSAFE_componentWillMount';}if (typeof instance.componentWillReceiveProps === 'function' &&instance.componentWillReceiveProps.__suppressDeprecationWarning !== true) {foundWillReceivePropsName = 'componentWillReceiveProps';} else if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';}if (typeof instance.componentWillUpdate === 'function' &&instance.componentWillUpdate.__suppressDeprecationWarning !== true) {foundWillUpdateName = 'componentWillUpdate';} else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {foundWillUpdateName = 'UNSAFE_componentWillUpdate';}if (foundWillMountName !== null ||foundWillReceivePropsName !== null ||foundWillUpdateName !== null) {const componentName = getComponentName(ctor) || 'Component';const newApiName =typeof ctor.getDerivedStateFromProps === 'function'? 'getDerivedStateFromProps()': 'getSnapshotBeforeUpdate()';if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(componentName)) {didWarnAboutLegacyLifecyclesAndDerivedState.add(componentName);warningWithoutStack(false,'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +'%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' +'The above lifecycles should be removed. Learn more about this warning here:\n' +'https://fb.me/react-async-component-lifecycle-hooks',componentName,newApiName,foundWillMountName !== null ? `\n  ${foundWillMountName}` : '',foundWillReceivePropsName !== null? `\n  ${foundWillReceivePropsName}`: '',foundWillUpdateName !== null ? `\n  ${foundWillUpdateName}` : '',);}}}}// Cache unmasked context so we can avoid recreating masked context unless necessary.// ReactFiberContext usually updates this cache but can't for newly-created instances.if (isLegacyContextConsumer) {cacheContext(workInProgress, unmaskedContext, context);}return instance;
    }
    
    • 进入 adoptClassInstance
      function adoptClassInstance(workInProgress: Fiber, instance: any): void {instance.updater = classComponentUpdater; // 挂载 update 方法workInProgress.stateNode = instance; // instance 挂载到 stateNode 以供下次进来时使用,下次进来就会存在了// The instance needs access to the fiber so that it can schedule updatesReactInstanceMap.set(instance, workInProgress);if (__DEV__) {instance._reactInternalInstance = fakeInternalInstance;}
      }
      
    • 进入 ReactInstanceMap 这个就是对 instance 上 _reactInternalFiber 属性的设置
      export function remove(key) {key._reactInternalFiber = undefined;
      }export function get(key) {return key._reactInternalFiber;
      }export function has(key) {return key._reactInternalFiber !== undefined;
      }export function set(key, value) {key._reactInternalFiber = value;
      }
      
      • 这也意味着,通过 this._reactInternalFiber 就可以拿到当前 fiber 对象
  • 进入 mountClassInstance

    // Invokes the mount life-cycles on a previously never rendered instance.
    function mountClassInstance(workInProgress: Fiber,ctor: any,newProps: any,renderExpirationTime: ExpirationTime,
    ): void {if (__DEV__) {checkClassInstance(workInProgress, ctor, newProps);}const instance = workInProgress.stateNode;instance.props = newProps;instance.state = workInProgress.memoizedState;instance.refs = emptyRefsObject;const contextType = ctor.contextType;if (typeof contextType === 'object' && contextType !== null) {instance.context = readContext(contextType);} else {const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);instance.context = getMaskedContext(workInProgress, unmaskedContext);}if (__DEV__) {if (instance.state === newProps) {const componentName = getComponentName(ctor) || 'Component';if (!didWarnAboutDirectlyAssigningPropsToState.has(componentName)) {didWarnAboutDirectlyAssigningPropsToState.add(componentName);warningWithoutStack(false,'%s: It is not recommended to assign props directly to state ' +"because updates to props won't be reflected in state. " +'In most cases, it is better to use props directly.',componentName,);}}if (workInProgress.mode & StrictMode) {ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(workInProgress,instance,);ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress,instance,);}if (warnAboutDeprecatedLifecycles) {ReactStrictModeWarnings.recordDeprecationWarnings(workInProgress,instance,);}}// 获取 updateQueue,初次渲染这个 updateQueue 是空的// 对于有 setState 的情况,它的 updateQueen 里面可能有多个update// 这个时候, 需要调用这个 updateQueen 来去得到一个新的statelet updateQueue = workInProgress.updateQueue;if (updateQueue !== null) {// 这里 计算出新的state 并赋值给 workInProgress.memoizedStateprocessUpdateQueue(workInProgress,updateQueue,newProps,instance,renderExpirationTime,);// 同时更新 instance.stateinstance.state = workInProgress.memoizedState;}// 判断是否有这个生命周期方法const getDerivedStateFromProps = ctor.getDerivedStateFromProps;if (typeof getDerivedStateFromProps === 'function') {// 如果存在该生命周期方法,则计算新的state// 这个生命周期会在组件更新的过程中被调用applyDerivedStateFromProps(workInProgress,ctor,getDerivedStateFromProps,newProps,);instance.state = workInProgress.memoizedState;}// In order to support react-lifecycles-compat polyfilled components,// Unsafe lifecycles should not be invoked for components using the new APIs.// 判断是否有 componentWillMount 生命周期方法// 在这个生命周期方法中,会执行 setState 方法if (typeof ctor.getDerivedStateFromProps !== 'function' &&typeof instance.getSnapshotBeforeUpdate !== 'function' &&(typeof instance.UNSAFE_componentWillMount === 'function' ||typeof instance.componentWillMount === 'function')) {// callComponentWillMount(workInProgress, instance);// If we had additional state updates during this life-cycle, let's// process them now.// 执行了 这个生命周期方法,就需要重新执行 updateQueue// 如果在这时执行了 setState, 立马被反映到组件上面updateQueue = workInProgress.updateQueue;if (updateQueue !== null) {processUpdateQueue(workInProgress,updateQueue,newProps,instance,renderExpirationTime,);instance.state = workInProgress.memoizedState;}}if (typeof instance.componentDidMount === 'function') {workInProgress.effectTag |= Update;}
    }
    
    • 进入 processUpdateQueue
      export function processUpdateQueue<State>(workInProgress: Fiber,queue: UpdateQueue<State>,props: any,instance: any,renderExpirationTime: ExpirationTime,
      ): void {hasForceUpdate = false;// 克隆 updateQueuequeue = ensureWorkInProgressQueueIsAClone(workInProgress, queue);if (__DEV__) {currentlyProcessingQueue = queue;}// These values may change as we process the queue.let newBaseState = queue.baseState;let newFirstUpdate = null;let newExpirationTime = NoWork;// Iterate through the list of updates to compute the result.let update = queue.firstUpdate; let resultState = newBaseState;while (update !== null) {const updateExpirationTime = update.expirationTime;if (updateExpirationTime < renderExpirationTime) {// This update does not have sufficient priority. Skip it.if (newFirstUpdate === null) {// This is the first skipped update. It will be the first update in// the new list.newFirstUpdate = update;// Since this is the first update that was skipped, the current result// is the new base state.newBaseState = resultState;}// Since this update will remain in the list, update the remaining// expiration time.if (newExpirationTime < updateExpirationTime) {newExpirationTime = updateExpirationTime;}} else {// This update does have sufficient priority. Process it and compute// a new result.resultState = getStateFromUpdate(workInProgress,queue,update,resultState,props,instance,);const callback = update.callback;// 判断是否有 callbackif (callback !== null) {workInProgress.effectTag |= Callback; // 加上 Callback 这块// Set this to null, in case it was mutated during an aborted render.update.nextEffect = null; // 防止在中断的渲染中被修改if (queue.lastEffect === null) {queue.firstEffect = queue.lastEffect = update; // 链表操作} else {queue.lastEffect.nextEffect = update;queue.lastEffect = update;}}}// Continue to the next update.update = update.next;}// Separately, iterate though the list of captured updates.let newFirstCapturedUpdate = null;update = queue.firstCapturedUpdate;while (update !== null) {const updateExpirationTime = update.expirationTime;if (updateExpirationTime < renderExpirationTime) {// This update does not have sufficient priority. Skip it.if (newFirstCapturedUpdate === null) {// This is the first skipped captured update. It will be the first// update in the new list.newFirstCapturedUpdate = update;// If this is the first update that was skipped, the current result is// the new base state.if (newFirstUpdate === null) {newBaseState = resultState;}}// Since this update will remain in the list, update the remaining// expiration time.if (newExpirationTime < updateExpirationTime) {newExpirationTime = updateExpirationTime;}} else {// This update does have sufficient priority. Process it and compute// a new result.resultState = getStateFromUpdate(workInProgress,queue,update,resultState,props,instance,);const callback = update.callback;if (callback !== null) {workInProgress.effectTag |= Callback;// Set this to null, in case it was mutated during an aborted render.update.nextEffect = null;if (queue.lastCapturedEffect === null) {queue.firstCapturedEffect = queue.lastCapturedEffect = update;} else {queue.lastCapturedEffect.nextEffect = update;queue.lastCapturedEffect = update;}}}update = update.next;}if (newFirstUpdate === null) {queue.lastUpdate = null;}if (newFirstCapturedUpdate === null) {queue.lastCapturedUpdate = null;} else {workInProgress.effectTag |= Callback;}if (newFirstUpdate === null && newFirstCapturedUpdate === null) {// We processed every update, without skipping. That means the new base// state is the same as the result state.newBaseState = resultState;}queue.baseState = newBaseState;queue.firstUpdate = newFirstUpdate;queue.firstCapturedUpdate = newFirstCapturedUpdate;// Set the remaining expiration time to be whatever is remaining in the queue.// This should be fine because the only two other things that contribute to// expiration time are props and context. We're already in the middle of the// begin phase by the time we start processing the queue, so we've already// dealt with the props. Context in components that specify// shouldComponentUpdate is tricky; but we'll have to account for// that regardless.workInProgress.expirationTime = newExpirationTime;workInProgress.memoizedState = resultState;if (__DEV__) {currentlyProcessingQueue = null;}
      }
      
      • 进入 ensureWorkInProgressQueueIsAClone
      function ensureWorkInProgressQueueIsAClone<State>(workInProgress: Fiber,queue: UpdateQueue<State>,
      ): UpdateQueue<State> {const current = workInProgress.alternate;if (current !== null) {// If the work-in-progress queue is equal to the current queue,// we need to clone it first.if (queue === current.updateQueue) {// 要保证 workInProgress.updateQueue 是一个克隆的queue, 而非直接进行修改queue = workInProgress.updateQueue = cloneUpdateQueue(queue); // 拷贝 queue}}return queue;
      }
      
    • 进入 getStateFromUpdate
      function getStateFromUpdate<State>(workInProgress: Fiber,queue: UpdateQueue<State>,update: Update<State>,prevState: State,nextProps: any,instance: any,
      ): any {switch (update.tag) {case ReplaceState: {const payload = update.payload;if (typeof payload === 'function') {// Updater functionif (__DEV__) {if (debugRenderPhaseSideEffects ||(debugRenderPhaseSideEffectsForStrictMode &&workInProgress.mode & StrictMode)) {payload.call(instance, prevState, nextProps);}}return payload.call(instance, prevState, nextProps);}// State objectreturn payload;}// 这里 a & ~b 表示: a & 除了b之外的所有属性case CaptureUpdate: {workInProgress.effectTag =(workInProgress.effectTag & ~ShouldCapture) | DidCapture; // 这里最终剩下的 只有 DidCapture}// Intentional fallthroughcase UpdateState: {const payload = update.payload;let partialState;if (typeof payload === 'function') {// Updater functionif (__DEV__) {if (debugRenderPhaseSideEffects ||(debugRenderPhaseSideEffectsForStrictMode &&workInProgress.mode & StrictMode)) {payload.call(instance, prevState, nextProps);}}// 如果是 function 则调用 payload 传入之前的参数 计算出 statepartialState = payload.call(instance, prevState, nextProps);} else {// Partial state objectpartialState = payload;}// 处理特殊情况if (partialState === null || partialState === undefined) {// Null and undefined are treated as no-ops.return prevState;}// Merge the partial state and the previous state.// 合并return Object.assign({}, prevState, partialState);}case ForceUpdate: {hasForceUpdate = true;return prevState;}}return prevState;
      }
      
  • 进入 finishClassComponent

    function finishClassComponent(current: Fiber | null,workInProgress: Fiber,Component: any,shouldUpdate: boolean,hasContext: boolean,renderExpirationTime: ExpirationTime,
    ) {// Refs should update even if shouldComponentUpdate returns false// 这个先跳过markRef(current, workInProgress);// 判断 workInProgress.effectTag 上是否有 DidCaptureconst didCaptureError = (workInProgress.effectTag & DidCapture) !== NoEffect;// 在没有更新也没有错误捕获的情况下if (!shouldUpdate && !didCaptureError) {// Context providers should defer to sCU for renderingif (hasContext) {invalidateContextProvider(workInProgress, Component, false);}// 跳过更新return bailoutOnAlreadyFinishedWork(current,workInProgress,renderExpirationTime,);}const instance = workInProgress.stateNode;// RerenderReactCurrentOwner.current = workInProgress;let nextChildren;// 有任何错误捕获, 但没有配置这个api的时候// 这个 getDerivedStateFromError 生命周期的api和 componentDidCatch 有区别, 后者在下次更新的时候处理,中间可能instance不存在(因为出错了)// 就继续可能引发在这个class component上面设置的 ref拿到一个null, 操作ref的时候就会出现错误// 在前者这个新的生命周期的api中, 在本次渲染中,生成 state, 渲染出属性, 这样对于 instance 对象依然存在 对应 ref 就可以拿到实际的对象if (didCaptureError &&typeof Component.getDerivedStateFromError !== 'function') {// If we captured an error, but getDerivedStateFrom catch is not defined,// unmount all the children. componentDidCatch will schedule an update to// re-render a fallback. This is temporary until we migrate everyone to// the new API.// TODO: Warn in a future release.nextChildren = null;if (enableProfilerTimer) {stopProfilerTimerIfRunning(workInProgress);}} else {if (__DEV__) {ReactCurrentFiber.setCurrentPhase('render');nextChildren = instance.render();if (debugRenderPhaseSideEffects ||(debugRenderPhaseSideEffectsForStrictMode &&workInProgress.mode & StrictMode)) {instance.render();}ReactCurrentFiber.setCurrentPhase(null);} else {// 调用 render 方法,计算出新的 childrennextChildren = instance.render();}}// React DevTools reads this flag.workInProgress.effectTag |= PerformedWork; // 增加 PerformedWork// 不是第一次渲染,并且有错误捕获if (current !== null && didCaptureError) {// If we're recovering from an error, reconcile without reusing any of// the existing children. Conceptually, the normal children and the children// that are shown on error are two different sets, so we shouldn't reuse// normal children even if their identities match.forceUnmountCurrentAndReconcile(current,workInProgress,nextChildren,renderExpirationTime,);} else {// 正常情况调用 调和 childrenreconcileChildren(current,workInProgress,nextChildren,renderExpirationTime,);}// Memoize state using the values we just used to render.// TODO: Restructure so we never read values from the instance.workInProgress.memoizedState = instance.state;// The context might have changed so we need to recalculate it.if (hasContext) {invalidateContextProvider(workInProgress, Component, true);}return workInProgress.child; // 把 render 方法渲染出来的第一个子节点 返回给 nextUnitOfWork, 就可以在 workLoop 中继续进行更新的流程
    }
    
  • 简单总结

    • 以上是更新 class component 的整体流程
    • 一开始要使用 constructClassInstance 创建 class instance
    • 内部会根据不同情况调用不同方法
    • 并且要调用 mountClassInstance 来挂载
    • 如果是第一次渲染被中断的情况
      • 如果存在 instance 则重复使用 instance
      • 它调用的生命周期方法仍然是 第一次渲染的生命周期方法
      • 也就是 componentDidMount
    • 如果不是第一次渲染的情况
      • 调用 updateClassInstance 方法
      • 这种会最终调用 componentDidUpdate 方法
    • 它们中间的流程类似
      • 判断各种情况,创建新的 instance 或 复用之前的
      • 通过 processUpdateQueue 来获取新的 state
      • 会把它赋值到 instance 和 fiber 上面进行记录
      • 最终都会调用 finishClassComponent
        • 这里面做一些错误判断的处理
        • 以及是否可以跳过更新的过程
        • 还有重新调和子节点 (通用流程)

这篇关于React16源码: React中的updateClassComponent的源码实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/629482

相关文章

Nginx实现高并发的项目实践

《Nginx实现高并发的项目实践》本文主要介绍了Nginx实现高并发的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录使用最新稳定版本的Nginx合理配置工作进程(workers)配置工作进程连接数(worker_co

python中列表list切分的实现

《python中列表list切分的实现》列表是Python中最常用的数据结构之一,经常需要对列表进行切分操作,本文主要介绍了python中列表list切分的实现,文中通过示例代码介绍的非常详细,对大家... 目录一、列表切片的基本用法1.1 基本切片操作1.2 切片的负索引1.3 切片的省略二、列表切分的高

基于Python实现一个PDF特殊字体提取工具

《基于Python实现一个PDF特殊字体提取工具》在PDF文档处理场景中,我们常常需要针对特定格式的文本内容进行提取分析,本文介绍的PDF特殊字体提取器是一款基于Python开发的桌面应用程序感兴趣的... 目录一、应用背景与功能概述二、技术架构与核心组件2.1 技术选型2.2 系统架构三、核心功能实现解析

使用Python实现表格字段智能去重

《使用Python实现表格字段智能去重》在数据分析和处理过程中,数据清洗是一个至关重要的步骤,其中字段去重是一个常见且关键的任务,下面我们看看如何使用Python进行表格字段智能去重吧... 目录一、引言二、数据重复问题的常见场景与影响三、python在数据清洗中的优势四、基于Python的表格字段智能去重

Spring AI集成DeepSeek实现流式输出的操作方法

《SpringAI集成DeepSeek实现流式输出的操作方法》本文介绍了如何在SpringBoot中使用Sse(Server-SentEvents)技术实现流式输出,后端使用SpringMVC中的S... 目录一、后端代码二、前端代码三、运行项目小天有话说题外话参考资料前面一篇文章我们实现了《Spring

Nginx中location实现多条件匹配的方法详解

《Nginx中location实现多条件匹配的方法详解》在Nginx中,location指令用于匹配请求的URI,虽然location本身是基于单一匹配规则的,但可以通过多种方式实现多个条件的匹配逻辑... 目录1. 概述2. 实现多条件匹配的方式2.1 使用多个 location 块2.2 使用正则表达式

使用Apache POI在Java中实现Excel单元格的合并

《使用ApachePOI在Java中实现Excel单元格的合并》在日常工作中,Excel是一个不可或缺的工具,尤其是在处理大量数据时,本文将介绍如何使用ApachePOI库在Java中实现Excel... 目录工具类介绍工具类代码调用示例依赖配置总结在日常工作中,Excel 是一个不可或缺的工http://

SpringBoot实现导出复杂对象到Excel文件

《SpringBoot实现导出复杂对象到Excel文件》这篇文章主要为大家详细介绍了如何使用Hutool和EasyExcel两种方式来实现在SpringBoot项目中导出复杂对象到Excel文件,需要... 在Spring Boot项目中导出复杂对象到Excel文件,可以利用Hutool或EasyExcel

前端bug调试的方法技巧及常见错误

《前端bug调试的方法技巧及常见错误》:本文主要介绍编程中常见的报错和Bug,以及调试的重要性,调试的基本流程是通过缩小范围来定位问题,并给出了推测法、删除代码法、console调试和debugg... 目录调试基本流程调试方法排查bug的两大技巧如何看控制台报错前端常见错误取值调用报错资源引入错误解析错误

Python如何实现读取csv文件时忽略文件的编码格式

《Python如何实现读取csv文件时忽略文件的编码格式》我们再日常读取csv文件的时候经常会发现csv文件的格式有多种,所以这篇文章为大家介绍了Python如何实现读取csv文件时忽略文件的编码格式... 目录1、背景介绍2、库的安装3、核心代码4、完整代码1、背景介绍我们再日常读取csv文件的时候经常