diff --git a/libs/horizon/src/dom/validators/PropertiesData.ts b/libs/horizon/src/dom/validators/PropertiesData.ts
new file mode 100644
index 00000000..c11e5fdf
--- /dev/null
+++ b/libs/horizon/src/dom/validators/PropertiesData.ts
@@ -0,0 +1,125 @@
+/* eslint-disable no-sparse-arrays */
+
+// 属性值的数据类型
+export enum PROPERTY_TYPE {
+ BOOLEAN, // 普通布尔类型
+ STRING, // 普通的字符串类型
+ SPECIAL, // 需要特殊处理的属性类型
+ BOOLEAN_STR, // 字符串类型的 true false
+};
+
+export type PropDetails = {
+ propName: string,
+ type: PROPERTY_TYPE,
+ attrName: string,
+ attrNS: string | null,
+};
+
+export function getPropDetails(name: string): PropDetails | null {
+ return propsDetailData[name] || null;
+}
+
+// 属性相关数据
+// 依次为 propertyName、type、attributeName、attributeNamespace,不填则使用默认值
+// type 默认 STRING
+// attributeName 默认与 propertyName 相同
+// attributeNamespace 默认 null
+const propertiesData = [
+ // 一些特殊属性
+ ['children', PROPERTY_TYPE.SPECIAL],
+ ['dangerouslySetInnerHTML', PROPERTY_TYPE.SPECIAL],
+ ['defaultValue', PROPERTY_TYPE.SPECIAL],
+ ['defaultChecked', PROPERTY_TYPE.SPECIAL],
+ ['innerHTML', PROPERTY_TYPE.SPECIAL],
+ ['style', PROPERTY_TYPE.SPECIAL],
+
+ // propertyName 和 attributeName 不一样
+ ['acceptCharset', , 'accept-charset'],
+ ['className', , 'class'],
+ ['htmlFor', , 'for'],
+ ['httpEquiv', , 'http-equiv'],
+ // 字符串类型的 true false
+ ['contentEditable', PROPERTY_TYPE.BOOLEAN_STR, 'contenteditable'],
+ ['spellCheck', PROPERTY_TYPE.BOOLEAN_STR, 'spellcheck'],
+ ['draggable', PROPERTY_TYPE.BOOLEAN_STR],
+ ['value', PROPERTY_TYPE.BOOLEAN_STR],
+ // SVG 相关,字符串类型的 true false
+ ['autoReverse', PROPERTY_TYPE.BOOLEAN_STR],
+ ['externalResourcesRequired', PROPERTY_TYPE.BOOLEAN_STR],
+ ['focusable', PROPERTY_TYPE.BOOLEAN_STR],
+ ['preserveAlpha', PROPERTY_TYPE.BOOLEAN_STR],
+ // 布尔类型
+ ['allowFullScreen', PROPERTY_TYPE.BOOLEAN, 'allowfullscreen'],
+ ['async', PROPERTY_TYPE.BOOLEAN],
+ ['autoFocus', PROPERTY_TYPE.BOOLEAN, 'autofocus'],
+ ['autoPlay', PROPERTY_TYPE.BOOLEAN, 'autoplay'],
+ ['controls', PROPERTY_TYPE.BOOLEAN],
+ ['default', PROPERTY_TYPE.BOOLEAN],
+ ['defer', PROPERTY_TYPE.BOOLEAN],
+ ['disabled', PROPERTY_TYPE.BOOLEAN],
+ ['disablePictureInPicture', PROPERTY_TYPE.BOOLEAN, 'disablepictureinpicture'],
+ ['disableRemotePlayback', PROPERTY_TYPE.BOOLEAN, 'disableremoteplayback'],
+ ['formNoValidate', PROPERTY_TYPE.BOOLEAN, 'formnovalidate'],
+ ['hidden', PROPERTY_TYPE.BOOLEAN],
+ ['loop', PROPERTY_TYPE.BOOLEAN],
+ ['noModule', PROPERTY_TYPE.BOOLEAN, 'nomodule'],
+ ['noValidate', PROPERTY_TYPE.BOOLEAN, 'novalidate'],
+ ['open', PROPERTY_TYPE.BOOLEAN],
+ ['playsInline', PROPERTY_TYPE.BOOLEAN, 'playsinline'],
+ ['readOnly', PROPERTY_TYPE.BOOLEAN, 'readonly'],
+ ['required', PROPERTY_TYPE.BOOLEAN],
+ ['reversed', PROPERTY_TYPE.BOOLEAN],
+ ['scoped', PROPERTY_TYPE.BOOLEAN],
+ ['seamless', PROPERTY_TYPE.BOOLEAN],
+ ['itemScope', PROPERTY_TYPE.BOOLEAN, 'itemscope'],
+ // 框架需要当做 property 来处理的,而不是 attribute 来处理的属性
+ ['checked', PROPERTY_TYPE.BOOLEAN],
+ ['multiple', PROPERTY_TYPE.BOOLEAN],
+ ['muted', PROPERTY_TYPE.BOOLEAN],
+ ['selected', PROPERTY_TYPE.BOOLEAN],
+
+ // SVG 属性
+ // xlink namespace 的 SVG 属性
+ ['xlinkActuate', , 'xlink:actuate', 'http://www.w3.org/1999/xlink'],
+ ['xlinkArcrole', , 'xlink:arcrole', 'http://www.w3.org/1999/xlink'],
+ ['xlinkRole', , 'xlink:role', 'http://www.w3.org/1999/xlink'],
+ ['xlinkShow', , 'xlink:show', 'http://www.w3.org/1999/xlink'],
+ ['xlinkTitle', , 'xlink:title', 'http://www.w3.org/1999/xlink'],
+ ['xlinkType', , 'xlink:type', 'http://www.w3.org/1999/xlink'],
+ // xml namespace 的 SVG 属性
+ ['xmlBase', , 'xml:base', 'http://www.w3.org/XML/1998/namespace'],
+ ['xmlLang', , 'xml:lang', 'http://www.w3.org/XML/1998/namespace'],
+ ['xmlSpace', , 'xml:space', 'http://www.w3.org/XML/1998/namespace'],
+ // HTML and SVG 中都有的属性,大小写敏感
+ ['tabIndex', , 'tabindex'],
+ ['crossOrigin', , 'crossorigin'],
+ // 接受 URL 的属性
+ ['xlinkHref', , 'xlink:href', 'http://www.w3.org/1999/xlink'],
+ ['formAction', , 'formaction'],
+];
+
+const propsDetailData = {};
+
+propertiesData.forEach(record => {
+ const propName = record[0];
+ let [type, attrName, attrNS] = record.slice(1);
+
+ if (type === undefined) {
+ type = PROPERTY_TYPE.STRING;
+ }
+
+ if (!attrName) {
+ attrName = propName;
+ }
+
+ if (!attrNS) {
+ attrNS = null;
+ }
+
+ propsDetailData[propName] = {
+ propName,
+ type,
+ attrName,
+ attrNS,
+ };
+});
diff --git a/libs/horizon/src/dom/validators/ValidateProps.ts b/libs/horizon/src/dom/validators/ValidateProps.ts
new file mode 100644
index 00000000..d04aec3e
--- /dev/null
+++ b/libs/horizon/src/dom/validators/ValidateProps.ts
@@ -0,0 +1,117 @@
+import {
+ getPropDetails, PROPERTY_TYPE, PropDetails,
+} from './PropertiesData';
+
+const INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/;
+
+
+// 是内置元素
+export function isNativeElement(tagName: string, props: Object) {
+ return !tagName.includes('-') && props.is === undefined;
+}
+
+function isInvalidBoolean(
+ attributeName: string,
+ value: any,
+ propDetails: PropDetails,
+): boolean {
+ if (propDetails.type === PROPERTY_TYPE.SPECIAL) {
+ return false;
+ }
+
+ // 布尔值校验
+ if (typeof value === 'boolean') {
+ const isBooleanType = propDetails.type === PROPERTY_TYPE.BOOLEAN_STR || propDetails.type === PROPERTY_TYPE.BOOLEAN;
+
+ if (isBooleanType || (attributeName.startsWith('data-') && attributeName.startsWith('aria-'))) {
+ return false;
+ }
+
+ // 否则有问题
+ return true;
+ }
+
+ return false;
+}
+
+function isValidProp(tagName, name, value) {
+ // 校验事件名称
+ if (isEventProp(name)) {
+ // 事件名称不满足小驼峰
+ if (INVALID_EVENT_NAME_REGEX.test(name)) {
+ console.error('Invalid event property `%s`, events use the camelCase name.', name);
+ }
+ return true;
+ }
+
+ const propDetails = getPropDetails(name);
+
+ // 当已知属性为错误类型时发出警告
+ if (propDetails !== null && isInvalidBoolean(name, value, propDetails)) {
+ return false;
+ }
+
+ return true;
+}
+
+export function isInvalidValue(
+ name: string,
+ value: any,
+ propDetails: PropDetails | null,
+ isNativeTag: boolean,
+): boolean {
+ if (value == null) {
+ return true;
+ }
+
+ if (!isNativeTag) {
+ return false;
+ }
+
+ if (propDetails !== null && isInvalidBoolean(name, value, propDetails)) {
+ return true;
+ }
+
+ if (propDetails !== null && propDetails.type === PROPERTY_TYPE.BOOLEAN) {
+ return !value;
+ }
+
+ return false;
+}
+
+// 是事件属性
+export function isEventProp(propName) {
+ return propName.substr(0, 2) === 'on';
+}
+
+// dev模式下校验属性是否合法
+export function validateProps(type, props) {
+ if (!props) {
+ return;
+ }
+
+ // 非内置的变迁
+ if (!isNativeElement(type, props)) {
+ return;
+ }
+
+ // style属性必须是对象
+ if (props.style != null && typeof props.style !== 'object') {
+ throw new Error('style should be a object.');
+ }
+
+ if (__DEV__) {
+ // 校验属性
+ const invalidProps = Object.keys(props).filter(key => !isValidProp(type, key, props[key]));
+
+ const propString = invalidProps.map(prop => '`' + prop + '`').join(', ');
+
+ if (invalidProps.length >= 1) {
+ console.error(
+ 'Invalid value for prop %s on <%s> tag.',
+ propString,
+ type,
+ );
+ }
+ }
+}
diff --git a/libs/horizon/src/dom/valueHandler/InputValueHandler.ts b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts
new file mode 100644
index 00000000..5632085f
--- /dev/null
+++ b/libs/horizon/src/dom/valueHandler/InputValueHandler.ts
@@ -0,0 +1,96 @@
+import {updateCommonProp} from '../DOMPropertiesHandler/UpdateCommonProp';
+import {getVNodeProps} from '../DOMInternalKeys';
+import {IProperty} from '../utils/Interface';
+import {getRootElement} from '../utils/Common';
+import {isInputValueChanged} from './ValueChangeHandler';
+
+function getInitValue(dom: HTMLInputElement, properties: IProperty) {
+ const {value, defaultValue, checked, defaultChecked} = properties;
+
+ const defaultValueStr = defaultValue != null ? defaultValue : '';
+ const initValue = value != null ? value : defaultValueStr;
+ const initChecked = checked != null ? checked : defaultChecked;
+
+ return {initValue, initChecked};
+}
+
+export function getInputPropsWithoutValue(dom: HTMLInputElement, properties: IProperty) {
+ // checked属于必填属性,无法置空
+ let {checked} = properties;
+ if (checked == null) {
+ checked = getInitValue(dom, properties).initChecked;
+ }
+
+ return {
+ ...properties,
+ value: undefined,
+ defaultValue: undefined,
+ defaultChecked: undefined,
+ checked,
+ };
+}
+
+export function updateInputValue(dom: HTMLInputElement, properties: IProperty) {
+ const {value, checked} = properties;
+
+ if (checked != null) {
+ updateCommonProp(dom, 'checked', checked);
+ } else if (value != null) { // 处理 dom.value 逻辑
+ if (dom.value !== String(value)) {
+ dom.value = String(value);
+ }
+ }
+}
+
+// 设置input的初始值
+export function setInitInputValue(dom: HTMLInputElement, properties: IProperty) {
+ const {value, defaultValue} = properties;
+ const {initValue, initChecked} = getInitValue(dom, properties);
+
+ if (value != null || defaultValue != null) {
+ // value 的使用优先级 value 属性 > defaultValue 属性 > 空字符串
+ const initValueStr = String(initValue);
+
+ dom.value = initValueStr;
+
+ dom.defaultValue = initValueStr;
+ }
+
+ // checked 的使用优先级 checked 属性 > defaultChecked 属性 > false
+ dom.defaultChecked = Boolean(initChecked);
+}
+
+export function resetInputValue(dom: HTMLInputElement, properties: IProperty) {
+ const {name, type} = properties;
+ // 如果是 radio,先更新相同 name 的 radio
+ if (type === 'radio' && name != null) {
+ // radio 的根节点
+ const radioRoot = getRootElement(dom);
+
+ const radioList = radioRoot.querySelectorAll(`input[type="radio"]`);
+
+ for (let i = 0; i < radioList.length; i++) {
+ const radio = radioList[i];
+ // @ts-ignore
+ if (radio.name !== name) {
+ continue;
+ }
+ if (radio === dom) {
+ continue;
+ }
+ // @ts-ignore
+ if (radio.form !== dom.form) {
+ continue;
+ }
+
+ // @ts-ignore
+ const nonHorizonRadioProps = getVNodeProps(radio);
+
+ isInputValueChanged(radio);
+ // @ts-ignore
+ updateInputValue(radio, nonHorizonRadioProps);
+ }
+ } else {
+ updateInputValue(dom, properties);
+ }
+}
diff --git a/libs/horizon/src/dom/valueHandler/OptionValueHandler.ts b/libs/horizon/src/dom/valueHandler/OptionValueHandler.ts
new file mode 100644
index 00000000..bd487d4e
--- /dev/null
+++ b/libs/horizon/src/dom/valueHandler/OptionValueHandler.ts
@@ -0,0 +1,21 @@
+import * as Horizon from '../../external/Horizon';
+import {IProperty} from '../utils/Interface';
+
+// 把 const a = 'a'; 转成 giraffe
+function concatChildren(children) {
+ let content = '';
+ Horizon.Children.forEach(children, function(child) {
+ content += child;
+ });
+
+ return content;
+}
+
+export function getOptionPropsWithoutValue(dom: Element, properties: IProperty) {
+ const content = concatChildren(properties.children);
+
+ return {
+ ...properties,
+ children: content || undefined, // 覆盖children
+ };
+}
diff --git a/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts
new file mode 100644
index 00000000..6602e876
--- /dev/null
+++ b/libs/horizon/src/dom/valueHandler/SelectValueHandler.ts
@@ -0,0 +1,70 @@
+import {HorizonSelect, IProperty} from '../utils/Interface';
+
+// 更新