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

IDEA的一些坑(持续更新) 转为IDEA后,被这个乱码问题困扰了两天,最终解决了,写个文总结下。 我们的项目全部使用UTF-8,先出现的问题是新建文件不是UTF-8,先修改如下配置后正常 File->settings->Editor->File Encoding 将Project Encoding、IDE Encoding都改为UTF-8后,新建文件就是UTF-8了,文件编码可以在状态栏看到。如果以前用其他编码编写的代码 阅读详情

NewContext


1 )概述

  • 新的 context API 是一个组件化的使用方式
    • 它就跟写其他的组件一样,像写jsx,通过标签的这种方式来赋值一些props
    • 还有去给子节点去拿到这个 conntext 的属性
  • context的提供方和订阅方都是独立的
    • 在什么地方想要用到这个 context
    • 就去声明式的写这个 consumer 就可以了
    • 而不需要说在这个子树的渲染过程当中
    • 都需要处于这个context的一个环境下面
  • 没有什么附带的性能影响
  • 主要关注源码中的 updateContextProvider 和 updateContextConsumer
    • 它们就是通过 createContext 返回的两个组件
    • 就是provider和consumer, 它们是一个组件的类型
    • 所以它们会有特定的组件的更新方式
  • 而新的context-api都在 ReactFiberNewContext.js 中
    • 它提供的主要核心的两个api就是 pushProvider 以及 popProvider

2 ) 源码

定位到 packages/react-reconciler/src/ReactFiberBeginWork.js#L1352

