乐趣区

关于前端:如何修改-React-Native-的默认字体

引子

工夫过得好快,人不知; 鬼不觉,退出脉脉曾经快一个月了。之前的工作中次要做一些 PC、H5、Node 相干的开发,开发 RN 还是头一次接触,感觉还挺好玩的。????

为什么要设置默认字体?

前两天 Fix 了一个 RN 端的 BUG,共事的小米 10Pro 手机上发现文字被遮挡。如下图所示:

不仅是小米,一些 Android 的其余机型也会遇到相似的问题。因为 Android 手机厂商很多很多,不像 iPhone 只有一家公司,默认字体是不对立的。这时候如果组件没有设置字体,就会应用手机的默认字体。而有些字体,比方“OnePlus Slate”、“小米兰亭 pro”在应用 Text 组件渲染的时候,就会呈现被遮挡的问题。

那么,如何解决这个问题呢?

如何实现全局字体的批改

自定义组件

第一种思路比较简单,能够封装 Text 组件,针对 Android 零碎设置默认字体。

首先,创立一个新文件,命名为:CustomText.js。

// CustomText.js
import React from "react";
import {StyleSheet, Text, Platform} from "react-native";

// Fix Android 机型文字被遮挡的问题
const defaultAndroidStyles = StyleSheet.create({
  text: {fontFamily: ""}
});
// 这里针对 web 加一个简略的款式,不便测试
const defaultWebStyles = StyleSheet.create({
  text: {color: "#165EE9"}
});

const CustomText = (props) => {
  let customProps = {...props};

  if (Platform.OS === "android") {customProps.style = [defaultAndroidStyles.text, props.style];
  }

  if (Platform.OS === "web") {customProps.style = [defaultWebStyles.text, props.style];
  }

  delete customProps.children;

  const kids = props.children || props.children === 0 ? props.children : null;

  return <Text {...customProps}>{kids}</Text>;
};

export default CustomText;
复制代码

接下来,在 App.js 中应用这个组件。

// App.js

import React from 'react';
import {StyleSheet, View, Text} from 'react-native';
import {CustomText} from './CustomText';

const App = () => {
  return (<View style={styles.container}>
      <Text style={styles.text}> 应用 Props 传递 style 款式 </Text>
      <CustomText> 应用 CustomText 自带 style 款式 </CustomText>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#070825'
  },
  text: {color: '#ff0000'}
});
复制代码

如下图所示,会自带默认的蓝色,而 则须要手动传递色彩等款式。对于 如果咱们须要批改色彩,失常传递 style 属性即可。

然而这种解决形式须要将所有引入、应用组件的都改为,还是比拟麻烦的,有没有什么更加不便的办法呢?

笼罩 Text 组件的 render 办法

首先,减少一个函数,来笼罩 React Native 的 render 办法:

// util.js
import {Text, Platform} from "react-native";

export const setCustomText = () => {
  const TextRender = Text.render;

  let customStyle = {};
  // 重点,Fix Android 款式问题
  if (Platform.OS === "android") {
    customStyle = {fontFamily: ""};
  }
  // 为了不便演示,减少绿色字体
  if (Platform.OS === "web") {
    customStyle = {
      lineHeight: "1.5em",
      fontSize: "1.125rem",
      marginVertical: "1em",
      textAlign: "center",
      color: "#00ca20"
    };
  }

  Text.render = function render(props) {
    let oldProps = props;
    props = {...props, style: [customStyle, props.style] };
    try {return TextRender.apply(this, arguments);
    } finally {props = oldProps;}
  };
};
复制代码

这里参考了 Ajackster/react-native-global-props 中 setCustomText 的实现。

而后在 App.js 中,调用 setCustomText 函数即可。

// App.js

import React from 'react';
import {StyleSheet, View, Text} from 'react-native';
import {CustomText} from './CustomText';
import {setCustomText} from "./util";

setCustomText();

const App = () => {
  return (<View style={styles.container}>
            <Text> 通过调用 utils.setCustomText() 批改 </Text>
      <Text style={styles.text}> 应用 Props 传递 style 款式 </Text>
      <CustomText> 应用 CustomText 自带 style 款式 </CustomText>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#070825'
  },
  text: {color: '#ff0000'}
});
复制代码

