DOMProperty模块用于加载节点属性插件,最终影响DOMPropertyOperation模块对节点属性的添加移除处理。
'use strict'; var _prodInvariant = require('./reactProdInvariant'); var invariant = require('fbjs/lib/invariant'); // 获取是否以node[propName]=value方式添加属性,或属性值处理方式 function checkMask(value, bitmask) { return (value & bitmask) === bitmask; } var DOMPropertyInjection = { MUST_USE_PROPERTY: 0x1,// 0x 16位 以node[propName]=value方式添加属性 HAS_BOOLEAN_VALUE: 0x4,// 过滤否值 HAS_NUMERIC_VALUE: 0x8,// 过滤非数值型 HAS_POSITIVE_NUMERIC_VALUE: 0x10 | 0x8,// 过滤非正数型 HAS_OVERLOADED_BOOLEAN_VALUE: 0x20,// 过滤false /**节点属性插件的写法,即含有的属性: * * isCustomAttribute: 函数,返回真值,将添加到节点的属性,如HTMLDOMPropertyConfig模块的data-、aria- * * Properties: 设定属性值类型处理方式的集合,如某属性值为0x4,否值将不会添加为节点的属性 * * DOMAttributeNames: 键值对存储属性名,以字符串形式拼接属性名及其值时使用 * * DOMAttributeNamespaces: 键值对约定属性命名空间的集合 * * DOMPropertyNames: Properties中属性设为0x1,即MUST_USE_PROPERTY时,node[propName]=value方式添加属性的属性名集合 * * DOMMutationMethods: 存储设定属性值的方法集,如{propName:(node,value)=>{}} */ // 加载节点属性插件,为DOMProperty.properties、DOMProperty._isCustomAttributeFunctions注入内容 injectDOMPropertyConfig: function (domPropertyConfig) { var Injection = DOMPropertyInjection; // 设定属性值类型处理方式的集合,如某属性值为0x4,否值将不会添加为节点的属性 var Properties = domPropertyConfig.Properties || {}; // 约定属性命名空间的集合 var DOMAttributeNamespaces = domPropertyConfig.DOMAttributeNamespaces || {}; // react属性名到浏览器节点属性名的映射,如{className:"class"} var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {}; // 存储属性的命名空间 var DOMPropertyNames = domPropertyConfig.DOMPropertyNames || {}; // 存储设定属性值的方法集,如{propName:(node,value)=>{}} var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {}; // 自定义属性校验,通过则将添加到节点的相应属性上,如HTMLDOMPropertyConfig模块的data-、aria- if (domPropertyConfig.isCustomAttribute) { DOMProperty._isCustomAttributeFunctions.push(domPropertyConfig.isCustomAttribute); } for (var propName in Properties) { // 同名属性不能加载两次,也即各节点属性插件的属性名不能相冲 !!DOMProperty.properties.hasOwnProperty(propName) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'injectDOMPropertyConfig(...):' + ' You\'re trying to inject DOM property \'%s\' which has already been injected.' + ' You may be accidentally injecting the same DOM property config twice,' + ' or you may be injecting two configs that have conflicting property names.', propName) : _prodInvariant('48', propName) : void 0; var lowerCased = propName.toLowerCase(); // 节点属性插件中,propName指代属性名,Properties[propName]约定类型 // Properties[propName]同DOMPropertyInjection.HAS_NUMERIC_VALUE等属性作按位与比较 // 两者等值时,属性值须设为DOMPropertyInjection.HAS_NUMERIC_VALUE等属性的字面类型 // 即DOMPropertyInjection.HAS_NUMERIC_VALUE要求节点的属性为数值 var propConfig = Properties[propName]; var propertyInfo = { // 拼接字符串方式添加节点属性时,作为属性名 attributeName: lowerCased, // 属性的命名空间,node.setAttributeNS(namespace,attr,value)添加属性;初始化为null attributeNamespace: null, // propertyInfo.mustUseProperty为真值时,node[propertyInfo[propertyName]]设置节点的属性时作为节点的属性名 propertyName: propName, // 设置属性值的方法,(node,value)=>{}形式;初始化为null mutationMethod: null, // 为真值时,以node[propertyInfo[propertyName]]设置节点的属性,而非setAttribute方法 // 处理ie8、9setAttribute方法将属性值转化为字符串`[object]`的兼容性问题 mustUseProperty: checkMask(propConfig, Injection.MUST_USE_PROPERTY), // 用于设定节点属性值的类型,如hasNumericValue属性为真值,属性值得类型即为数值型 hasBooleanValue: checkMask(propConfig, Injection.HAS_BOOLEAN_VALUE), hasNumericValue: checkMask(propConfig, Injection.HAS_NUMERIC_VALUE), hasPositiveNumericValue: checkMask(propConfig, Injection.HAS_POSITIVE_NUMERIC_VALUE), hasOverloadedBooleanValue: checkMask(propConfig, Injection.HAS_OVERLOADED_BOOLEAN_VALUE) }; // 属性值的类型设定相冲,如不能既是布尔型,又是数值型 !(propertyInfo.hasBooleanValue + propertyInfo.hasNumericValue + propertyInfo.hasOverloadedBooleanValue <= 1) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'DOMProperty: Value can be one of boolean, overloaded boolean,' + ' or numeric value, but not a combination: %s', propName) : _prodInvariant('50', propName) : void 0; if (process.env.NODE_ENV !== 'production') { DOMProperty.getPossibleStandardName[lowerCased] = propName; } // 节点属性插件的DOMAttributeNames属性是react属性名到浏览器节点属性名的映射,如className映射为class // 以字符串形式拼接属性名及其值时使用 if (DOMAttributeNames.hasOwnProperty(propName)) { var attributeName = DOMAttributeNames[propName]; propertyInfo.attributeName = attributeName; if (process.env.NODE_ENV !== 'production') { DOMProperty.getPossibleStandardName[attributeName] = propName; } } // 提取节点属性插件DOMAttributeNamespaces的命名空间 if (DOMAttributeNamespaces.hasOwnProperty(propName)) { propertyInfo.attributeNamespace = DOMAttributeNamespaces[propName]; } // DOMPropertyNames存放propertyInfo.mustUseProperty为真值时可添加的属性名集合 if (DOMPropertyNames.hasOwnProperty(propName)) { propertyInfo.propertyName = DOMPropertyNames[propName]; } // propertyInfo.mutationMethod设置属性值的方法,(node,value)=>{}形式 if (DOMMutationMethods.hasOwnProperty(propName)) { propertyInfo.mutationMethod = DOMMutationMethods[propName]; } DOMProperty.properties[propName] = propertyInfo; } } }; var ATTRIBUTE_NAME_START_CHAR = ':A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD'; var DOMProperty = { ID_ATTRIBUTE_NAME: 'data-reactid', ROOT_ATTRIBUTE_NAME: 'data-reactroot', // 用于校验属性名 ATTRIBUTE_NAME_START_CHAR: ATTRIBUTE_NAME_START_CHAR, ATTRIBUTE_NAME_CHAR: ATTRIBUTE_NAME_START_CHAR + '\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040', /**设定属性名添加或移除方式 * attributeName: 拼接字符串方式 * attributeNamespace: 属性的命名空间 * propertyName: mustUseProperty为真值时,node[propName]方式添加或移除属性时,作为propName * mutationMethod: 设置属性值的方法,(node,value)=>{}形式,优先级最高 * mustUseProperty: 是否以node[propertyInfo[propertyName]]设置节点的属性,而非setAttribute方法 * 设定属性值的提取方式 * hasBooleanValue: 属性值设为否值时不添加到dom元素上 * hasNumericValue: 属性值须设置为数值型或可转化成数值型的字符串,否则不添加到dom元素上 * hasPositiveNumericValue: 属性值须设置为正数,否则不添加到dom元素上 * hasOverloadedBooleanValue: 属性值为false时不添加到dom元素上 */ properties: {}, // 存储小写形式的节点属性到react节点属性的映射,如{class:"className"}等;调试用 getPossibleStandardName: process.env.NODE_ENV !== 'production' ? { autofocus: 'autoFocus' } : null, // 存储各节点属性插件的isCustomAttribute方法,如HTMLDOMPropertyConfig模块的isCustomAttribute方法 _isCustomAttributeFunctions: [], // 是否允许设置dom节点的自定义属性attributeName,如HTMLDOMPropertyConfig模块设定以data-或aria-起始的属性名 isCustomAttribute: function (attributeName) { for (var i = 0; i < DOMProperty._isCustomAttributeFunctions.length; i++) { var isCustomAttributeFn = DOMProperty._isCustomAttributeFunctions[i]; if (isCustomAttributeFn(attributeName)) { return true; } } return false; }, // 通过ReactDefaultInjection模块加载节点属性插件,如ARIADOMPropertyConfig、HTMLDOMPropertyConfig、SVGDOMPropertyConfig // 最终为当前模块的DOMProperty.properties、DOMProperty._isCustomAttributeFunctions注入内容 injection: DOMPropertyInjection }; module.exports = DOMProperty;