function updateContextProvider(
  current: Fiber | null,
  workInProgress: Fiber,
  renderExpirationTime: ExpirationTime,
) {
  // 在这个更新的过程中,去获取了 workingprogress点type
  // 这个type就是我们通过createcontext返回的那个provider对象
  // 这个对象上面会有一个属性指向 consumer 那个type
  const providerType: ReactProviderType<any> = workInProgress.type; // 这里获得的就是 provider 组件
  const context: ReactContext<any> = providerType._context; // 这里获得的 context 就是 consumer

  // 拿到前后两个 props
  const newProps = workInProgress.pendingProps;
  const oldProps = workInProgress.memoizedProps;

  const newValue = newProps.value;

  // 跳过
  if (__DEV__) {
    const providerPropTypes = workInProgress.type.propTypes;

    if (providerPropTypes) {
      checkPropTypes(
        providerPropTypes,
        newProps,
        'prop',
        'Context.Provider',
        ReactCurrentFiber.getCurrentFiberStackInDev,
      );
    }
  }

  // 注意这里
  pushProvider(workInProgress, newValue);

  if (oldProps !== null) {
    const oldValue = oldProps.value;
    const changedBits = calculateChangedBits(context, newValue, oldValue);
    if (changedBits === 0) {
      // 没有更新
      // No change. Bailout early if children are the same.
      if (
        oldProps.children === newProps.children &&
        !hasLegacyContextChanged()
      ) {
        return bailoutOnAlreadyFinishedWork(
          current,
          workInProgress,
          renderExpirationTime,
        );
      }
    } else {
      // 存在更新
      // The context value changed. Search for matching consumers and schedule
      // them to update.
      propagateContextChange(
        workInProgress,
        context,
        changedBits,
        renderExpirationTime,
      );
    }
  }

  const newChildren = newProps.children;
  reconcileChildren(current, workInProgress, newChildren, renderExpirationTime);
  return workInProgress.child;
}
  • 关于 const context: ReactContext<any> = providerType._context;
    // packages/react/src/ReactContext.js#L53
    // 在 createContext 函数内
    context.Provider = {
      $$typeof: REACT_PROVIDER_TYPE,
      _context: context,
    };
    
  • 进入 pushProvider
    // packages/react-reconciler/src/ReactFiberNewContext.js#L55
    export function pushProvider<T>(providerFiber: Fiber, nextValue: T): void {
      const context: ReactContext<T> = providerFiber.type._context;
    
      // isPrimaryRenderer 来自 ReactFiberHostConfig.js
      // 这个值在 dom环境中是 true
      if (isPrimaryRenderer) {
        // 这个 valueCursor 记录的是 当前这个树下面一共经历了几个provider,它对应的值
        // consumer 也就是 context 它的 value 去获取,是通过赋制在它上面的这个 _currentValue 来进行一个获取的
        push(valueCursor, context._currentValue, providerFiber);
    
        // 所以, 这跟最终去获取这个 consumer 上面的 context 的时候,跟这个 valueCursor 是没有任何关系的
        context._currentValue = nextValue;
        if (__DEV__) {
          warningWithoutStack(
            context._currentRenderer === undefined ||
              context._currentRenderer === null ||
              context._currentRenderer === rendererSigil,
            'Detected multiple renderers concurrently rendering the ' +
              'same context provider. This is currently unsupported.',
          );
          context._currentRenderer = rendererSigil;
        }
      } else {
        push(valueCursor, context._currentValue2, providerFiber);
    
        context._currentValue2 = nextValue;
        if (__DEV__) {
          warningWithoutStack(
            context._currentRenderer2 === undefined ||
              context._currentRenderer2 === null ||
              context._currentRenderer2 === rendererSigil,
            'Detected multiple renderers concurrently rendering the ' +
              'same context provider. This is currently unsupported.',
          );
          context._currentRenderer2 = rendererSigil;
        }
      }
    }
    
  • 进入 calculateChangedBits
    export function calculateChangedBits<T>(
      context: ReactContext<T>,
      newValue: T,
      oldValue: T,
    ) {
      // Use Object.is to compare the new context value to the old value. Inlined
      // Object.is polyfill.
      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
      // 使用这种方法可更准确判断两个值是否相等, 符合下面条件,被认为是全等的
      if (
        (oldValue === newValue &&
          (oldValue !== 0 || 1 / oldValue === 1 / (newValue: any))) ||
        (oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare
      ) {
        // No change 没有变化
        return 0;
      } else {
        // 注意,这里 _calculateChangedBits 这个API未开放,直接忽略即可
        const changedBits =
          typeof context._calculateChangedBits === 'function'
            ? context._calculateChangedBits(oldValue, newValue)
            : MAX_SIGNED_31_BIT_INT;
    
        if (__DEV__) {
          warning(
            (changedBits & MAX_SIGNED_31_BIT_INT) === changedBits,
            'calculateChangedBits: Expected the return value to be a ' +
              '31-bit integer. Instead received: %s',
            changedBits,
          );
        }
        return changedBits | 0; // | 0 这里是去除小数部分
      }
    }
    
    • 上述 (oldValue !== 0 || 1 / oldValue === 1 / (newValue: any) 表示 -0 !== +0
    • 上述 oldValue !== oldValue && newValue !== newValue 表示 NAN !== NAN
    • 上述 MAX_SIGNED_31_BIT_INT 转换成2进制就是 32 位 的 1
    • 以上判断就是 除了全等就是 MAX_SIGNED_31_BIT_INT
  • 进入 propagateContextChange
    export function propagateContextChange(
      workInProgress: Fiber,
      context: ReactContext<mixed>,
      changedBits: number,
      renderExpirationTime: ExpirationTime,
    ): void {
      // 拿到 当前 provider 的第一个子节点
      let fiber = workInProgress.child;
      if (fiber !== null) {
        // Set the return pointer of the child to the work-in-progress fiber.
        fiber.return = workInProgress;
      }
      // 对子节点进行遍历
      while (fiber !== null) {
        let nextFiber;
    
        // Visit this fiber.
        let dependency = fiber.firstContextDependency; // 读取这个属性
        // 存在,则进入循环
        if (dependency !== null) {
          do {
            // Check if the context matches.
            // changedBits 是32位的二进制数都是1
            // 只要 dependency.observedBits 不是0,dependency.observedBits & changedBits 就不是 0
            // dependency.context === context 表示遍历过程中的组件是依赖于这个当前的context的
            // 如果这个context的变化,那么说明它要重新渲染
            // 同时它去判断它提供的这个 observedBits 跟 changedBits 它们是有相交的部分的
            // 说明它依赖的部分也变化了, 通过这种方式判断这个组件其实是需要更新了
            if (
              dependency.context === context &&
              (dependency.observedBits & changedBits) !== 0
            ) {
              // Match! Schedule an update on this fiber.
              // 这个组件如果需要更新,除非是他自己调用了setState来创建了一个更新
              // 不然的话没有外部的方式让他去可以更新这种情况
              // 因为我们在 beginWork 的时候开始是要判断每一个组件自己的 expirationTime 的
              // 如果那个组件它本身没有创建过更新,那么它的 expirationTime 是 nowork
              // 是nowork的话,它就直接跳过更新了,这明显不符合我们这边的一个context的一个需求
              // context这边它就遍历到这个节点的时候,发现它依赖这个context,如何更新?
              // 通过主动去创建 update,并且设置你的 update.tag 是 ForceUpdate
              // 这其实没有state的一个更新,但是你必须要更新
              // 因为依赖了这个context,context更新了,所以强制更新一下
              // 然后把这个update执行 enqueueUpdate 一下,这样的话
              // 在后续要更新到这个组件的时候,它就会发现它上面是有update的, 需要去更新它
              if (fiber.tag === ClassComponent) {
                // Schedule a force update on the work-in-progress.
                const update = createUpdate(renderExpirationTime);
                update.tag = ForceUpdate;
                // TODO: Because we don't have a work-in-progress, this will add the
                // update to the current fiber, too, which means it will persist even if
                // this render is thrown away. Since it's a race condition, not sure it's
                // worth fixing.
                enqueueUpdate(fiber, update);
              }
    
              // 同时这边创建 update 是不够的,还要对这个 fiber 它的 expirationTime 进行一个操作
              // 要看它,如果目前它的 expirationTime 的优先级是要大于我当前正在渲染这次 expirationTime 的
              // 那么我就把它的 expirationTime 设置为这次 update,让它在这次渲染过程当中,肯定会被更新到
              if (fiber.expirationTime < renderExpirationTime) {
                fiber.expirationTime = renderExpirationTime;
              }
              // 不仅要在这个 workingprogress 上面去设置,我还要对它的 alternate,也就是current也要进行一个设置
              // 因为这个东西它们应该是要被同步的
              let alternate = fiber.alternate;
              if (
                alternate !== null &&
                alternate.expirationTime < renderExpirationTime
              ) {
                alternate.expirationTime = renderExpirationTime;
              }
              // Update the child expiration time of all the ancestors, including
              // the alternates.
              // 因为在这个过程当中给这个组件创建了一个update
              // 代表的意思是,我父链上面的每一个节点,它的 childExpirationTime 有可能会被改变
              // 同样的要执行一遍,类似于我们在 scheduleWorkToRoot 的时候的做的事情,就是设置它的 childExpirationTime
              let node = fiber.return;
              while (node !== null) {
                alternate = node.alternate;
                if (node.childExpirationTime < renderExpirationTime) {
                  node.childExpirationTime = renderExpirationTime;
                  if (
                    alternate !== null &&
                    alternate.childExpirationTime < renderExpirationTime
                  ) {
                    alternate.childExpirationTime = renderExpirationTime;
                  }
                } else if (
                  alternate !== null &&
                  alternate.childExpirationTime < renderExpirationTime
                ) {
                  alternate.childExpirationTime = renderExpirationTime;
                } else {
                  // Neither alternate was updated, which means the rest of the
                  // ancestor path already has sufficient priority.
                  break;
                }
                node = node.return;
              }
            }
            // 最后设置
            nextFiber = fiber.child;
            dependency = dependency.next; // 从这里可以看出 dependency 它也是可以有多个的
          } while (dependency !== null);
        } else if (fiber.tag === ContextProvider) {
          // Don't scan deeper if this is a matching provider
          nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
        } else {
          // Traverse down.
          nextFiber = fiber.child;
        }
        // 接下去和很多地方差不多的,就是它往子树上去找
        // 如果子树上没有了,它就往它的兄弟节点去找
        // 就是相当于要把我们的这个provider当前的这个组件,它的子树的每一个节点去遍历到
        // 并且找到有 firstContextDependency 这个属性的这些节点
        // 给它去创建更新的一个过程
        if (nextFiber !== null) {
          // Set the return pointer of the child to the work-in-progress fiber.
          nextFiber.return = fiber;
        } else {
          // No child. Traverse to next sibling.
          nextFiber = fiber;
          while (nextFiber !== null) {
            if (nextFiber === workInProgress) {
              // We're back to the root of this subtree. Exit.
              nextFiber = null;
              break;
            }
            let sibling = nextFiber.sibling;
            if (sibling !== null) {
              // Set the return pointer of the sibling to the work-in-progress fiber.
              sibling.return = nextFiber.return;
              nextFiber = sibling;
              break;
            }
            // No more siblings. Traverse up.
            nextFiber = nextFiber.return;
          }
        }
        fiber = nextFiber;
      }
    }
    
    • 关于 firstContextDependency
      • 在 packages/react-reconciler/src/ReactFiberBeginWork.js#L1415 中的 updateContextConsumer 函数中
        • 调用了 prepareToReadContext 函数, 在这个函数中 workInProgress.firstContextDependency = null;
      • 接下去调用了 readContext 获取到 newValue
        • 在 lastContextDependency 为 null 的时候
          • 设置了 currentlyRenderingFiber.firstContextDependency = lastContextDependency = contextItem;
        • 这是第一次进来的逻辑,对于 consumer 的情况已经够了,只会调这么一次,一个consumer 对应一个 provider 的,只会有一个context
        • 这里面有很多变量控制和单项链表的数据结构来进行存储不同的 context
        • 猜测是为了实现支持 hooks 的环境里面,一个 function component里面是可以读取多个context的使用的
        • 就是 useContext 这个API, 读取多个context,说明这个function component是依赖于多个context provider
        • 那么这个时候我们就要在这个 firstcontextdependency 上面去设置一个链表了
        • 能够让我们在有多个context的情况下,每个context变化都能让这个function component去执行一次更新
        • 后续的contextitem直接设置为当前的这个next
        • 因为这个contextItem里面是有个next的指针的指向一个它依赖的context就可以了
      • 拿到 newValue 之后,调用 render 拿到 newChildren
      • 之后,reconcileChildren 就完成了,这就是 consumer 的一个更新过程
    • 注意,在之前的 packages/react-reconciler/src/ReactFiberClassComponent.js#L997 里面
      • const contextType = ctor.contextType; 声明了一个叫做 contextType 的这么一个属性
      • 这个属性拿到之后,就可以去 readContext
    • 对于 updateClassComponent packages/react-reconciler/src/ReactFiberBeginWork.js#L428
      • 也会调用 prepareToReadContext
      • 所以对于 class component 这个fiber对象,也会给它设置 firstContextDependency 这个属性的
    • 这就是在 propagateContextChange 这个方法里面可以看到
      • 它只有对于class component它才会去创建一个update
      • 对于consumer它是不会去创建update的,因为consumer它不是update的一个载体
      • 我们只需需要给它设置 expirationTime 就可以了, 不需要给它建update来告诉它,我需要 forceUpdate
      • 因为在 consumer 中如果需要更新,就会执行更新的流程
      • 它没有像 class component,可以通过 scu 这种方式的规避一个组件的更新的流程

总结

  • 以上就是新的context API它的一个实现过程
  • 最主要的就是通过去更新 provider 的时候,给这个context它的 _currentValue 设置了一个值
  • 设置的这个值就是新的context,它提供的这个value,以此让我们的consumer可以去获取到这个值
  • 可以看到它 readcontext 的时候,最后是有一句代码的
  • return isPrimaryRenderer ? context._currentValue : context._currentValue2;
  • 这个才是真正返回值的情况, 前面是处理它这个context的依赖
  • 对于 provider 更新了之后,如何去通知一个consumer依赖的那个组件
  • 通过手动的去遍历它的所有子节点, 然后去给它指定它的 expirationTime,告诉后续的更新的流程里面
  • 这个依赖于我的context,所以它需要去更新
  • 而且对于 class component,要单独为它去创建一个update
  • 这就是 provider 和 consumer 这种新的 context API 它的一个实现过程
React性能优化:PureComponent与memo实战解析 虚拟DOM和组件重渲染机制是React性能优化的核心概念。React默认采用保守的渲染策略,父组件状态变化会触发整个子树的重渲染,这在复杂组件树和高频更新场景下会产生性能瓶颈。通过浅比较(shallow compare)实现的PureComponent和React.memo,能有效避免不必要的重渲染,特别适用于展示型组件和大型列表场景。优化时需要遵循不可变数据原则,配合useCallback保持引用稳定,并通过性能分析工具定位真正的瓶颈。这些技术在电商列表、实时仪表盘等高频交互场景中能显著提升帧率,从工程实 阅读详情

相关推荐

Playwright-MCP实战:AI驱动浏览器自动化与智能测试覆盖率分析

浏览器自动化测试是现代Web开发中保障应用质量的关键环节,其核心原理是通过程序模拟用户操作,实现对页面交互、数据验证及功能流程的自动化验证。随着前端应用复杂度提升,传统测试框架在应对异步加载、动态渲染等场景时面临挑战,而新一代框架如Playwright凭借其多浏览器原生支持、智能等待机制及强大的网络拦截能力,显著提升了测试的稳定性和覆盖率。结合模型上下文协议(MCP),可将浏览器实例转化为AI可调用的标准化服务,实现自然语言驱动的自动化操作与实时状态查询,为测试脚本生成、探索性测试及缺陷定位提供了智能化支持

weixin_30733003的博客 442

React16源码: context用法与createContext源码实现

在这个渲染的过程中,比如这个 Child2 组件,希望获取父层组件中提供的 context 里面的某几个属性。然后return的这个对象, 就是作为子组件当中能够获取这个 context 的对象。想要获取上层组件提供的 context,需要在子组件 Child2 当中。2 )关于 childContextTypes 旧版API的说明。如果不声明,它提供的这个 context ,子组件是无法获取到的。4 )为什么要弃用老的API要改成这种新的API。它的内容也是跟上层组件的。上层组件是必须要声明的。

