最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 如何修改 React Native 的默认字体

    正文概述 掘金(joking_zhang)   2021-01-17   590

    时间过得好快,不知不觉,加入脉脉已经快一个月了。之前的工作中主要做一些 PC、H5、Node 相关的开发,开发RN还是头一次接触,感觉还挺好玩的。?

    为什么要设置默认字体?

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

    如何修改 React Native 的默认字体

    不仅是小米,一些 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 属性即可。

    如何修改 React Native 的默认字体
    但是这种处理方式需要将所有引入、使用组件的都改为 ,还是比较麻烦的,有没有什么更加方便的方法呢?

    覆盖Text组件的render方法

    首先,增加一个函数,来覆盖 Text 组件的 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'
      }
    });
    

    如下图所示,我们新增了一个 组件,没有传递任何属性,但是可以看到,它是绿色的~

    如何修改 React Native 的默认字体

    仅仅需要执行一遍这个函数,就可以影响到所有组件的 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…

    起源地下载网 » 如何修改 React Native 的默认字体

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元