如下图所示,咱们新增了一个 组件,没有传递任何属性,然而能够看到,它是绿色的~

仅仅须要执行一遍这个函数,就能够影响到所有组件的 render 办法,咱们不须要再导入 组件了。这种形式真的能够帮忙咱们一劳永逸的解决这个问题!

demo 地址:codesandbox.io/s/wizardly-…

原理浅析

React Native Text 组件

Text 组件是一个类组件,在它的 render 办法中,首先解构了 props 属性,而后依据是否存在先人节点,以及外部的状态合并 props 属性。

render 办法通过 babel 的编译之后,会转换成 React.createElement 所包裹的语法树,从而转换成虚构的 Dom 树。这就能够联想到另一个 API:

React.cloneElement(
  element,
  [props],
  [...children]
)
复制代码

咱们在笼罩 Text.render 办法时,只须要应用 Text.prototype.render.call 失去之前的节点,更新 props,并调用

React.cloneElement 失去一个新的节点返回即可。

 import React from 'react';
 
 import {StyleSheet, Text} from 'react-native';
 
 const styles = StyleSheet.create({
   defaultFontFamily: {fontFamily: 'lucida grande',},
 });
 
 export default function fixOppoTextCutOff() {
   const oldRender = Text.prototype.render;
   Text.prototype.render = function render(...args) {const origin = oldRender.call(this, ...args);
     return React.cloneElement(origin, {style: [styles.defaultFontFamily, origin.props.style],
     });
   };
 }
复制代码

搜寻官网 issue,会找到相似的问题:github.com/facebook/re…,就是用的这种解决思路。

react-native-global-props 的实现

Ajackster/react-native-global-props 是一个能够增加默认组件属性的库。

上面摘自 setCustomText.js

import {Text} from 'react-native'

export const setCustomText = customProps => {
  const TextRender = Text.render
  const initialDefaultProps = Text.defaultProps
  Text.defaultProps = {
    ...initialDefaultProps,
    ...customProps
  }
  Text.render = function render(props) {
    let oldProps = props
    props = {...props, style: [customProps.style, props.style] }
    try {return TextRender.apply(this, arguments)
    } finally {props = oldProps}
  }
}
复制代码

它笼罩了 Text 组件的动态属性:defaultProps 和 render 办法。这里不一样的是,它没有借助 React.cloneElement 返回一个新的节点,而是在返回后果的前后,批改 props 中的 style 属性,这里等同于批改

arguments[0] 的值,因为他们的援用雷同。并在最初重置 props,防止 props 被净化。能够看出,这种形式实现的更加奇妙。

styled-components css.Text 为什么会受影响

styled-components是一个 React 的第三方库,是 CSS in JS 的优良实际。它对于 React Native 也有着不错的反对。因为 React Native 批改款式只能通过批改 style 属性来实现,所以 CSS in JS的计划对于 React Native 我的项目来说有着人造的劣势。

对于 React 我的项目,styled-components会批改 className 属性来达到批改款式的目标;而对于 React Native,则是应用上面的办法,批改组件 props 中的 style 属性来达到目标。

 propsForElement.style = [generatedStyles].concat(props.style || []);

 propsForElement.ref = refToForward;

 return createElement(elementToBeCreated, propsForElement);
复制代码

然而,这个批改的过程肯定是在咱们重写 render 函数之前实现的。所以,下面那个办法批改 style 对于 styled-components 创立的 React Native 组件同样实用。

结语

对于如何批改 React Native 的全局款式的探讨临时告一段落了。第一次发现还能够以这样的形式批改 React 的 render 函数的属性,感觉还是比拟神奇的。也是第一次尝试写这种偏原理探索的文章,如果有写的不对的中央,或者想和我交换的话,欢送评论留言哈~

PS:对脉脉感兴趣的小伙伴,欢送发送简历到 496691544@qq.com,我能够帮忙内推~

参考

  • React-Native Text 文档 reactnative.cn/docs/text
  • Two-way to change default font family in React Native ospfolio.com/two-way-to-…
  • 一文解决 RN0.58 局部安卓手机 text 显示不全问题 segmentfault.com/a/119000002…
  • react-native-global-props github.com/Ajackster/r…
  • styled-components 源码浏览:github.com/wangpin34/b…
  • ReactNative 源码篇:源码初识:github.com/sucese/reac…
退出移动版