Wang的专栏 1895

Choq:用 Cherry 与 QuickJS 构建轻量级 ClojureScript 运行环境

嵌入式设备和边缘计算场景对脚本运行时的体积和启动速度有着苛刻要求,传统 Node.js 或 JVM 方案往往显得过重。Cherry 作为 ClojureScript 方言编译器,能将类 Clojure 语法编译成体积可控的标准 JavaScript;而 QuickJS 则以低内存占用和毫秒级启动著称,是可嵌入 JavaScript 引擎中的标配。二者结合,为宿主程序提供了一条“类 Lisp 语法 + 轻量运行时”的完整链路。借助 Choq 这个集成项目,开发者可在 C、Rust、Go 等宿主中嵌入 Quic

weixin_34174322的博客 384

深入拆解 Claude Code 源码(一):7 万行 TypeScript 揭秘全球最强 AI 编程助手的内部架构

2024 年底,Anthropic 发布了 Claude Code —— 一个直接运行在终端里的 AI 编程助手。它就是一个 CLI 工具,你敲claude回车,它就开始帮你写代码。但你有没有想过,当你在终端里输入一句话,到 Claude 帮你改好代码、跑完测试、提交 PR,这中间到底发生了什么?我花了大量时间研究了 Claude Code v2.1.88 的恢复源码(从 npm 包的 source map 中提取),约 7 万行 TypeScript,1884 个文件。

