当前位置: 首页 > news >正文

长宁做网站公司拥有服务器后如何做网站

长宁做网站公司,拥有服务器后如何做网站,银川网站建设哪家好叫啥名字,项目管理软件下载概述 上文我们聊了useRef的使用和实现#xff0c;主要两个用途#xff1a;1、用于持久化保存 2、用于绑定dom。 但是有时候我们需要在父组件中访问子组件的dom或者属性/方法#xff0c;而React中默认是不允许父组件直接访问子组件的dom的#xff0c;这时候就可以通过forwa…概述 上文我们聊了useRef的使用和实现主要两个用途1、用于持久化保存 2、用于绑定dom。 但是有时候我们需要在父组件中访问子组件的dom或者属性/方法而React中默认是不允许父组件直接访问子组件的dom的这时候就可以通过forwardRef将ref传入子组件并暴露子组件的dom给父组件使用但是这种方式直接暴露了子组件的dom处于安全性能考虑我们希望子组件只暴露我们所希望的属性由子组件自己决定暴露什么这个就需要使用到useImperativeHandle来处理。 基于这些使用场景所以本文主要从基本使用和源码实现两方面来介绍下forwardRef、useImperativeHandle这两个API。 基本使用 本小节主要介绍这两个API在Function Component中的使用已经熟悉APi的同学可以跳过该部分直接查看源码解析部分。 forwardRef forwardRef主要解决的是从父组件传递ref到子组件的问题定义如下 const SomeComponent forwardRef(render)render组件的渲染函数。React 会调用该函数并传入父组件传递的 props 和 ref。返回的 JSX 将作为组件的输出。返回一个可以在 JSX 中渲染的 React 组件。与作为纯函数定义的 React 组件不同forwardRef 返回的组件还能够接收 ref 属性。 forwardRef接收一个渲染函数然后返回一个可在JSX中渲染的组件。一般用于包裹子组件用于ref传递将子组件绑定的ref通过第二个参数传入并绑定到子组件dom节点暴露。可以这样理解forwardRef 是一个接收render函数作为参数的高阶函数 如下demo在子组件中暴露了input组件使父组件可以访问并进行例如获取焦点等dom操作 const MyInput forwardRef(function MyInput(props, ref) {return (label{props.label}input ref{ref} //label); });进过forwardRef包裹之后父组件就可以通过ref来访问子组件中暴露的dom节点但是有时候我们希望自己控制暴露哪些属性尤其是当作为公共组件被多方调用的时候这时候就需要通过useImperativeHandle来实现自定义暴露属性 forwardRef并不是一个Hook这里主要是作为介绍useImperativeHandle的媒介通常是将这两个Api连用所以这里一起简单介绍下。所有的Hook都是用use开头命名的 useImperativeHandle useImperativeHandle 是 React 中的一个 Hook它能让你自定义由 ref 暴露出来的句柄。 useImperativeHandle(ref, createHandle, dependencies?) ref该 ref 是你从 forwardRef 渲染函数 中获得的第二个参数。createHandle该函数无需参数它返回你想要暴露的 ref 的句柄。该句柄可以包含任何类型。通常你会返回一个包含你想暴露的方法的对象。dependencies可选参数作为函数 createHandle的依赖收集。React 会使用 Object.is 来比较每一个依赖项与其对应的之前值。如果该数组值发生改变或者数组为空数组则会重新执行createHandle并将新的对象绑定到ref。 一般是将forwardRef和useImperativeHandle一起用通过useImperativeHandle暴露指定属性然后父组件可以通过forwardRef注入的ref来进行访问。举例来说假设你不想暴露出整个 DOM 节点但你想要它其中两个方法focus 和 scrollIntoView。为此用单独额外的 ref 来指向真实的浏览器 DOM。然后使用 useImperativeHandle 来暴露一个句柄它只返回你想要父组件去调用的方法 import { forwardRef, useRef, useImperativeHandle } from react;const MyInput forwardRef(function MyInput(props, ref) {const inputRef useRef(null);useImperativeHandle(ref, () {return {focus() {inputRef.current.focus();},scrollIntoView() {inputRef.current.scrollIntoView();},};}, []);return input {...props} ref{inputRef} /; });在上述代码中该 ref 已不再被转发到input中而是传入到了useImperativeHandle可以理解为useImperativeHandle将ref进行了劫持并将暴露的属性绑定到ref.current上。而且我们也可以自定义返回的数据比如正常在父组件是通过ref.current访问这个可以自定义为ref.xxx import { forwardRef, useRef, useImperativeHandle } from react;const MyInput forwardRef(function MyInput(props, ref) {const inputRef useRef(null);useImperativeHandle((createResult) {ref[xxx] createResult;return ref}, () {return {focus() {inputRef.current.focus();},scrollIntoView() {inputRef.current.scrollIntoView();},};}, []);return input {...props} ref{inputRef} /; });这是由于在源码实现中对于传递对象类型和函数类型的ref处理是不一样的详情可以查看下面源码解析。 源码解析 上面我们介绍了这两个API的基本语法和使用场景下面我们将从源码的角度一步一步分析其内部是如何实现的。 主要涉及文件如下 代码入口文件路径https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js执行代码文件路径: https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.jsforwardRef文件路径: https://github.com/facebook/react/blob/main/packages/react/src/ReactForwardRef.jsbeginWork函数路径: https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberBeginWork.js 下面的都是基于生产环境下的代码分析以及会省略与本次解释无关的代码完整代码可以根据以上路径前往官网github查看。 forwardRef 从代码中能看出在生产环境下调用forwardRef包裹render函数之后会将该render函数打上标签$$typeof用于React区分当前组件是什么类型并进行不同的处理。 $$typeof: 这个属性是一个符号常量用来标识这是一个 forwardRef 组件类型。在 React 内部这个符号被用来区分不同类型的 React 元素例如函数组件、类组件、片段fragment等。REACT_FORWARD_REF_TYPE 的值是一个独特的符号确保了它在 React 内部可以被正确识别。 export function forwardRefProps, ElementType: React$ElementType(render: (props: Props, ref: React$RefElementType) React$Node ) {const elementType {$$typeof: REACT_FORWARD_REF_TYPE,render,};return elementType; }当完成标记之后后续会进入到Recondiler协调器中进行fiber构造其中会经历beginWork阶段对JSX代码进行处理并生成Fiber节点。在这个阶段会根据tag来对不同组件进行处理这里就是ForwardRef类型 function beginWork(current, workInProgress, renderLanes) {// ...switch (workInProgress.tag) {// ...case ForwardRef:const type workInProgress.type;const unresolvedProps workInProgress.pendingProps;const resolvedProps disableDefaultPropsExceptForClasses ||workInProgress.elementType type? unresolvedProps: resolveDefaultPropsOnNonClassComponent(type, unresolvedProps);return updateForwardRef(current, // 当前页面显示的fiber树workInProgress, // 内存中构建的fiber树type, // fiber类型即ForwardRef返回的elementTyperesolvedProps, // 传递给组件的属性集合renderLanes // 优先级);// ...} }参数介绍如下 current: 当前页面显示的旧的fiber节点workInProgress: 内存中构建的新的fiber节点type: fiber类型即通过ForwardRef创建的elementType对象包含$$typeof、renderresolvedProps: 传递给组件的属性集合renderLanes: 渲染优先级 调用renderWithHooks处理 hooks 逻辑并调用实际的 render 函数传递 nextProps 和 ref function updateForwardRef(current, workInProgress, Component, nextProps, renderLanes) {const render Component.render; // 获取 render 函数const ref workInProgress.ref; // 获取 reflet nextChildren;// 调用 renderWithHooks 处理 hooks 逻辑并调用 render 函数nextChildren renderWithHooks(current, workInProgress, render, nextProps, ref, renderLanes);workInProgress.flags | PerformedWork;// 调用 reconcileChildren 处理子节点协调reconcileChildren(current, workInProgress, nextChildren, renderLanes);return workInProgress.child; } function renderWithHooks(current, workInProgress, Component, props, secondArg, nextRenderLanes) {// ...const children Component(props, secondArg);// ...return children; } 从以上代码能看出forWardRef原理就是 给传递的render函数打上ForWardRef($$typeof)的标签让React知道当前组件的类型然后在beginWork阶段会根据这个类型处理将render和ref解析处理之后将ref作为render的第二个参数传入即const children Component(props, secondArg); useImperativeHandle 该Hook提供了子组件自定义暴露的属性方法的能力同其他Hook一样本文也从首次渲染、更新渲染两个方便来说明其实现。 由于本文分成了Mount、Update两个部分介绍所以这里简单介绍下两者区别 Mount 阶段在组件的初始挂载第一次渲染阶段。主要负责初始化 Hook 的状态和队列并建立 Hook 之间的链式关系。Update 阶段组件在其 props 或 state 改变后被重新渲染。主要负责处理状态更新将新的状态应用到 memoizedState 中以便下一次渲染使用并更新 Hook 链表(复用/克隆现有Hook)及其更新队列updateQueue。 mount首次渲染 虽然我们在使用时只是useImperativeHandle函数但是在React内部通过dispatcher进行了派发在mount阶段执行的mountImperativeHandle函数 function mountImperativeHandleT(ref: { current: T | null } | ((inst: T | null) mixed) | null | void,create: () T,deps: Arraymixed | void | null ): void {// TODO: If deps are provided, should we skip comparing the ref itself?const effectDeps deps ! null deps ! undefined ? deps.concat([ref]) : null;let fiberFlags: Flags UpdateEffect | LayoutStaticEffect;mountEffectImpl(fiberFlags,HookLayout,imperativeHandleEffect.bind(null, create, ref),effectDeps); }mountImperativeHandle函数作为入口函数主要就是调用mountEffectImpl创建副作用 获取依赖并将ref第一个参数作为依赖添加以便后面对比判断是否更新获取Flag标识当前副作用通过bind绑定imperativeHandleEffect然后调用mountEffectImpl创建副作用 上面知道我们传入的第二个参数即create函数在调用时实际执行的imperativeHandleEffect函数来对ref进行处理,其中该函数逻辑如下 function imperativeHandleEffectT(create: () T,ref: { current: T | null } | ((inst: T | null) mixed) | null | void ): void | (() void) {if (typeof ref function) {const refCallback ref;const inst create(); // 创建实例const refCleanup refCallback(inst); // 执行 refCallback 并返回结果return () {if (typeof refCleanup function) {refCleanup(); // 如果 refCleanup 是函数则调用它} else {refCallback(null); // 否则调用 refCallback(null) 清除引用}};} else if (ref ! null ref ! undefined) {const refObject ref;const inst create();refObject.current inst;return () {refObject.current null;};} }从代码能看出来由于ref可以是对象、或者函数所以这里进行了差别处理。当为对象时会将暴露的对象绑定在current中即可以通过ref.current来访问暴露的属性然后会返回一个清除函数在组件卸载时会调用来清除引用便于垃圾收回。当ref是函数时会讲create执行结果作为入参传递给ref函数然后自行处理通过ref.current不能访问,根据返回的值是否是函数判断进一步处理清除函数方便垃圾回收。 mountEffectImpl函数如下 function mountEffectImpl(fiberFlags: Flags,hookFlags: HookFlags,create: () (() void) | void,deps: Arraymixed | void | null ): void {const hook mountWorkInProgressHook();const nextDeps deps undefined ? null : deps;currentlyRenderingFiber.flags | fiberFlags;hook.memoizedState pushEffect(HookHasEffect | hookFlags,create,createEffectInstance(),nextDeps); }主要就是通过mountWorkInProgressHook基于当前fiber创建一个初始化hook然后将依赖和create传入pushEffect处理副作用列表。 pushEffect函数如下 function pushEffect(tag: HookFlags,create: () (() void) | void,inst: EffectInstance,deps: Arraymixed | null ): Effect {const effect: Effect {tag,create,inst,deps,// Circularnext: (null: any),};let componentUpdateQueue: null | FunctionComponentUpdateQueue (currentlyRenderingFiber.updateQueue: any);// 首次渲染时 为nullif (componentUpdateQueue null) {componentUpdateQueue createFunctionComponentUpdateQueue();currentlyRenderingFiber.updateQueue (componentUpdateQueue: any);componentUpdateQueue.lastEffect effect.next effect;} else {const lastEffect componentUpdateQueue.lastEffect;if (lastEffect null) {componentUpdateQueue.lastEffect effect.next effect;} else {const firstEffect lastEffect.next;lastEffect.next effect;effect.next firstEffect;componentUpdateQueue.lastEffect effect;}}return effect; }createFunctionComponentUpdateQueue () {return {lastEffect: null,events: null,stores: null,memoCache: null,}; };主要逻辑就是根据当前配置创建effect副作用并将其添加到更新队列updateQueue中。在代码中通过判断 currentlyRenderingFiber.updateQueue是否为null来判断当前是否有其他的更新任务如果没有则通过createFunctionComponentUpdateQueue创建初始更新队列反之则直接添加到链表尾部。 updateQueue更新队列也是是通过lastEffect指向尾节点的循环链表可以更好的进行插入和快速找到头节点 至此我们介绍了在mount阶段依次调用的函数链 mountImperativeHandle - mountEffectImpl - mountWorkInProgressHook - pushEffect 最终初始化构建了从fiber到更新的链式关系。其中本次需要更新的状态保存在updateQueue中而memoizedState中保存的是上一次渲染更新的状态为了方便状态的追踪和新状态的基准值。 Update更新渲染 在这里先介绍下函数调用关系然后再针对该调用链以此介绍。通过dispatcher派发之后函数调用如下updateImperativeHandle - updateEffectImpl - imperativeHandleEffect - updateWorkInProgressHook - pushEffect 其中 imperativeHandleEffect和pushEffect在Mount阶段已经讲过所以这里就跳过主要介绍其他函数。 updateImperativeHandle函数 function updateImperativeHandleT(ref: { current: T | null } | ((inst: T | null) mixed) | null | void,create: () T,deps: Arraymixed | void | null ): void {// TODO: If deps are provided, should we skip comparing the ref itself?const effectDeps deps ! null deps ! undefined ? deps.concat([ref]) : null;updateEffectImpl(UpdateEffect,HookLayout,imperativeHandleEffect.bind(null, create, ref),effectDeps); }从代码能看出该函数主要工作就是调用updateEffectImpl来处理副作用 获取deps依赖数组并将ref第一个参数作为依赖添加绑定imperativeHandleEffect处理ref并调用updateEffectImpl更新副作用列表 updateEffectImpl函数 function updateEffectImpl(fiberFlags: Flags,hookFlags: HookFlags,create: () (() void) | void,deps: Arraymixed | void | null ): void {const hook updateWorkInProgressHook();const nextDeps deps undefined ? null : deps;const effect: Effect hook.memoizedState;const inst effect.inst;// currentHook is null on initial mount when rerendering after a render phase// state update or for strict mode.// 如果 currentHook 存在表示这是一个更新操作否则是一个初始化操作。if (currentHook ! null) {if (nextDeps ! null) {const prevEffect: Effect currentHook.memoizedState;const prevDeps prevEffect.deps;if (areHookInputsEqual(nextDeps, prevDeps)) {// 依赖没有变化则传入hookFlags不需要更新hook.memoizedState pushEffect(hookFlags, create, inst, nextDeps);return;}}}// 通过位或运算更新flag表示当前fiber需要更新currentlyRenderingFiber.flags | fiberFlags;hook.memoizedState pushEffect(HookHasEffect | hookFlags,create,inst,nextDeps); }// 通过for循环遍历依赖数组然后通过Object.is判断是否变化 function areHookInputsEqual(nextDeps: Arraymixed,prevDeps: Arraymixed | null,): boolean {if (prevDeps null) {return false;}// $FlowFixMe[incompatible-use] found when upgrading Flowfor (let i 0; i prevDeps.length i nextDeps.length; i) {// $FlowFixMe[incompatible-use] found when upgrading Flowif (is(nextDeps[i], prevDeps[i])) {continue;}return false;}return true;}该函数主要功能就是创建更新任务然后添加到Hook中并对比deps是否变化来决定是否触发更新最后更新memoizedState缓存状态。 通过updateWorkInProgressHook函数复用Hook并添加更新任务通过areHookInputsEqual对比依赖变化通过传入Flag来判断是否跳过更新通过pushEffect添加副作用并更新memoizedState缓存值 updateWorkInProgressHook函数在前面文章已经介绍过主要就是复用Hook链表优先复用workInProgress中的Hook没有则克隆当前页面显示的current Hook 详情可以查看这篇文章:【React Hooks原理 - useState】 Hook数据结构中和fiber数据结构中都有memoizedState字段但是表达的意义不同Hook中是作为缓存的state值但是fiber中是指向的当前fiber下的hooks队列的首个hookhook是链表结构指向首个就意味着可以访问整个hooks队列 至此Mount阶段和Update阶段就介绍完了总的来说就是在Mount阶段进行初始化在Update阶段创建更新任务添加到更新列表等待Scheduler调度更新。 总结 总的来说React默认不允许父组件访问子组件中的DOM所以需要通过forwardRef来将ref注入到子组件中通过在子组件中绑定dom来让父组件访问。但是我们又想自定义暴露哪些属性所以需要useImperativeHandle这个Hook来帮助完成。 forwardRef的本质就是返回一个带有特定标识符$$typeof的对象React根据这个表示知道当前组件是ForWardRef类型则会在执行函数组件渲染时将ref作为第二个参数传入即Component(props, ref) useImperativeHandle可以理解为这个Hook是对forwardRef传入的ref进行了拦截根据不同数据类型的ref做了不同处理对于对象类型直接将暴露的对象绑定到ref.current中因为ref是通过useRef创建默认会带有current属性而函数类型则将暴露的对象作为ref函数的入参由开发者自行控制。所以ref是对象时父组件可以通过ref.curren访问而ref是函数时则需要根据设置访问此时ref.current null。
http://www.hkea.cn/news/14318563/

