前言
今天我和平常一样在更新个人的组件时,封装了 Flex
组件。
在封装的过程中,涉及到了 flex
值的问题:
jsx
export default () => {
const flexStyle = useMemo(() => {
return {
flex: value
}
}, [flex])
return (
<div style={{...flexStyle}}></div>
)
}
然后这一过程里面发现自己对 flex 布局也就只会 justify-content、align-items
,用上面那种 flex: 值
的形式都少之又少。
封装完之后跟着MDN文档过了一遍校验后,对 flex 的认识也更深了。所以记录下来
style.flex 值的组成
根据MDN文档中我们知道,flex 由三个属性组成
然后对于每一个属性,我们先简单走一遍:
flex-grow
它的值 只能是 number
,取值范围是: <number [0,∞]>
flex-shrink
它和 flex-grow
一样,只能是 number
, 取值范围是 <number [0,∞]>
flex-basis
它的取值可以和 style.width
一样,即:2px、3em
,也可以是尺寸关键字
,比如:fill、max-content、content
等,具体可以看flex-basis
简单走了一遍 属性之后,我们来看 flex 使用的方法
style.flex 使用的三种方法
style.flex
使用的时候,可以以一个值
、两个值
、三个值
的方式来指定 style.flex
属性
-
单值语法:值必须是以下之一:
- 一个
<flex-grow>
的有效值:此时简写会扩展为flex: <flex-grow> 1 0
。 - 一个
<flex-basis>
的有效值:此时简写会扩展为flex: 1 1 <flex-basis>
。 - 关键字
none
或者全局关键字之一。
- 一个
-
双值语法:
-
第一个值必须是一个
flex-grow
的有效值。 -
第二个值必须是以下之一:
- 一个
flex-shrink
的有效值:此时简写会扩展为flex: <flex-grow> <flex-shrink> 0
。 - 一个
flex-basis
的有效值:此时简写会扩展为flex: <flex-grow> 1 <flex-basis>
。
- 一个
-
-
三值语法:值必须按照以下顺序指定:
- 一个
flex-grow
的有效值。 - 一个
flex-shrink
的有效值。 - 一个
flex-basis
的有效值。
- 一个
好了,清楚了上面的语法之后,我们来写一下他们的判断函数
flex-grow、flex-shrink 的判断
前面说了,flex-grow 和 flex-shrink 的类型只能是 number
,且范围是 [0,∞]
tsx
/**
* 校验是否是 flex-grow 的有效值
* @param str
* @returns
*/
const isFlexGrow = (str: string): boolean => {
return /^([1-9]+(\.\d+)?)|([0](\.\d+)?)$/.test(str);
};
/**
* 校验是否是 flex-shrink 的有效值
* @param str
* @returns
*/
const isFlexShrink = (str: string): boolean => {
return /^([1-9]+(\.\d+)?)|([0](\.\d+)?)$/.test(str);
};
flex-basis 的判断
flex-basis 的判断稍微复杂点。因为它可以是 style.width
的取值,也可以是 尺寸关键字
在这里,因为我封装 Flex 组件的时候,flex 是以 字符串
的形式传进来的
tsx
/**
* 校验是否是 flex-basis 的有效值
* @param str
* @returns
*/
const isFlexBasis = (str: string): boolean => {
// 如果传入的 flex-basis 是 尺寸关键词
if (flexBasisKeyWords.includes(str)) {
return true;
}
// 如果传入的是 20px、12em 这样的带单位的值
const regs = [
'px',
'in',
'cm',
'mm',
'pt',
'pc',
'em',
'rem',
'ex',
'vh',
'vw',
'vmin',
'vmax',
'%',
'fr',
];
const splitArr = str.split(/[0-9]/);
const currentReg = splitArr[splitArr.length - 1];
if (currentReg) {
return regs.includes(currentReg);
} else {
return false;
}
};
flex 值的合法校验(重点)
接下来,就是最重要的 flex
在 单值
、双值
、三值
的情况下使用时的检验方法
单值检验
前面说了,单值语法传入的值必须是 flex-grow
、flex-basis
其中的一个有效值。
- 如果传入的是
flex-grow
的有效值,则 flex 的样式规则为:flex: <flex-grow> 1 0
- 如果传入的是
flex-basis
的有效值,则 flex 的样式规则为:flex: 1 1 <flex-basis>
然后,还需要注意一下部分关键字:none
、content
、fill
、auto
时的 flex 值(这部分是我参照 antd 的样式来处理的)
tsx
/**
* 单值语法检验
* @param single
* @returns
*/
const validateSingleValued = (single: string): CSSProperties => {
// 如果只包含数值,说明此时传入的是 flex-grow 属性的有效值,此时简写会扩展为 flex: <flex-grow> 1 0。
// 比如: flex: 1
if (isFlexGrow(single)) {
return {
flex: `${single} 1 0%`,
};
} else if (isFlexBasis(single)) {
// 如果传入的是带单位的数字,或者是 flex-basis 的尺寸关键词时,此时是 flex-basis 的有效值,此时拓展为: flex: 1 1 <flex-basis>
// 比如:flex: 3em 、flex: max-content
return single !== 'fill' &&
single !== 'content' &&
single !== 'none' &&
single !== 'auto'
? {
flex: `1 1 ${single}`,
}
: single === 'none'
? {
flex: '0 0 auto',
}
: single === 'auto'
? {
flex: '1 1 auto',
}
: {};
} else if (flexSigleGlobals.includes(single)) {
// 如果传入的是 单值时的 全局值,如:flex: 'inherit'
return {
flex: single !== 'none' ? `${single}` : '0 0 auto',
};
} else {
return {};
}
};
双值校验
根据规则:
- 第一个值必须是
flex-grow
的有效值 - 第二个值必须是
flex-basis
的有效值或者flex-shrink
的有效值- 如果是
flex-basis
的有效值,则 flex 的样式规则为flex: <flex-grow> 1 <flex-basis>
- 如果是
flex-shrink
的有效值,则 flex 的样式规则为flex: <flex-grow> <flex-shrink> 0
- 如果是
tsx
/**
* 双值语法检验
* @param grow 传入的 flex-grow 值
* @param second 传入的 flex-basis 或者 flex-shrink 的有效值
* @returns
*/
const validateDoubleValued = (grow: string, second: string): CSSProperties => {
// 首先,第一个值必须是 flex-grow 的有效值,此时必须是 number,值是 [0, 正无穷],负值无效,默认为 0
// https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex-grow
if (!isFlexGrow(grow)) {
return {};
} else if (isFlexBasis(second)) {
// 第二个值必须是 flex-basis 的有效值 或者是 flex-shrink 的有效值
// 此时拓展为: flex: <flex-grow> 1 <flex-basis> 或者 flex: <flex-grow> <flex-shrink> 0
return {
flex: `${grow} 1 ${second}`,
};
} else if (isFlexShrink(second)) {
return {
flex: `${grow} ${second} 0%`,
};
} else {
return {};
}
};
三值校验
三值校验时,要按照 flex-grow
、flex-shrink
、flex-basis
的顺序依次校验
tsx
/**
* 三值语法检验
* @param grow 传入的 flex-grow 值
* @param basis 传入的 flex-basis 值
* @param shrink 传入的 flex-shrink 值
* @returns
*/
const validateTernaryValued = (
grow: string,
basis: string,
shrink: string,
): CSSProperties => {
// 按照 flex-grow flex-shrink flex-basis 的顺序校验是否合法
return isFlexGrow(grow) && isFlexShrink(shrink) && isFlexBasis(basis)
? {
flex: `${grow} ${shrink} ${basis}`,
}
: {};
};
计算 flex 属性
校验规则搞完后,最后来计算 flex
属性值
tsx
// 语法校验:https://developer.mozilla.org/zh-CN/docs/Web/CSS/flex#%E8%AF%AD%E6%B3%95
const flexStyle = useMemo(() => {
// 根据 flexLength,去做单值语法、双值语法、三值语法的校验
const flexLength = flex && flex !== '' ? flex.split(' ').length : 0;
// 如果 flex 无效
if (!flexLength) return {};
// 校验 flex
if (flexLength === 1) {
return validateSingleValued(flex.split(' ')[0]);
} else if (flexLength === 2) {
return validateDoubleValued(flex.split(' ')[0], flex.split(' ')[1]);
} else {
return validateTernaryValued(
flex.split(' ')[0],
flex.split(' ')[2],
flex.split(' ')[1],
);
}
}, [flex]);
页面效果
flex="1"
flex="auto"
flex="2 2px"
flex="2 max-content"
flex="1 1 2px"
结尾
源码的话我现在还没上传到github,但是你可以过几天再来看看,说不定到时候就传上去了
地址是:happy-ui