前言
今天我和平常一样在更新个人的组件时,封装了 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