2022年05月16-20 开发小计

1,227 阅读4分钟

TypeError: PromiseReject called on non-object at reject

产生这个错误的原因是因为我编写了如下代码

const newTask = oldTask.then(Promise.reject)

导致报错的原因是 Promise.reject内部依赖上下文(context), 只要改成 Promise.reject.bind(Promise)就可以了.

按惯性思维, 大多数原生方法都是不依赖 this的, 例如: Array.isArray, 就可以通过 const isArray = Array.isArray; isArray([]); 的方式调用.

在 React Native 中压缩图片至指定大小之下

tag: [React Native]

首先分析现有函数可完成的功能:

  1. 获取某图片文件大小, 以及其体积(width, height)
  2. 压缩图片至指定比率

在有了上述两个条件后就可以愉快的编写代码了, 压缩流程如下

  • 判断是否小于指定文件大小

  • 小于, 不需要压缩

  • 大于, 尝试压缩为原有质量的 80%, 判断是否小于指定文件大小

  • 小于, 返回压缩后的图片文件

  • 大于, 继续缩小质量

  • 如果压缩次数过多, 这时可能无法压缩了, 则直接缩小原始图片文件的尺寸(width, height), 再次缩小质量, 直到文件体积小于指定大小

    // 是否小于指定的文件大小 (单位为 MB) export const isLessThanTheMB = (fileSize: number, smallerThanSizeMB: number) => { const isOk = fileSize / 1024 / 1024 < smallerThanSizeMB; return isOk; };

    // 获取文件详情 export const inspectFile = (fileURI: string) => { return FileSystem.getInfoAsync(fileURI); };

    /**

    • 压缩图片
    • @param fileURI - 文件路径
    • @param limitMB - 最大文件大小, MB 为单位 */ export const imageCompress = async (fileURI: string, limitMB: number) => { // 先不实现 Web 压缩了 if (!AppEnvs.isNative) { return manipulateAsync( fileURI, [], { compress: 0.5 } ) }
    let fileInfo = await inspectFile(fileURI);
    
    // 先给一个初始值, 避免不需要压缩的情况
    let imageResult: ImageResult = await manipulateAsync(
      fileInfo.uri,
      [],
      { compress: 1 }
    );
    let compress = 0.9
    while (!isLessThanTheMB(fileInfo.size, limitMB)) {
      console.log("当前文件大小", fileInfo.size / 1024 / 1024);
      console.log("压缩比率", compress);
      if (compress < 0.1) {
        console.log("压缩率已至最低, 无法压缩, 尝试缩小图片尺寸");
        compress = 1
        imageResult = await manipulateAsync(
          fileInfo.uri,
          [{ resize: { width: imageResult.width * 0.5, height: imageResult.height * 0.5 } }],
          { compress: compress }
        );
      } else {
        imageResult = await manipulateAsync(
          fileInfo.uri,
          [],
          { compress: compress }
        );
        // 每次减少 20% 的体积
        compress = compress * 0.7;
      }
      fileInfo = await inspectFile(imageResult.uri);
    }
    
    return imageResult
    

    };

textTransform 的用法以及不适用场景

tag: [React Native]

官方文档, 虽然官方文档没有详细写出该属性的用法, 但是凭借名称, 我们可以大概猜到

TYPE

DEFAULT

enum('none', 'uppercase', 'lowercase', 'capitalize')

'none'

  • none

  • 默认值, 不对文字做转换

  • uppercase

  • 将所有字母大写, 如果有多个字母(通过空格分割)

  • Hello word -> HELLO WORLD

  • Helloword -> HELLOWORLD

  • lowercase

  • uppercase行为相反

  • capitalize

  • 将首字母大写

  • Hello word -> Hello World

  • Helloword -> Helloworld

上述的用法已经覆盖了 90% 的使用场景, 但是仍然有种场景无法覆盖, 那就是将 Usa payment转换为 USA Payment即一个非标准单词 + 标准单词转换为 非标转单词全大写, 标准单词首字母大写的情况, 不过这是一个合理的行为, 这里笔者也仅仅是做记录加深印象, 避免踩坑.

最后的最后, 因为 textTransform 没有提供文档描述, 本着求真的精神, 笔者去看了下源码

源码分析

所在位置: github.com/facebook/re…

可以发现, uppercaselowercase都是调用 java 的原生方法, capitalize则是 RN团队自定义的函数: 通过 BreakIterator对文本中的单词进行分割, 然后将首字母大写.

iOS 端的代码就不发了, 有兴趣自行阅读, 源码位置, 值得一提的是, 在 capitalize 模式下, Android 使用的是 BreakIterator来分割文本, 但是 iOS 是直接通过空格切分单词, 不知道会不会有什么坑.

package com.facebook.react.views.text;

import java.text.BreakIterator;

/** Types of text transforms for CustomTextTransformSpan */
public enum TextTransform {
  NONE,
  UPPERCASE,
  LOWERCASE,
  CAPITALIZE,
  UNSET;

  public static String apply(String text, TextTransform textTransform) {
    if (text == null) {
      return null;
    }

    String transformed;
    switch (textTransform) {
      case UPPERCASE:
        transformed = text.toUpperCase();
        break;
      case LOWERCASE:
        transformed = text.toLowerCase();
        break;
      case CAPITALIZE:
        transformed = capitalize(text);
        break;
      default:
        transformed = text;
    }

    return transformed;
  }

  private static String capitalize(String text) {
    BreakIterator wordIterator = BreakIterator.getWordInstance();
    wordIterator.setText(text);

    StringBuilder res = new StringBuilder(text.length());
    int start = wordIterator.first();
    for (int end = wordIterator.next(); end != BreakIterator.DONE; end = wordIterator.next()) {
      String word = text.substring(start, end);
      if (Character.isLetterOrDigit(word.charAt(0))) {
        res.append(Character.toUpperCase(word.charAt(0)));
        res.append(word.substring(1).toLowerCase());
      } else {
        res.append(word);
      }
      start = end;
    }

    return res.toString();
  }
};

Avoid prop drilling 中 drilling 的翻译

tag: [ 英语]

今天查阅英语文章时, 遇到了一个新的单词组 Avoid prop drilling, 要理解这个单词组就要先理解 React(Vue)props传递层级过深所导致的问题, 例如 Vue 文档中所提到的

这里的

组件可能其实根本不关心这些 props,但它仍然需要定义并将它们传递下去使得 能访问到这些 props,如果组件链路非常长,可能会影响到更多这条路上的组件。这一过程被称为“prop drilling”,这似乎不太好解决。

drilling:
v.
钻孔,打眼;操练,训练;(drill的现在分词)

复数: drills
原形: drill
第三人称单数: drills
现在分词: drilling
过去式: drilled
过去分词: drilled

关于为什么使用 drilling单词, 而没有使用 Deep transmission(深层传递) 等更容易理解的原因, 我的理解如下:

假如现在组件树为

  • Root

  • A#1

  • A#1-1

  • A#1-2

  • A#1-2-1

  • B

  • C

这里 A#1-2-1想要从 Root获取某些值就很困难, 那么到底有多难呢? 就像要从 A#1-2-1钻一个洞(drilling) 到 Root一样困难.

而这个单词组的重点也恰恰是突出, props在多级传递时的困难.

LOL 瞎理解的不知道对不对..