-
Notifications
You must be signed in to change notification settings - Fork 316
Closed
Description
原来非受控组件的if else分支太多,会影响效率,也影响检测程序的评估分数

function getOptionValue(props) {
//typeof props.value === 'undefined'
return isDefined(props.value)
? props.value
: props.children[0].text
}
function isDefined(a) {
return !(a === null || a === undefined)
}
export function postUpdateSelectedOptions(vnode) {
var props = vnode.props,
multiple = !!props.multiple,
value = isDefined(props.value)
? props.value
: isDefined(props.defaultValue)
? props.defaultValue
: multiple
? []
: '',
options = [];
collectOptions(vnode, props, options)
if (multiple) {
updateOptionsMore(vnode, options, options.length, value)
} else {
updateOptionsOne(vnode, options, options.length, value)
}
}
function collectOptions(vnode, props, ret) {
var arr = props.children
for (var i = 0, n = arr.length; i < n; i++) {
var el = arr[i]
if (el.type === 'option') {
ret.push(el)
} else if (el.type === 'optgroup') {
collectOptions(el, el.props, ret)
}
}
}
function updateOptionsOne(vnode, options, n, propValue) {
// Do not set `select.value` as exact behavior isn't consistent across all
// browsers for all cases.
var selectedValue = '' + propValue;
for (let i = 0; i < n; i++) {
let option = options[i]
let value = getOptionValue(option.props)
if (value === selectedValue) {
setDomSelected(option, true)
return
}
}
if (n) {
setDomSelected(options[0], true)
}
}
function updateOptionsMore(vnode, options, n, propValue) {
var selectedValue = {}
try {
for (let i = 0; i < propValue.length; i++) {
selectedValue['&' + propValue[i]] = true
}
} catch (e) {
/* istanbul ignore next */
console.warn('<select multiple="true"> 的value应该对应一个字符串数组')
}
for (let i = 0; i < n; i++) {
let option = options[i]
let value = getOptionValue(option.props)
let selected = selectedValue.hasOwnProperty('&' + value)
setDomSelected(option, selected)
}
}
function setDomSelected(option, selected) {
if (option._hostNode) {
option._hostNode.selected = selected
}
}
//react的单向流动是由生命周期钩子的setState选择性调用(不是所有钩子都能用setState),受控组件,事务机制
function stopUserInput(e) {
var target = e.target
var name = e.type === 'textarea'
? 'innerHTML'
: 'value'
target[name] = target._lastValue
}
function stopUserClick(e) {
e.preventDefault()
}
export function processFormElement(vnode, dom, props) {
var domType = dom.type
if (/text|password|number|date|time|color|month/.test(domType)) {
if ('value' in props && !hasOtherControllProperty(props, textMap)) {
console.warn(`你为${domType}元素指定了value属性,但是没有提供另外的${Object.keys(textMap)}
等用于控制value变化的属性,那么它是一个非受控组件,用户无法通过输入改变元素的value值`)
dom.oninput = stopUserInput
}
} else if (/checkbox|radio/.test(domType)) {
if ('checked' in props && !hasOtherControllProperty(props, checkedMap)) {
console.warn(`你为${domType}元素指定了value属性,但是没有提供另外的${Object.keys(checkedMap)}
等用于控制value变化的属性,那么它是一个非受控组件,用户无法通过输入改变元素的value值`)
dom.onclick = stopUserClick
}
} else if (/select/.test(domType)) {
if (!('value' in props || 'defaultValue' in props)) {
console.warn(`select元素必须指定value或defaultValue属性`)
}
postUpdateSelectedOptions(vnode)
}
}
var textMap = {
onChange: 1,
onInput: 1,
readOnly: 1,
disabled: 1
}
var checkedMap = {
onChange: 1,
onClick: 1,
readOnly: 1,
disabled: 1
}
function hasOtherControllProperty(props, map) {
for (var i in props) {
if (map[i]) {
return true
}
}
return false
}上面的processFormElement 的复杂度为8。
如果使用映射,可以大大减少复杂度
export function processFormElement(vnode, dom, props) {
var domType = dom.type
var duplexType = duplexMap[domType]
if (duplexType) {
var data = duplexData[duplexType]
var duplexProp = data[0]
var keys = data[1]
var eventName = data[2]
if (duplexProp in props && !hasOtherControllProperty(props, keys)) {
console.warn(`你为${vnode.type}[type=${domType}]元素指定了${duplexProp}属性,但是没有提供另外的${Object.keys(keys)}
等用于控制${duplexProp}变化的属性,那么它是一个非受控组件,用户无法通过输入改变元素的${duplexProp}值`)
dom[eventName] = data[3]
}
if (duplexType === 3) {
postUpdateSelectedOptions(vnode)
}
}
}
function hasOtherControllProperty(props, keys) {
for (var key in props) {
if (keys[key]) {
return true
}
}
}
var duplexMap = {
color: 1,
date: 1,
datetime: 1,
'datetime-local': 1,
email: 1,
month: 1,
number: 1,
password: 1,
range: 1,
search: 1,
tel: 1,
text: 1,
time: 1,
url: 1,
week: 1,
textarea: 1,
checkbox: 2,
radio: 2,
'select-one': 3,
'select-multiple': 3
}
function preventUserInput(e) {
var target = e.target
var name = e.type === 'textarea' ?
'innerHTML' :
'value'
target[name] = target._lastValue
}
function preventUserClick(e) {
e.preventDefault()
}
function preventUserChange(e) {
var target = e.target
var value = target._lastValue
var options = target.options
if (target.multiple) {
updateOptionsMore(options, options.length, value)
} else {
updateOptionsOne(options, options.length, value)
}
}
var duplexData = {
1: ['value', {
onChange: 1,
onInput: 1,
readOnly: 1,
disabled: 1
}, 'oninput', preventUserInput],
2: ['checked', {
onChange: 1,
onClick: 1,
readOnly: 1,
disabled: 1
}, 'onclick', preventUserClick],
3: ['value', {
onChange: 1,
disabled: 1
}, 'onchange', preventUserChange]
}
export function postUpdateSelectedOptions(vnode) {
var props = vnode.props,
multiple = !!props.multiple,
value = isDefined(props.value) ?
props.value :
isDefined(props.defaultValue) ?
props.defaultValue :
multiple ? [] :
'',
options = [];
collectOptions(vnode, props, options)
if (multiple) {
updateOptionsMore(options, options.length, value)
} else {
updateOptionsOne(options, options.length, value)
}
}
function isDefined(a) {
return !(a === null || a === void 666)
}
/**
* 收集虚拟DOM select下面的options元素,如果是真实DOM直接用select.options
*
* @param {VNode} vnode
* @param {any} props
* @param {Array} ret
*/
function collectOptions(vnode, props, ret) {
var arr = props.children
for (var i = 0, n = arr.length; i < n; i++) {
var el = arr[i]
if (el.type === 'option') {
ret.push(el)
} else if (el.type === 'optgroup') {
collectOptions(el, el.props, ret)
}
}
}
function updateOptionsOne(options, n, propValue) {
var selectedValue = '' + propValue;
for (let i = 0; i < n; i++) {
let option = options[i]
let value = getOptionValue(option, option.props)
if (value === selectedValue) {
getOptionSelected(option, true)
return
}
}
if (n) {
getOptionSelected(options[0], true)
}
}
function updateOptionsMore(options, n, propValue) {
var selectedValue = {}
try {
for (let i = 0; i < propValue.length; i++) {
selectedValue['&' + propValue[i]] = true
}
} catch (e) {
/* istanbul ignore next */
console.warn('<select multiple="true"> 的value应该对应一个字符串数组')
}
for (let i = 0; i < n; i++) {
let option = options[i]
let value = getOptionValue(option, option.props)
let selected = selectedValue.hasOwnProperty('&' + value)
getOptionSelected(option, selected)
}
}
function getOptionValue(option, props) {
if (!props) {
return getDOMOptionValue(option)
}
return props.value === undefined ? props.children[0].text : props.value
}
function getDOMOptionValue(node) {
if (node.hasAttribute && node.hasAttribute('value')) {
return node.getAttribute('value')
}
var attr = node.getAttributeNode('value')
if (attr && attr.specified) {
return attr.value
}
return node.innerHTML.trim()
}
function getOptionSelected(option, selected) {
var dom = option._hostNode || option
dom.selected = selected
}Metadata
Metadata
Assignees
Labels
No labels
