【AICon】探索RAG 技术在实际应用中遇到的挑战及应对策略!AICon精华内容已上线73%>>> 了解详情
写点什么

Antd Form 实现机制解析

  • 2021-03-13
  • 本文字数:10565 字

    阅读完需:约 35 分钟

Antd Form 实现机制解析

背景


在中后台业务中,表单页面基础的场景包括组件值的收集、校验和更新。在 to B 业务中,表单页面的需求往往更复杂和定制化,除了上述的基本操作,还要处理包括自定义表单组件、表单联动、表单的异步检验等复杂场景,在一些大型表单页面中还要考虑性能的问题,表单页面的需求往往是新同学摔得第一个跤。


本文分为两个部分,第一部分会通过对 Antd Form 源码的分析来帮助大家对 Form 的整体设计和流程有一个清晰的概念,第二部分会分享一些复杂场景的解决方案。希望可以帮助大家更容易的处理表单需求和快速定位表单场景中的问题。


本文并不涉及过于具体的源码实现分析,大家可以放松心情,一起来对 Form 有一个感性的认知吧~


Form 组件解决了什么问题


首先我们先看一个简单的表单,收集并校验两个组件的值。只需要通过监听两个表单组件的 onChange 事件,获取表单项的 value,根据定义的校验规则对 value 进行检验,生成检验状态和检验信息,再通过 setState 驱动视图更新,展示组件的值以及校验信息即可。


代码实现可能是这样的:


exportdefaultclass LoginForm extends React.Component {  state = {    username:{      value: '',      error: '',    },    password:{      value: '',      error: '',    },  }    fieldMeta = {    username:{    rules:[],  },    password:{    rules:[],   }, }    onInputChange = (e) => {   const { value,name } = e.target; 	// 获取校验结果   const error = this.doValidate(value, name);      this.setState({     [name]:{       value,       error,     }   })  }    validator = (value, rules) => {  	...  }    doValidate = (value, name) => {    // 读取校验规则    const { rules } = this.fieldMeta[name];    return validator(value,rules);  }
render() { const { username, password } = this.state; return (<div> <div> <Input onChange={this.onInputChange} name='username' value={username.value} /> </div> <div style={errorStyle}> {username.error} </div> <div> <Input onChange={this.onInputChange} name='password' value={password.value}/> </div> <div style={errorStyle}> {password.error} </div> <Button onClick={this.onSubmit}>登录</Button> </div>); }}
复制代码

用流程图来表示是这样的:


上面的实现,我们设定了一个表单数据状态的模型,来维护组件的 value 和校验的错误信息。


this.state = {  [field1]:{    value: '',    error: '',  },  [field2]:{    value: '',    error: '',  },  ...}
复制代码

还有一个字段配置相关的模型,维护组件对应的校验规则。

fieldMeta = {  username:{    rules:[]  },  password:{    rules:[],  }}
复制代码

对于这种简单的业务场景,上述方式完全可以满足需求。


具体到真实的业务场景,往往更复杂,其中包含多种表单组件,如 Input、Checkbox、Radio、Upload,还有一些自定义表单组件。



这个时候如果继续采用这种方式,不仅需要维护多个 onChange 事件,还要对不同组件 value 的取值做差异化处理,以及对各个组件的校验以及触发时机规则进行维护,就很容易出现“祖传代码”。


对表单场景进行归纳,可以发现每个组件的数据收集、校验、数据更新的流程其实是一致的。对这个流程进行抽象,并且通过一些配置屏蔽组件间的差异性,再对组件的值以及组件的配置规则统一管理,就是我们常见的 Form 表单的解决方案。


Antd Form 是怎么实现的


要实现上面的方案需要解决三个问题:


  • 如何实时收集内部组件的数据?

  • 如何对组件的数据进行校验?

  • 如何更新组件的数据?


下面我们就带着这三个问题,一起看看 Antd Form 是如何来做的吧~


先看一下 Form class 的结构,Form 组件有两个静态属性 Item、createFormField 和一个静态方法 create,Item 是对 FormItem 组件的引用,createFormField 指向 rc-form 提供的同名方法,create 方法则是对  rc-form createDOMForm 的调用,为了方便理解,这边隐藏了部分代码,Form class 整体的结构如下:


import * as React from'react';import * as PropTypes from'prop-types';import classNames from'classnames';import createDOMForm from'rc-form/lib/createDOMForm';import createFormField from'rc-form/lib/createFormField';import omit from'omit.js';import { ConfigConsumer, ConfigConsumerProps } from'../config-provider';import { tuple } from'../_util/type';import warning from'../_util/warning';import FormItem from'./FormItem';import { FIELD_META_PROP, FIELD_DATA_PROP } from'./constants';import FormContext from'./context';
const FormLayouts = tuple('horizontal', 'inline', 'vertical');
exportdefaultclass Form extends React.Component{ static defaultProps = { colon: true, layout: 'horizontal', hideRequiredMark: false, onSubmit(e: React.FormEvent<HTMLFormElement>) { e.preventDefault(); }, };
static propTypes = { prefixCls: PropTypes.string, layout: PropTypes.oneOf(FormLayouts), children: PropTypes.any, onSubmit: PropTypes.func, hideRequiredMark: PropTypes.bool, colon: PropTypes.bool, }; // Item 是对 FormItem 组件的引用 static Item = FormItem; // createFormField 指向 rc-form 提供的同名方法 static createFormField = createFormField;
// create 方法则是对 rc-form createDOMForm 的调用 static create = function create( options ){ return createDOMForm({ fieldNameProp: 'id', ...options, fieldMetaProp: FIELD_META_PROP, fieldDataProp: FIELD_DATA_PROP, }); };
constructor(props) { super(props); }
renderForm = ({ getPrefixCls }: ConfigConsumerProps) => { const { prefixCls: customizePrefixCls, hideRequiredMark, className = '', layout } = this.props; const prefixCls = getPrefixCls('form', customizePrefixCls); const formClassName = classNames( prefixCls, { [`${prefixCls}-horizontal`]: layout === 'horizontal', [`${prefixCls}-vertical`]: layout === 'vertical', [`${prefixCls}-inline`]: layout === 'inline', [`${prefixCls}-hide-required-mark`]: hideRequiredMark, }, className, );
const formProps = omit(this.props, [ 'prefixCls', 'className', 'layout', 'form', 'hideRequiredMark', 'wrapperCol', 'labelAlign', 'labelCol', 'colon', ]);
return<form {...formProps} className={formClassName} />; };
render() { const { wrapperCol, labelAlign, labelCol, layout, colon } = this.props; return ( <FormContext.Provider value={{ wrapperCol, labelAlign, labelCol, vertical: layout === 'vertical', colon }} > <ConfigConsumer>{this.renderForm}</ConfigConsumer> </FormContext.Provider> ); }}
复制代码



从 Form 的源码上看,组件本身并不涉及表单数据流程相关的逻辑,Form 组件以及 FormItem 主要处理布局方式、表单样式、属性必填样式、检验文案等视图层面的逻辑。


对数据的收集、校验、更新的流程的抽象以及组件数据管理主要由 rc-form 实现。下面我们继续来看一下核心的 rc-form 模块是怎样的结构。


rc-form 的 核心文件以及核心类图如下:



rc-form 核心逻辑可以从两个文件来看,createBaseForm.js、createFieldsStore.js。


createBaseForm.js 中暴露出的 createBaseForm 函数,创建了一个高阶组件 decorate,decorate 会为我们的目标组件包裹上一个容器组件,也就是上图中的核心类 BaseForm。


createFieldsStore.js 中暴露的 createFieldsStore 函数用来创建 FieldsStore 类的实例。FieldsStore 类可以理解为组件数据的管理中心,负责数据模型的初始化,并提供 Api 对组件数据进行更新和读取,以及获取组件数据的校验结果和数据更改状态。


Form 组件流程分析


我们通过 Antd Pro 中登录页面的实现来一起看一下,Form 内部的调用流程。


首先来看一下 Form 表单的用法:


class CustomizedForm extends React.Component {}
CustomizedForm = Form.create({})(CustomizedForm);

复制代码


我们有一个自定义组件 CustomizedForm,在使用 Form 表单的时候,我们会先调用 Form.create({})(CustomizedForm)。



初始化阶段


Form.create 函数指向 rc-form 提供的 createBaseForm 方法,createBaseForm 则创建了高阶组件 decorate。


decorate 的参数就是我们的 CustomizedForm 自定义组件。decorate 会创建一个被 BaseForm 组件包裹的自定义表单组件,经过包裹的组件将会自带 this.props.form 属性。为了方便记忆,我们把这个组件称为 FormHocCustomizedForm。


/***  rc-form/createBaseForm.js*/
// 默认的数据收集触发事件const DEFAULT_TRIGGER = 'onChange';
function createBaseForm(option = {}, mixins = []) { const { validateMessages, onFieldsChange, onValuesChange, mapProps = identity, mapPropsToFields, fieldNameProp, fieldMetaProp, fieldDataProp, formPropName = 'form', name: formName, // @deprecated withRef, } = option;
// 高阶组件 returnfunction decorate(WrappedComponent) { const Form = createReactClass({ mixins, ... render() { const { wrappedComponentRef, ...restProps } = this.props; // eslint-disable-line // 为目标组件注入 form props const formProps = { [formPropName]: this.getForm(), }; if (withRef) { if ( process.env.NODE_ENV !== 'production' && process.env.NODE_ENV !== 'test' ) { warning( false, '`withRef` is deprecated, please use `wrappedComponentRef` instead. ' + 'See: https://github.com/react-component/form#note-use-wrappedcomponentref-instead-of-withref-after-rc-form140', ); } formProps.ref = 'wrappedComponent'; } elseif (wrappedComponentRef) { formProps.ref = wrappedComponentRef; } const props = mapProps.call(this, { ...formProps, ...restProps, }); return<WrappedComponent {...props} />; }, }); return argumentContainer(unsafeLifecyclesPolyfill(Form), WrappedComponent); };}
export default createBaseForm;
复制代码



组件创建完成之后,FormHocCustomizedForm 就会经历 React 组件的生命周期。


getInitailState 阶段


Form 并没有通过内部的 state 来管理内部组件的值, 而且创建了 FieldsStore 实例,也就是上面提到的组件数据管理中心。



通过上面的类图我们可以看到 FieldsStore 包含两个属性,fields 和 fieldsMeta,fields 主要用来记录每个表单项的实时数据,包含以下属性:


  • dirty 数据是否已经改变,但未校验

  • errors 校验文案

  • name 字段名称

  • touched 数据是否更新过

  • value 字段的值

  • validating 校验状态



fieldsMeta 用来记录元数据,即每个字段对应组件的配置信息:


  • name 字段的名称

  • originalProps 被 getFieldDecorator() 装饰的组件的原始 props

  • rules 校验的规则

  • trigger 触发数据收集的时机 默认 onChange

  • validate 校验规则和触发事件

  • valuePropName 子节点的值的属性,例如 checkbox 应该设为 checked

  • getValueFromEvent 如何从 event 中获取组件的值

  • hidden 为 true 时,校验或者收集数据时会忽略这个字段



Render 阶段


被 Form 管理的组件,需要使用 props.form.getFieldDecorator 来包装,在 Render 阶段需要调用 getFieldDecorator 传入我们的组件配置,包括字段名 name 以及组件元数据 otherOptions,再将字段对应的组件传入 getFieldDecorator 返回的高阶组件。


{getFieldDecorator('name', otherOptions)(<Input />)}
复制代码


/** * rc-form/createBaseForm.js */			// getFieldDecorator 实际上创建了一个高阶组件,参数是字段对应的组件getFieldDecorator(name, fieldOption) {  // 装饰组件的 props  const props = this.getFieldProps(name, fieldOption);  returnfieldElem => {    // We should put field in record if it is rendered    this.renderFields[name] = true;
const fieldMeta = this.fieldsStore.getFieldMeta(name); const originalProps = fieldElem.props; // 校验细节略过 ... fieldMeta.originalProps = originalProps; fieldMeta.ref = fieldElem.ref; return React.cloneElement(fieldElem, { ...props, ...this.fieldsStore.getFieldValuePropValue(fieldMeta), }); };},
复制代码


经过 getFieldDecorator 包装的组件,表单组件会自动添加  value(或 valuePropName 指定的其他属性) onChange(或 trigger 指定的其他属性)属性,接下来的数据同步将被 Form 接管。


getFieldDecorator 主要用于装饰组件,其中调用的  getFieldProps 用于装饰 props,getFieldProps 会将组件的 (DEFAULT_TRIGGER = 'onChange')触发事件指向 FormHocCustomizedForm 的 onCollect 方法,并将配置的 validateTriggers 指向 onCollectValidate。在这个阶段还会收集组件的元数据,也就是我们调用  getFieldDecorator 中传入的 option 配置,这些配置会存入 fieldStore 的 fieldsMeta 对象中,作为组件的元数据。


这里我们就可以回答第一个问题,如何实时收集内部组件的数据?


Form 通过 getFieldDecorator 对组件进行包装,接管组件的 value 和 onChange 属性,当用户输入改变时,触发 onCollect 或 onCollectValidate 来收集组件最新的值。


用户输入


当用户输入触发组件的 onChange 或者其他的 trigger 事件时,执行 onCollect 或者  onCollectValidate,onCollect 执行组件数据收集,onCollectValidate 除了执行组件数据收集,还会根据配置的校验规则来校验组件,其中校验的流程并不由 rc-form 实现,而且通过引入第三方校验库  async-validator 执行。


onCollect 和 onCollectValidate 方法中收集数据的动作主要由 onCollectCommon 来处理。


/** * rc-form/createBaseForm.js */
onCollect(name_, action, ...args) { const { name, field, fieldMeta } = this.onCollectCommon( name_, action, args, ); const { validate } = fieldMeta;
this.fieldsStore.setFieldsAsDirty();
const newField = { ...field, dirty: hasRules(validate), }; this.setFields({ [name]: newField, });},
复制代码


onCollectCommon 负责组件数据的收集,在事件的回调中,通过默认的 getValueFromEvent 方法或者组件配置的 getValueFromEvent 方法,可以从参数 event 中正确的拿到组件的值。


/** * rc-form/createBaseForm.js */
onCollectCommon(name, action, args) { const fieldMeta = this.fieldsStore.getFieldMeta(name);
if (fieldMeta[action]) { fieldMeta[action](...args); } elseif (fieldMeta.originalProps && fieldMeta.originalProps[action]) { fieldMeta.originalProps[action](...args); } const value = fieldMeta.getValueFromEvent ? fieldMeta.getValueFromEvent(...args) : getValueFromEvent(...args); if (onValuesChange && value !== this.fieldsStore.getFieldValue(name)) { const valuesAll = this.fieldsStore.getAllValues(); const valuesAllSet = {}; valuesAll[name] = value; Object.keys(valuesAll).forEach(key => set(valuesAllSet, key, valuesAll[key]), ); onValuesChange( { [formPropName]: this.getForm(), ...this.props, }, set({}, name, value), valuesAllSet, ); } const field = this.fieldsStore.getField(name); return { name, field: { ...field, value, touched: true }, fieldMeta };},
复制代码


针对不同的组件取值差异,由 getValueFromEvent 方法来屏蔽。


/** * rc-form/utils.js */
// 默认的 getValueFromEventexportfunction getValueFromEvent(e) { // To support custom element if (!e || !e.target) { return e; } const { target } = e; return target.type === 'checkbox' ? target.checked : target.value;}
复制代码


收集并校验组件的值。


/** * rc-form/createBaseForm.js */
onCollectValidate(name_, action, ...args) { const { field, fieldMeta } = this.onCollectCommon(name_, action, args); // 获取组件最新的值 const newField = { ...field, dirty: true, };
this.fieldsStore.setFieldsAsDirty(); // 对组件最新的值 进行校验 this.validateFieldsInternal([newField], { action, options: { firstFields: !!fieldMeta.validateFirst, }, });},
复制代码


执行校验。


validateFieldsInternal(  fields,  { fieldNames, action, options = {} },  callback,) {  const allRules = {};  const allValues = {};  const allFields = {};  const alreadyErrors = {};  fields.forEach(field => {    const name = field.name;    if (options.force !== true && field.dirty === false) {      if (field.errors) {        set(alreadyErrors, name, { errors: field.errors });      }      return;    }    const fieldMeta = this.fieldsStore.getFieldMeta(name);    const newField = {      ...field,    };    newField.errors = undefined;    newField.validating = true;    newField.dirty = true;    // 从 fieldMeta 中读取校验规则    allRules[name] = this.getRules(fieldMeta, action);    allValues[name] = newField.value;    allFields[name] = newField;  });  // 校验前更新字段状态  this.setFields(allFields);  // in case normalize  Object.keys(allValues).forEach(f => {    allValues[f] = this.fieldsStore.getFieldValue(f);  });
// AsyncValidator 三方校验库 async-validator; const validator = new AsyncValidator(allRules); if (validateMessages) { validator.messages(validateMessages); } validator.validate(allValues, options, errors => { const errorsGroup = { ...alreadyErrors, }; if (errors && errors.length) { errors.forEach(e => { // 省略... const fieldErrors = get(errorsGroup, fieldName.concat('.errors')); fieldErrors.push(e); }); } const expired = []; const nowAllFields = {}; Object.keys(allRules).forEach(name => { const fieldErrors = get(errorsGroup, name); const nowField = this.fieldsStore.getField(name); // avoid concurrency problems if (!eq(nowField.value, allValues[name])) { expired.push({ name, }); } else { nowField.errors = fieldErrors && fieldErrors.errors; nowField.value = allValues[name]; nowField.validating = false; nowField.dirty = false; nowAllFields[name] = nowField; } }); // 检验完成 更新字段实时数据 this.setFields(nowAllFields); // ... });},
复制代码


到这里我们可以回答上面的第二个问题,如何对组件的数据进行校验?


当通过执行 onCollectCommon 完成了表单数据的收集,onCollectValidate 会调用 validateFieldsInternal 方法创建 AsyncValidator 的实例,由 AsyncValidator 根据组件的配置规则进行校验,并将最终的校验结果和表单数据更新到 fieldStore。


到这里就完成了表单数据的收集和校验的环节,已经拿到了表单最新的数据以及校验结果。


下一步,就是数据的更新,也就是将表单最新的值和校验相关的信息更新到视图上。


在 onCollect 和 validateFieldsInternal 方法中,我们看到最后一步调用了 setFields 来更新实时数据。


/** * rc-form/createBaseForm.js */
setFields(maybeNestedFields, callback) { const fields = this.fieldsStore.flattenRegisteredFields( maybeNestedFields, ); // 更新 fieldsStore this.fieldsStore.setFields(fields);
if (onFieldsChange) { const changedFields = Object.keys(fields).reduce( (acc, name) =>set(acc, name, this.fieldsStore.getField(name)), {}, ); onFieldsChange( { [formPropName]: this.getForm(), ...this.props, }, changedFields, this.fieldsStore.getNestedAllFields(), ); } // 更新 this.forceUpdate(callback);},
复制代码


setFields 方法将字段组件最新的数据更新到 fieldStore。此时 fieldStore 已经收集存储了组件最新的值,下面我们就需要更新组件,将数据正确的在界面上渲染出来。


可以看到,setFields 中最后调用了 React 组件提供的 forceUpdate 函数。这里可以回答第三个问题,如何更新组件的数据?


因为我们在最顶层的 FormHocCustomizedForm 组件中调用 forceUpdate,forceUpdate 会跳过 shouldComponentUpdate 触发组件的 Render 方法,进而触发所有子组件的更新流程。在子组件 Render 的执行过程中, getFieldDecorator 方法从 fieldStore 中读取实时的表单数据以及校验信息,并通过注入 value 或者  valuePropName 的值设定的属性来更新表单。


到这里,一个完整的 Form 数据收集、校验、更新流程就完成了,整个过程的流程图如下所示:



复杂表单场景的最佳实践


看完了上面的 Form 内部的运行流程,下面我们一起来看看 Form 还提供了哪些机制方便我们解决一些复杂场景问题。


嵌套数据结构收集


FieldStore 内部集成了  lodash/set,可以设置对象路径(eg: a.b.c 或者 a.b[0])为字段值,通过使用对象路径字段,我们可以很方便的实现嵌套数据结构值的收集。


<FormItem>  <Col span={16}>    {getFieldDecorator('nested.fieldObj.name')(<Input/>)}  </Col></FormItem><FormItem>  <Col span={16}>    {getFieldDecorator('nested.fieldArray[0].name')(<Input/>)}  </Col></FormItem>
复制代码


上面的代码中,我们通过对象路径的方式来设置 field,在获取表单值的时候已经被转换成了对应路径结构的对象或数组,如下面所示:


{  nested:{   fieldObj:{     name:'嵌套对象的值'   },   fieldArray:['嵌套数组的值']  }}
复制代码


自定义表单接入


上面的分析里提到,Form 通过接管组件的 value 和 onChange 事件来管理组件,想实现一个可以接入 Form 管理的组件,只需要满足下面三个条件


  • 提供受控属性 value 或其它与 valuePropName 的值同名的属性

  • 提供 onChange 事件或 trigger 的值同名的事件

  • 支持 ref:

  • React@16.3.0 之前只有 Class 组件支持

  • React@16.3.0 及之后可以通过 forwardRef 添加 ref 支持(https://codesandbox.io/s/7wj199900x


表单联动


组件的数据由 FieldStore 来统一管理,组件值变化时也会实时更新,所以结合 ES6 的 get 方法可以很简单的实现组件之间的联动。


class Linkage extends Component {  get showInput(){   return this.props.form.getFieldValue('checkbox');  }  render() {    const { form } = this.props;    const { getFieldDecorator } = form;    return (      <div>      {        getFieldDecorator('checkbox', {valuePropName: 'checked',})(          <Checkbox>show Input</Checkbox>        )      }      {       this.showInput && <Input/>	      }      </div>    );  }}
export default Form.create()(Linkage);
复制代码


总结


本文在流程上对 Form 组件的实现机制进行了解析,省略了里面的实现细节,大家对流程有一个整体认知之后,也可以自己翻阅 Form 的源码来了解实现细节。


Antd Form 具有很好的灵活性,可以帮我们快速的实现表单需求,但是也存在一些问题,比如当表单中的任何一个组件值发生改变,触发 onCollect 数据收集、执行更新流程,都会调用 forceUpdate 触发所有组件的更新。


在复杂表单业务,用户频繁的输入场景就会产生性能瓶颈。对于复杂的表单组件,我们可以通过拆分组件的粒度,通过 shouldComponentUpdate 来避免不必要的更新,或者修改组件的数据收集时机来减少数据的收集频率。当然这并不是很优雅的解决方案,在未来要发布的 Antd V4 版本中,Form 的底层实现已经替换为 rc-field-form(https://github.com/react-component/field-form),主页上的介绍是:


React Performance First Form Component.


大家也可以期待一下官方新版本的 Form 组件。



头图:Unsplash

作者:子洋

原文:https://mp.weixin.qq.com/s/LwBCJJzhT5uKB-ix4XkqbA

原文:Antd Form 实现机制解析

来源:政采云前端团队 - 微信公众号 [ID:Zoo-Team]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


2021-03-13 13:164056

评论

发布
暂无评论
发现更多内容

理解Java中对象基础Object类

Java JVM jdk8 Object

10分钟搞定OAuth2

Java 程序员 后端

10 K8S之名称空间

穿过生命散发芬芳

k8s 11月日更

150个超实用的网站,整理成资源库页面分享给大家

Java 程序员 后端

互联网 | 逻辑上的黑话才是真正的花里胡哨

程序员 互联网 黑话

阿里内部疯传的分布式架构手册,轻松吊打小日子过的不错的面试官

编程 程序员 架构 分布式

rfc2988:Computing TCP's Retransmission Timer

Changing Lin

11月日更

“平”地而起,2022中国企业数智服务市场趋势洞察报告即将发布

海比研究院

研发人员除了编码还能干什么?

卢卡多多

研发效能 11月日更

12 高可用的应用(1)

Java 程序员 后端

13万字!腾讯高工手写JDK源码笔记 带你飙向实战(1)

Java 程序员 后端

南瓜电影 7 天内全面 Serverless 化实践

阿里巴巴云原生

阿里云 Serverless 云原生 实践 南瓜电影

从欧拉捐赠中,读懂数字基建与数字中国

脑极体

《大教堂与集市》

石云升

读书笔记 开源 11月日更

NodeJs深入浅出之旅:异步I/O (下)🐂

空城机

大前端 Node 11月日更

ClickHouse深度解析,收藏这一篇就够了~

大数据老哥

gitlab-runner构建解决java缓存问题

ilinux

Java 设计模式 Monads 的美丽世界

码语者

Java 设计模式 Monads

Qcon 实时音视频专场:实时互动的最佳实践与未来展望

声网

人工智能 算法 音视频

-So-easy!多图详解CLH锁的原理与实现,轻松把握AQS

Java 程序员 后端

18道kafka高频面试题(附答案)

Java 程序员 后端

18张图揭秘高性能Linux服务器内存池技术是如何实现的

Java 程序员 后端

18级大数据专家,漫谈大数据平台安全风险与建设,值得学(下篇)

Java 程序员 后端

19年末从外包辞职,10000小时后,进字节跳动拿offer

Java 程序员 后端

第四范式OpenMLDB在金融风控数据库的计算优化实践

第四范式开发者社区

第四范式 开源技术 OpenMLDB datafun

大数据毕业作业

Clarke

统一开发平台通用管理端工程

中原银行

中原银行 管理端

150 道面试题:集合 +JVM+ 设计模式 +spring

Java 程序员 后端

15个顶级Java多线程面试题及答案

Java 程序员 后端

【死磕Java并发】-----Java内存模型之重排序

chenssy

11月日更 死磕 Java 死磕 Java 并发

13万字!腾讯高工手写JDK源码笔记 带你飙向实战

Java 程序员 后端

Antd Form 实现机制解析_语言 & 开发_政采云前端团队_InfoQ精选文章