Taro react native 开发问题总结

307 阅读5分钟

Taro 集成 小程序、Android、iOS 项目

Text 组件

  1. Text 组件,在 react native 中,Text 组件布局不是 inline 的,而是 block 的。而 react native 中无法使用 inline、inline-block 等布局,只能通过 flex 布局进行实现。

    Q&A

    • 当一段文本中文字需要样式,如果只是简单的字体样式不同

      可以使用 Text 组件包裹 Text 组件进行设置,这样设置 Text 组件不会保持一行宽度,以类似 span 标签的形式进行展示,但是 Text 组件同样不能设置宽高,padding,margin 等属性,也不生效。

    • 当这段文字中有块级元素(如 Button 等),则需要通过 flex 布局来实现。

      如下:

      <View className="flexRow">
        <View className="btn">Button</View>
        <Text>Text</Text>
      </View>
      

      但当文本过长需要换行时,就会遇到宽度不够的问题,导致换行无效。flex 布局将文字按钮被分为两块区域,因此就需要进行一些特殊处理。

      <View className="flexRow flexWrap">
        <View className="btn">Button</View>
        {"假设这是一段很长的文字".split("").map((item, index) => {
          return <Text key={index}>{item}</Text>;
        })}
      </View>
      
    • Text 保持一行,在微信、支付宝小程序中有较大差异,设置 one-line 在支付宝中不生效,需要设置 numberOfLines={1},但还不能设置 one-line className 样式,否知不显示省略号

      <Text className="one-line-wx" numberOfLines={1}>
        假设这是一段很长很长的文字
      </Text>
      
      // 处理微信小程序的省略号
      // 因为支付宝小程序 text 组件设置 number-of-lines 后
      // 同时设置 one-line 导致省略号不显示
      .one-line-wx {
        /*  #ifdef weapp  */
        white-space: nowrap;
        text-overflow: ellipsis;
        overflow: hidden;
        /*  #endif  */
      }
      

弹窗问题

  1. react native 中 position 只能设置 absolute、relative。react native 默认为 relative 定位,而且若设置 absoulte,想定位到根节点顶部、底部也十分困难。

    因此,大多数弹窗都直接设置在页面层,当在子组件中设置弹窗时,就会非常麻烦,代码逻辑也十分混乱。因此出现了一个库 react-native-root-siblings,用于解决这个问题。

    但是我当前开发需求还需要再小程序中运行,所以没能直接使用该库。仔细看了下这个库的代码,了解其实现思路,自己写一个弹窗控制器,实现功能。

    react-native-root-siblings 实现思路:依旧是将组件渲染到最外层,通过一个数组来管理组件的显示与隐藏,在根组件循环遍历渲染。

    因此,想着通过 createContext + reducer 来控制数组,这样在不同组件中也能够使用管理弹窗的方法。

    const initialState = { siblings: [] };
    
    const RootSiblingsContext = React.createContext(initialState);
    
    const reducer = (state, action) => {
      switch (action.type) {
        case "add":
          return [...state, action.payload];
        case "remove":
          return state.filter((item) => item.key !== action.payload);
        default:
          return state;
      }
    };
    
    export { RootSiblingsContext, reducer };
    

    RootSiblingsManager.jsx:

    const { siblings, dispatch } = useReducer(reducer, initialState);
    export default const RootSiblingsManager = (props) => {
      return (
        <RootSiblingsContext.Provider value={{ siblings, dispatch }}>
          {props.children}
          {state.siblings.map((item, index) => (
            <Fragment key={index}>{item.element}</Fragment>
          ))}
        </RootSiblingsContext.Provider>
      );
    };
    
    export const withRootSiblingsManagerWrapper = (Component) => {
      return (props) => (
        <RootSiblingsManager>
          <Component {...props} />
        </RootSiblingsManager>
      );
    };
    

    补充问题

    当上面的在不同页面使用时,最上面的导航栏是不会被弹窗遮住的,是因为 app.rn.jsx 设置的问题。

    <>
      <StatusBar {...props} />
      {props.children}
    </>
    

    改为

    <RootSiblingsManager>
      <StatusBar {...props} />
      {props.children}
    </RootSiblingsManager>
    

    这样设置的组件就会直接显示在最顶层,和页面是同级的。 因此 app.rn.jsx 中的 <StatusBar /> 就可以正常被遮住,在 IOS 中,滚动也不会出现穿透的情况。

    但这样写在 Android 中,却会遇到另外一个问题,弹窗打开时,通过手势及按键返回时,弹窗不会消失。

    在通过查看不同的 App 后,发现在 Android 中,弹窗打开时,使用手势及按键返回,会执行关闭弹窗,没有返回到下一页的逻辑。可能是劫持了返回按键的逻辑,只有在满足一定条件时,才会执行返回逻辑。

    接下来就是尝试如何实现弹窗的返回逻辑。

    还记得上面说过的 弹窗组件和页面组件同层吗?在这就隐藏着具体实现逻辑。 只需将弹窗组件最外层包裹一层组件,并实现返回逻辑。

     import {BackHandler} from "react-native";
    
     const { state, dispatch } = useContext(RootSiblingsManagerContext)
    
     useEffect(() => {
       // Android 全局弹窗拦截返回键
       const onBackPress = () => {
         if(state.siblings.length > 0) {
           dispatch({
             type: ACTION_TYPE.POP,
             payload: null
           })
           return true
         }
         return false
       }
       const subscription = BackHandler.addEventListener('hardwareBackPress', onBackPress)
       return () => {
         subscription.remove()
       }
     }, [state.siblings])
    

布局问题 Flexbox

占据剩余空间 flex: 1,在小程序中 flex: 1,当高度超出剩余高度时,高度会继续增加,为解决该问题还需给该元素设置 overflow: auto;

<View className="flexColumn">
  <View className="xxx">NavBar</View>
  <View className="flex1 overflowAuto">
    <View>Content</View>
  </View>
</View>

flex 布局内设置 Text 组件,会影响 Text 组件默认功能,可以给 Text 组件设置宽高及 borderRadius 等,且具体高度会被其他子元素影响。

获取真实宽高

因该项目中使用的是标准 750px 布局,因此获取真实 1px 高度只需通过将 真实屏幕宽度 / 750 就能获取真实 1px 像素值

const realPx = function () {
  return Taro.getSystemInfoSync().windowWidth / 750;
};

弹窗中键盘弹起导致弹窗顶部超出

问题是因为 CoverPopWindow 组件的设置是通过 position: absoulte 实现,当键盘弹起时,窗口大小发生变化,因此 CoverPopWindow 高度缩小,内部的弹窗内容也因此发生位置变化。 之后考虑到 CoverPopWindow 高度发生变化,导致该问题,因此采用 ScrollView 组件重新实现 CoverPopWindow 组件。当键盘弹起时,CoverPopWindow 依旧能够包裹住弹窗内容,这样就可以滚动查看剩余内容。然后发现当这样实现时,弹窗内容不会在键盘弹起时超出屏幕,但是此时还可以滚动弹窗查看内容,因此再次禁用 ScrollView 的滚动,就可以完美解决弹窗中唤起键盘问题。