u013367305的博客 925

Playwright工程化提示词:可执行、可维护、信创兼容的自动化指令集

Playwright不是浏览器模拟工具,而是基于真实浏览器上下文的自动化执行引擎;其核心能力依赖精准的locator定位、显式等待机制与上下文隔离模型。传统自然语言提示词因缺乏超时控制、状态约束和选择器语义,在CI/CD、信创环境及跨浏览器场景中极易失效。真正的提示词工程,是将业务操作意图转化为带执行前提(visible/enabled)、动作类型(click/fill)、容错参数(timeout/retry)和环境适配(麒麟V10、神威CPU)的结构化代码注释层。它提升的不仅是脚本稳定性,更是团队协作效率

aofan9566的博客 398

x86架构学习之中断

外部中断,一般是由CPU的外部引脚电信号触发,这些引脚留给硬件厂商去定义中断,例如主板厂商可以将USB中断挂载70号中断引脚上,MCU厂商可以将片上外设(SPI、I2C等)的中断挂载在指定中断线上。中断描述符主要用于说明该中断的入口地址,中断描述符表则是将中断描述符一项一项管理在一个数组中,每一项中断描述符占用8个字节(64位模式下,占用16字节),其。简称中断,后文如果没特别说明,中断都指外部中断,由外部IO事件触发,由于时钟源来自CPU外部,与CPU时钟不同源,因此是异步事件。

