写点什么

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:164182

评论

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

大批量更新数据mysql批量更新的四种方法

大数据技术指南

11月日更

NDPQ(NDP+PQ),定义分布式数据库新方向

华为云开发者联盟

数据库 查询 GaussDB(for MySQL) 近数据处理 NDP

Python量化数据仓库搭建系列2:Python操作数据库

恒生LIGHT云社区

Python 量化

数仓开发详细剖析

五分钟学大数据

11月日更

云原生数据库TDSQL-C关键技术内核解密

腾讯云数据库

tdsql 国产数据库

数据分析从零开始实战,Pandas读取HTML页面+数据处理解析

老表

Python 数据分析 数据处理 11月日更

从社区贡献者到加入核心团队,开源给他带来了这些变化

TDengine

tdengine 时序数据库

Rainbond通过插件整合SkyWalking,实现APM即插即用

北京好雨科技有限公司

Kubernetes 云原生 全链路追踪

博文推荐|深入解析 Apache Pulsar 中的事务

Apache Pulsar

大数据 架构 分布式 云原生 Apache Pulsar

Python代码阅读(第65篇):根据条件应用指定函数

Felix

Python 编程 Code 阅读代码 Python初学者

并发编程中,你加的锁未必安全

华为云开发者联盟

线程 高并发 并发 线程安全

一些常用的hive sql函数

编程江湖

大数据 hive Hive SQL

TDSQL演进与突破:把企业级分布式数据库做到极致

腾讯云数据库

tdsql 国产数据库

Java SPI 机制从原理到实战

江璇Up

Java spi JavaSPI

测试不趁早,“持续测试”搞不好

SoFlu软件机器人

DevOps 敏捷开发 自动化测试

为云而生,云原生数据库TDSQL-C技术突破与演进

腾讯云数据库

tdsql 国产数据库

安全经典JWT算法漏洞

网络安全学海

网络安全 信息安全 渗透测试 WEB安全 安全漏洞

技术探究:Apache Pulsar 的事务型事件流

Apache Pulsar

Java 架构 分布式 云原生 Apache Pulsar

很好用的压测工具 - Apache Bench工具

恒生LIGHT云社区

测试 AB AB testing实战

聊聊 ab 和 jmeter 的并发模型

恒生LIGHT云社区

测试 AB

一文讲透一致性哈希的原理和实现

万俊峰Kevin

微服务 高并发 哈希算法 go-zero Go 语言

Linux一学就会之文件的基本管理和XFS文件系统备份恢复

学神来啦

Linux 运维 文件系统 linux云计算 linux基础

TDSQL | 国产化浪潮下,数据库+云如何跑上核心业务?

腾讯云数据库

tdsql 国产数据库

使用 Litho 改进 News Feed 上的 Android 视频表现

CatTalk

android facebook 性能优化 Feed流 Litho

前端开发之JavaScript优化

@零度

JavaScript 大前端

Google I/O 2021 移动和 Web应用上的设备端机器学习

CatTalk

机器学习 tensorflow android Google

Java开发中常用的消息队列工具 ActiveMQ

编程江湖

Activemq Java 开发

处理可能超时的异步操作

编程江湖

大前端

大数据开发之Hive

@零度

大数据 hive

万字讲解WiFi为何物

华为云开发者联盟

wifi 物联网 无线通信 传输 无线

Google I/O 2021 What's new in Android Machine Learning

CatTalk

机器学习 tensorflow android Google

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