相关文章:

  • 沭阳网站建设方案建网站 发信息 做推广
  • 谷歌怎么做公司网站如何做vip影视网站
  • 杭州做网站哪个公司好天津网站定制
  • 外贸网站模板 外贸网站制作网上购物网站开发报价
  • 企业网站seo哪里好wordpress 数字不连续
  • 网站建设时间进度表ai效果图网站
  • 海口网站建设推广pre_get_posts wordpress
  • 用别人网站做app的危害免费建设网站
  • 基于php的网站开发设计本地网站更新不了 vps登陆可以
  • 网站建设seo优化推广展陈公司排名
  • 百度网站域名费一年多少钱上海市建设工程安全协会网站
  • 免费学服装设计的网站网站主机是什么意思
  • 郑州网站制作哪家好一级a做爰片迅雷网站
  • 做网站和编程有关系吗大连网站建设仟亿科技
  • 百度风云榜电视剧排行榜seo技术教程博客
  • 哪家微信网站建设好直播互动
  • 织梦视频网站源码wordpress 微信 登陆
  • 网站怎么做跳转页面网站建设广东
  • 南昌商城网站建设上海建网站价格
  • 焦作建设银行网站什么职位做网站
  • 网站建设规划书中的技术可行性不包括企业网站cms源码
  • 建设自己的网站需要哪些步骤下载优化大师
  • 购物网站风格昆明网站推广公司
  • 深圳做网站公司哪家比较好网站静态页面做网站
  • wordpress 下载网站wordpress event
  • 响应式视频网站模板下载网站建设发展
  • 佛山网站建设模板河南建设工程信息网官网首页
  • 三国曹魏古城建设网站建设网站的企业费用
  • 第三方商城网站建设天津自助建站
  • 安徽伟诚建设工程有限公司网站黄骅贴吧2020招聘信息