qq_26962739的博客 1572

AutoSizer 实战:让虚拟列表自适应容器尺寸的完整指南

当列表数据上万条、屏幕从手机切到桌面、窗口拖拽又是日常操作时,写死的宽高很快就撑不住了,要么手动算尺寸,要么自己监听 window.resize。react-virtualized 里的 AutoSizer 组件就是为这个场景准备的:把 List、Table 或 Grid 包一层,它会自动测量父容器尺寸,把精确的宽度和高度传给子组件,完成响应式适配。读完后,能带走一份可直接粘贴运行的自适应列表最小

gitblog_01118的博客 900

RocketMq面板访问异常 com.alibaba.fastjson.JSONException: not close json text, token : string

rocketmq JSONException: not close json text, token : string 问题解决

qq_21067307的博客 5575

GitHub高星项目再放送:10个让你“哇塞”的代码宝藏!

目录(前方高能预警)1.1 项目名称: 一句话介绍:“人脸识别,一行代码搞定!妈妈再也不用担心我认不出老板了!” 1.2 项目名称: 一句话介绍:“给你的ChatGPT套个漂亮马甲,私人AI助理即刻拥有!”2.1 项目名称: 一句话介绍:“在网页里造一座3D城市?用它就对了!”2.2 项目名称: 一句话介绍:“给网页元素加特效,就像加糖一样简单!”3.1 项目名称: 一句话介绍:“让终端(Terminal)从黑白电视机变成科幻驾驶舱!”3.2 项目名称: 一句话介绍:“曼(man)手册太长不看?‘

