React Native 源碼閱讀筆記 - StyleSheet

  • [時間]: 2020/3/27
  • [keyword]: React Native,Typescript,源碼閱讀

StyleSheet 的源碼位置在: LibrariesStyleSheetStyleSheet.jsjavascript

https://github.com/facebook/r...java

官方文檔react

功能介紹

StyleSheet 是用 RN 開發中提供樣式相關功能的工具集。android

它提供了下面幾個靜態方法和靜態屬性git

靜態方法: compose() create() flatten() setStyleAttributePreprocessor() github

靜態屬性: absoluteFill absoluteFillObject hairlineWidthreact-native

compose()

compose<T: DangerouslyImpreciseStyleProp>(
    style1: ?T,
    style2: ?T,
  ): ?T | $ReadOnlyArray<T> {
    // 判斷 style1 和 style2 是否都不是 null,若是都不是返回 [style1, style2] 這樣的數組
    if (style1 != null && style2 != null) {
      return ([style1, style2]: $ReadOnlyArray<T>);
    } else {
      // 若是有一個是 null 則判斷下 style1 是否是 null,若是不是則返回 style1, 不然返回 style2, 因此實際這裏若是 style2 是 null 這個函數仍是有可能返回 null 的
      return style1 != null ? style1 : style2;
    }
  },

create()

用於建立 react native jsx 對象的 style 屬性數組

StyleSheetValidation 源碼位置: https://github.com/facebook/r...函數

// 假設咱們傳入 obj = { test: { color: "white" } }

create<+S: ____Styles_Internal>(obj: S): $ObjMap<S, (Object) => any> {  
    
        // 若是在開發環境下則驗證是否傳入了非法的屬性
    if (__DEV__) {
      for (const key in obj) {
        // StyleSheetValidation.validateStyle 見下  
        // 這裏 key = "test", obj = { test: { color: "white" } }
        StyleSheetValidation.validateStyle(key, obj);
        if (obj[key]) {
          // Object.freeze 文檔: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
          // 簡單來講就是不容許傳入的對象發生改變, 例如 a.a = 1 不會改變 a.a 的值  
          Object.freeze(obj[key]);
        }
      }
    }
    return obj;
    
    
static validateStyle(name: string, styles: Object) {
    // 若是不是開發環境則不驗證
    if (!__DEV__ || global.__RCTProfileIsProfiling) {
      return;
    }
    
    for (const prop in styles[name]) {
      // prop = "color" 
      // styles = { test: { color: "white" } }
      // name = "test"  
      // styles[name] = { color: "white" }
      
      // StyleSheetValidation.validateStyleProp 源碼見下
      StyleSheetValidation.validateStyleProp(
        prop,
        styles[name],
        'StyleSheet ' + name,
      );
    }
  }

  static validateStyleProp(prop: string, style: Object, caller: string) {
    // 簡單來講就是 RN 內部維護一個叫作 allStylePropTypes 的 Map 對象,map 的 key 爲全部被容許的 style 名稱,例如 「color」 "fontSize",而 validateStyleProp 方法會去 allStylePropTypes 對象裏面查找傳入的 prop 是否存在,若是不存在則會報錯
      
    if (!__DEV__ || global.__RCTProfileIsProfiling) {
      return;
    }
    // 若是 prop 不存在 allStylePropTypes 中,也就是傳入了錯誤的 prop, 例如傳入 prop = "test"
    if (allStylePropTypes[prop] === undefined) {
      const message1 = '"' + prop + '" is not a valid style property.';
      const message2 =
        '\nValid style props: ' +
        JSON.stringify(Object.keys(allStylePropTypes).sort(), null, '  ');
      // 上面構建報錯信息,這裏拋出 error
      styleError(message1, style, caller, message2);
    }
      
    // allStylePropTypes[prop] 有多是一個函數,參考 StyleSheetValidation.js addValidStylePropTypes() 方法
    // 這裏嘗試調用這個函數,若是這個函數返回了 error 則會將 error 拋出
    const error = allStylePropTypes[prop](
      style,
      prop,
      caller,
      'prop',
      null,
      ReactPropTypesSecret,
    );
    if (error) {
      styleError(error.message, style, caller);
    }
  }

flatten()

flatten(打平) 的源碼在這個文件 https://github.com/facebook/r...工具

他的效果爲將 [{color:"white"}, {fontSize: 11}] 這種結構的對象變成深遞歸轉換成 {color: "white", fontSize: 11}, 注意這裏是深遞歸,也就是說 [ {corlor: "white", test: { fontSize: 10 }} ] 這種結構也能夠處理,不過 jsx 在 transform 的過程當中會驗證 style ,因此 test 會被報錯

function flattenStyle(
  style: ?DangerouslyImpreciseStyleProp,
): ?DangerouslyImpreciseStyle {
  // 處理邊界狀況    
  if (style === null || typeof style !== 'object') {
    return undefined;
  }

  // 若是不是數組天然沒法 flatten ,因此直接返回    
  if (!Array.isArray(style)) {
    return style;
  }

  const result = {};
  for (let i = 0, styleLength = style.length; i < styleLength; ++i) {
    // 打平傳入的 style 對象,按照第一個例子這裏就可能爲 flattenStyle({color:"white"}), 最後獲得 {color:"white"} 這種結構
    const computedStyle = flattenStyle(style[i]);
    if (computedStyle) {
      for (const key in computedStyle) {
        // 將值存在 result 中  
        result[key] = computedStyle[key];
      }
    }
  }
  return result;
}

setStyleAttributePreprocessor()

setStyleAttributePreprocessor() 會改變 RN 內部用於處理 color(顏色)transformshadowOffset的預處理器( preprocessor )

上面提到的幾個東西怎麼理解呢?

首先要明確一件事情,那就是開發者傳入的 style 的 color transform shadowOffset 屬性 RN 是不會直接使用的,而是先作處理,用於處理的函數就是預處理器

咱們來看看 color 的默認預處理器,這部分代碼過於底層,筆者的水平還不足以講解具體意思,大概意思是會將你傳入的 white, #FFFFFF hsla(0, 0%, 100%, 1) 等顏色格式轉換爲 32 位的整數。

function processColor(color?: ?(string | number)): ?number {
  if (color === undefined || color === null) {
    return color;
  }

  let int32Color = normalizeColor(color);
  if (int32Color === null || int32Color === undefined) {
    return undefined;
  }

  // Converts 0xrrggbbaa into 0xaarrggbb
  int32Color = ((int32Color << 24) | (int32Color >>> 8)) >>> 0;

  if (Platform.OS === 'android') {
    // Android use 32 bit *signed* integer to represent the color
    // We utilize the fact that bitwise operations in JS also operates on
    // signed 32 bit integers, so that we can use those to convert from
    // *unsigned* to *signed* 32bit int that way.
    int32Color = int32Color | 0x0;
  }
  return int32Color;
}

這個就是預處理器的意思,transfromshadowOffset 也是同理。

absoluteFill

等價於下方代碼

const absoluteFill = {
  position: 'absolute',
  left: 0,
  right: 0,
  top: 0,
  bottom: 0,
}

hairlineWidth

一個當前設備上能顯示的最細數值的常量。TODO PixelRatio 部分會在接下來的章節講解,以後會在連接放講解的超連接

源碼以下:

let hairlineWidth: number = PixelRatio.roundToNearestPixel(0.4);
if (hairlineWidth === 0) {
  hairlineWidth = 1 / PixelRatio.get();
}
相關文章
相關標籤/搜索