热爱技术与前沿创新,深耕科技领域,在科创中精进自我;探索技术乐趣,分享技术干货与成长心得,以技术为伴,热爱生活,在科创与生活中双向成长。 1047

银河麒麟V10 SP3下NFS客户端挂载失败?5个常见问题排查与修复

本文针对银河麒麟V10 SP3系统中NFS客户端挂载失败的常见问题,提供了5个实战排查方案。从基础服务状态检查到防火墙配置、版本兼容性、/etc/fstab设置及权限映射,详细解析了问题根源与解决方案,帮助用户快速恢复NFS文件共享功能。

weixin_42525005的博客 363

FAST-LIVO2 源码精读(五):IMU 运动模型、离散传播与去畸变原理

本文是「FAST-LIVO2 激光-惯性-视觉里程计源码精读」专栏第五篇,数学基础篇章(第 4–7 篇)的第二篇。上一篇在 SO(3) 流形上建立了姿态表示与右扰动模型;本篇聚焦 IMU 预积分的核心问题:一帧激光扫描横跨约 100 ms,帧内每一个点的采集时刻各不相同,如何利用 200 Hz 的 IMU 量测将全帧点云统一对齐到帧末时刻?答案完整封装在(587 行)的函数中,本篇逐段拆解其四阶段逻辑,并推导协方差矩阵随误差状态转移矩阵F_x逐帧演化的物理含义。

PX4-SLAM-PNC 158

Payload UI Reskin 工作流:基于 Figma 的组件换肤与 SCSS 到 CSS 迁移实战指南

Payload v4 管理后台的视觉换肤(reskin)有一套标准化的 Agent 技能流程,定义在 [ui4 技能文档](https://link.gitcode.com/i/2d30ae50a928239790ab10e3d5582f9f) 中:给定一个 Figma URL 作为设计基准,依次完成图标扫描、SCSS 到原生 CSS 的语法迁移、Figma 变体分析、按设计令牌重新着色、测试集合

gitblog_00184的博客 813

S7-1200PLC与昆仑通态触摸屏以太网通信的具体方法和步骤(图文)

S7-1200PLC与昆仑通态触摸屏以太网通信的具体方法和步骤(图文)

Robot_PLC_HMI_Automation 1万+

Playwright for Java 发布说明精读:从 1.7 到 1.62 的 API 演进、浏览器版本矩阵与 Java 工程实践

本文以 Playwright 仓库中 Java 版官方发布说明 [release-notes-java.md](https://link.gitcode.com/i/2fbd3ba5668ce0502aabfbe7da697648) 为主体,系统梳理 Playwright for Java 从 1.7(首个与 JavaScript 绑定齐平的 SDK)到 1.62 的完整版本脉络:每个版本的标志性

gitblog_00012的博客 727

(四)C++自制植物大战僵尸游戏启动流程

鼠标左键单机VS2022上方工具栏中进行项目启动。第一次启动项目需要编译项目中所有代码文件,编译生成需要一定的时间。不同性能的电脑需要消耗的时间差别可能较大,通常在几分钟到几十分钟之间。编译成功后会自动启动运行游戏。qi编译成功后自动启动运行游戏如下图。

尔灵尔亿的博客 2468

Python 爬虫实战:在房天下抓取房产信息,分析房价走势

通过本次 Python 爬虫实战,我们成功地从房天下爬取了房产信息,并进行了存储、处理、分析和可视化展示。在这个过程中,我们不仅掌握了爬虫技术的基本应用,还学会了如何对动态加载的数据进行处理和分析。然而,本次爬虫任务也存在一些局限性,例如仅爬取了部分区域的房产数据,且未对房产的图片和视频内容进行分析。在未来的工作中,我们可以进一步优化爬虫程序,爬取更多区域的房产数据,并深入分析房产的多媒体内容、用户行为模式等,为购房者和房地产从业者提供更有价值的数据支持。

u014481728的博客 1372

urql 与 Svelte 集成实战:从零搭建 @urql/svelte 示例项目

本指南以仓库中的 [with-svelte 示例](https://link.gitcode.com/i/d9e1471d6ff6aa9a27d1561a2863d8ab) 为骨架,完整讲解如何在一个基于 Vite 的 Svelte 4 项目中集成 urql:包括安装依赖、配置 `Client`、通过 Svelte Context API 共享客户端,以及使用 `queryStore` / `mu

gitblog_00418的博客 150

软件设计师---UML

软件设计师---UML

我从不打没有准备的仗! 1万+

Web端E2E测试提速实战:从浏览器机制到CI环境的全链路优化

端到端(E2E)测试是保障现代Web应用质量的关键环节,其核心在于模拟真实用户在浏览器中的交互行为。理解浏览器渲染生命周期、JavaScript事件循环与前端框架(如React/Vue)的挂载机制,是突破性能瓶颈的前提;而测试工具(Cypress/Playwright/Puppeteer)的抽象层级、等待策略与底层驱动配置,则直接决定执行效率。技术价值体现在将冗余等待、重复上下文创建、低效资源加载等‘性能税’系统性消除,使单用例耗时从秒级降至毫秒级。典型应用场景包括CI/CD流水线加速、高频PR验证、大型管

weixin_33738982的博客 531

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

OpenClaw:浏览器自动化环境治理的CLI编排层

浏览器自动化已从‘写脚本’演进为‘治环境’——核心挑战不再是DOM操作,而是Chromium版本管理、系统依赖兼容、反爬指纹控制与Docker化部署等环境治理问题。Playwright虽提供强大API,但其底层二进制分发、跨平台启动、profile隔离等能力需大量工程补丁。OpenClaw不替代Playwright,而是作为CLI驱动的编排层,通过Managed Chromium Profile(MCP)、Skill技能封装和标准化Deploy Target,将浏览器实例抽象为可复现、可验证、可交付的服务单

weixin_34417183的博客 360

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

【实测好用】GT-SUITE 中文资料合集:GT-power 中文手册与上手指南

翻英文 GT-SUITE 文档翻到第三页就开始劝退?这个仓库直接提供 GT-SUITE 中文资料,照着中文手册入门即可。 ## GT-SUITE 是什么,仓库收录了什么 GT-SUITE 是一套多领域仿真软件,常见于汽车、航空、能源等方向。本仓库收录面向新手的中文学习资料,目前包含一份 GT-power 中文手册。 ## 📚 资料清单:你能拿到什么 - GT-power中文手册(风声无边

gitblog_01038的博客 314

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Playwright Python 版本发布日志精读:从 v1.7 到 v1.62 的 API 演进、破坏性变更与升级决策指南

本篇基于官方发布日志 [release-notes-python.md](https://link.gitcode.com/i/097a8496a34664c8f1a67eb041995317) 逐版本梳理 Playwright for Python 从 v1.7 到 v1.62 的全部关键变更:包括 WebP 截图、WebAuthn 虚拟通行密钥、Screencast 录像、ARIA 快照断言等

gitblog_00784的博客 823

Fix signatures do not match the previously installed version

报错信息FAILURE: Build failed with an exception.* What went wrong: Execution failed for task ':app:installDebug'. > com.android.builder.testing.api.DeviceException: com.android.ddmlib.InstallException: Fai

Wang的专栏 2万+

在react中 实现 base64的图片转换成formdata格式并实现上传功能

如果不想要服务端实现处理base64格式的图片的转换,那么在浏览器端来实现,部分属于伪代码,仅作为开发参考,请勿直接拷贝使用

Wang的专栏 8836

快速解决gradle下载慢的问题的小技巧

在ionic run 或build 或clean命令的时候,如果没有下载过gradle, 需要重新下载一次,而通过CLI的方式下载很慢,即使科学上网后,下载还是存在一些问题。这里总结一下,快速的下载安装方式。 步骤 本地安装 http-server 包, 通过 $ npm i -g http-server 的方式安装到全局 在官网上下载自己的版本,目前自带的版本是 2.14.1,其他版本可能在执行过

Wang的专栏 6714

React 实现 2D 画板功能

在移动端或者PC端我们来模拟画板的实现, 支持清除和导出功能,此处简单实现,仅作开发参考。 效果演示 主要代码演示 /* 此模块用于实现签名绘图功能 */ // 需要用到的变量定义 let clickX = new Array(); let clickY = new Array(); let clickDrag = new Array(); let paint; let point...

Wang的专栏 4325
上一篇: React16源码: React中LegacyContext的源码实现
下一篇: React16源码: React中处理ref的核心流程源码实现
Wang's Blog
博客等级 码龄12年 7438粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值