关于react-native:React-Native开发初体验

环境装置

参见官网:

https://reactnative.dev/docs/environment-setup

https://reactnative.cn/docs/environment-setup

Notes

  • 针对依赖Node外围的包,RN没有进行解决,须要借助rn-nodeify解决,e.g. https://github.com/mvayngrib/react-native-crypto
  • 针对Java依赖的版本问题,能够借助jetifier主动解决大部分的版本差别
  • 针对Java依赖版本问题,能够通过patch-package打补丁

    • 打出的补丁会蕴含很多无用的局部,能够选择性删除
    • 打出的补丁可能二次批改,e.g.https://github.com/browserify/pbkdf2/blob/master/lib/default-encoding.js

      • 源码中global.process && global.process.version,装置后global.process && global."v16.13.0"patches/pbkdf2+3.1.2.patch须要二次批改

我的项目创立

npx react-native@latest init rnProject

Notes:项目名称只反对驼峰,不反对连字符。

我的项目运行

npm run android

  • 往模拟器或真机装置APK:包运行所须要的资源。
  • 援用原生依赖,须要从新运行该命令。
  • --mode=release长期打包,输入门路android\app\build\outputs\apk\release

npm run start

  • 运行Metro打包JS代码,启动热更新,在模拟器或真机实时查看改变。
  • --reset-cache革除Metro缓存,从新编译JS代码。

革除运行缓存

  • "./gradlew" clean

    • 切换到android目录下,在命令行执行该命令,可革除gradle缓存。
  • npx react-native start --reset-cache

    • 革除Metro缓存,从新编译JS代码
    • 利用场景:环境报错、开发者工具呈现问题

页面适配

计划一:

// src/utils/px2dp.js
import { Dimensions, PixelRatio, StyleSheet } from 'react-native';
const windowWidth = Dimensions.get('window').width;

const px2dp = function (px) {
  if (!isNaN(px)) {
    return (windowWidth / 1080) * px / PixelRatio.get()
  } else {
    return 0
  }
}
export default px2dp
// entry.js
import px2dp from '../utils/px2dp';
StyleSheet.create({
  pageTitle: {
    fontSize: px2dp(64),
    lineHeight: px2dp(85),
    paddingLeft: px2dp(100),
    paddingRight: px2dp(100),
    marginTop: px2dp(80),
  },
})

计划二:

import React from 'react';
import {
  SafeAreaView,
  StatusBar,
  View,
  Text,
  StyleSheet,
  Dimensions,
  PixelRatio,
} from 'react-native';
const {width: layoutWidth, height: layoutHeight} = Dimensions.get('window');
const pixelWidth = PixelRatio.getPixelSizeForLayoutSize(layoutWidth);
const pixelHeight = PixelRatio.getPixelSizeForLayoutSize(layoutHeight);
const ratio = PixelRatio.get();
const styles = StyleSheet.create({
  page: {
    width: pixelWidth,
    height: pixelHeight,
    transform: [
      {
        translateX: -0.5 * pixelWidth,
      },
      {
        translateY: -0.5 * pixelHeight,
      },
      {
        scale: 1080 / (ratio * pixelWidth),
      },
      {
        translateX: 0.5 * pixelWidth,
      },
      {
        translateY: 0.5 * pixelHeight,
      },
    ],
  },
  container: {
    width: 1080,
    backgroundColor: 'red',
  },
  half: {
    width: 540,
    backgroundColor: 'green',
  },
  quartar: {
    width: 270,
    backgroundColor: 'gray',
  },
});
function App(): JSX.Element {
  return (
    <SafeAreaView style={styles.page}>
      <StatusBar barStyle="light-content" />
      <View style={styles.container}>
        <Text>1</Text>
      </View>
      <View style={styles.half}>
        <Text>2</Text>
      </View>
      <View style={styles.quartar}>
        <Text>3</Text>
      </View>
    </SafeAreaView>
  );
}

export default App;

构造&款式

原生组件:

https://reactnative.dev/docs/components-and-apis

  • 只有特定的组件才有交互款式与事件,如ButtonTouchableHighlightTouchableOpacity,其余组件无奈绑定onPress事件

    • 个别不选用Button,而应用自定义Button组件,因为原生Button款式不好调节
  • 不反对svg,须要借助第三方库,e.g.react-native-svgreact-native-svg-transformer
  • 构造搭建:将HTML的标签用法齐全遗记,从新依据文档学习应用办法。

款式:

https://reactnative.dev/docs/image-style-props

  • 只能应用组件标准的款式,否则不起作用
  • lineHeight不能使文字居中,请应用justifyContent
  • 针对Text组件,须要独自定义相干款式,不会继承父级非Text组件Text款式,e.g.不会从父级继承color
  • 不反对渐变色、投影等成果,须要借助第三方库,e.g.react-native-linear-gradientreact-native-shadow-2

Notes:第三方库的装置,须要重新启动我的项目,否则,会报模块找不到。

嵌套ScrollView

  • flatlist嵌套在scrollView无奈滚动,父子级都须要设置nestedScrollEnabled属性
  • scrollView必须设定一个高度,否则会应用默认高度,并非由内容撑开。

    • 留神stylecontentContainerStyle的区别
    • scrollView高度设定:专设View组件包裹,scrollView高度设置为flex:1

代码调试

react-native-debugger的应用

  • 连贯react-native-debugger,须要应用程序开启Debug模式

    • 真机:摇一摇手机,呈现操作面板,抉择Debug
  • 模拟器:模拟器聚焦后,应用Ctrl + m关上操作面板

    • 双击rreload
  • 应用react-native-debugger时,如果收回网络申请,可能会在Network面板发现没有request发动,申请(胜利/失败)回调没有执行。

    • react-native-debugger非Chrome控制台面板中右键,开启Enable Network Inspect

  • 应用react-native-debugger时,如果发现Components面板始终空白,应用npx react-native start --reset-cache革除缓存启动我的项目
  • react-native-debugger的应用,须要同版本react-devtoolsreact-devtools-core作为开发依赖。

以后实例获取

类同Chrome开发者工具,在RN任意调试工具的Console面板:

  • 通过$reactNative等同于import $reactNative from "react-native",能够应用react-native库中的办法。
  • 通过$r能够获取以后选中节点实例

多种调试工具切换

在模拟器中开启debug后,调试每次都会主动默认关上debugger-ui页面,如何敞开:

  1. 在启动我的项目npm run start的命令行中应用shift + d切换到模拟器并关上调试面板,此时点击debug面板选项不再主动调起debugger-ui页面。
  2. 通过勾销选中小勾号来解决它Maintain Priority
  3. 防止间接在模拟器中通过Ctrl + M唤起面板,抉择debug,这样会默认调起debugger-ui

UI审查

开启Android Studio:

  • 通过右下角的Layout Inspector,抉择模拟器或真机。
  • 点击构造,能够查看组件属性
  • 能够通过该面板的右上角设置,切换单位。
  • 能够通过给组件赋值testIDaria-label,进行组件辨认。

日志查看

开启Android Studio:

  • 通过下方的logcat能够设施日志,可用于剖析应用程序解体起因。
  • 可进行日志筛选,e.g.package:com.awesomeproject level:error

内部字体

https://www.jianshu.com/p/6000eb97d53b

  • 不像H5一样,针对不同的font-weight设置@`font-face`
@font-face { font-family: "Sans"; font-weight: 100; src: url("../assets/fonts/Thin.otf");}
@font-face { font-family: "Sans"; font-weight: 200; src: url("../assets/fonts/Light.otf");}
@font-face { font-family: "Sans"; font-weight: 400; src: url("../assets/fonts/Normal.otf");}

只能通过字体文件名设置不同的fontFamily

// font-weight:100
f100: {
  fontFamily: 'Thin' // 字体文件名
}
// font-weight:200
f200: {
  fontFamily: 'Light' // 字体文件名
}
// font-weight:300
f300: {
  fontFamily: 'Normal' // 字体文件名
}

动画

  1. toValue只能定义整型,如果动画须要其余类型的值,须要应用interpolate
  2. 无奈对svg中的circle应用transform定义动画,只能通过View或其余元素包裹,实现旋转动画
  3. 如果页面内有大量运算,==Animated会被阻塞,因为Animated是在JS线程运行,而非UI线程(https://stackoverflow.com/questions/56980044/how-to-prevent-setinterval-in-react-native-from-blocking-running-animation)
  const animationRef = useRef({
    strokeDashoffset: new Animated.Value(240),
    rotateZ: new Animated.Value(1)
  })
  useEffect(() => {
    const animationDef = () => {
      Animated.loop(
        Animated.sequence([
          Animated.parallel([
            Animated.timing(
              animationRef.current.strokeDashoffset,
              {
                toValue: 240,
                useNativeDriver: true,
                duration: 900
              }
            ),
            Animated.timing(
              animationRef.current.rotateZ,
              {
                toValue: 2,
                useNativeDriver: true,
                duration: 900
              }
            ),
          ]),
          Animated.parallel([
            Animated.timing(
              animationRef.current.strokeDashoffset,
              {
                toValue: 200,
                useNativeDriver: true,
                duration: 100
              }
            ),
            Animated.timing(
              animationRef.current.rotateZ,
              {
                toValue: 3,
                useNativeDriver: true,
                duration: 100
              }
            ),
          ]),
          Animated.parallel([
            Animated.timing(
              animationRef.current.strokeDashoffset,
              {
                toValue: 200,
                useNativeDriver: true,
                duration: 900
              }
            ),
            Animated.timing(
              animationRef.current.rotateZ,
              {
                toValue: 4,
                useNativeDriver: true,
                duration: 900
              }
            ),
          ]),
          Animated.parallel([
            Animated.timing(
              animationRef.current.strokeDashoffset,
              {
                toValue: 240,
                useNativeDriver: true,
                duration: 100
              }
            ),
            Animated.timing(
              animationRef.current.rotateZ,
              {
                toValue: 5,
                useNativeDriver: true,
                duration: 100
              }
            ),
          ])
        ])
      ).start()
    }
    animationDef()
  }, [animationRef])
  const rotateZ = animationRef.current.rotateZ.interpolate({
    inputRange: [1, 2, 3, 4, 5],
    outputRange: ['-100deg', '-424deg', '-500deg', '-784deg', '-820deg'],
  })

罕用第三方库

我的项目须要重启才能够,否则会报模块找不到

  • 渐变色

    • import LinearGradient from 'react-native-linear-gradient';
  • 反对svg

    • "react-native-svg": "^13.9.0",
    • "react-native-svg-transformer": "^1.0.0",
  • 全局款式变量

"react-native-extended-stylesheet": "^0.12.0",

  • 毛玻璃

@react-native-community/blur —— 只能有一个子节点,多个子节点须要包裹

  • 投影

yarn add react-native-shadow-2 —— 包裹惟一子节点不能应用margin,会导致Shadow不对齐,如果须要设置margin,应用View包裹Shadow元素

第三方库应用带来的问题

UseEffect vs. UseFocusEffect

应用react-native-navigation在同一路由栈中切换页面,页面没有销毁,所以,useEffect的革除回调不会被触发,须要思考应用[useFocusEffect](https://reactnavigation.org/docs/bottom-tab-navigator)代替useEffect

环境变量设置

应用react-native-dotenv或其余第三方库设置环境变量,会呈现报错:Property left of AssignmentExpression expected node to be of a type ["LVal"] but instead got "StringLiteral"

须要批改rn-nodeify主动生成的shim.js文件,给process.env换一种赋值形式:

const isDev = typeof __DEV__ === 'boolean' && __DEV__
const env = process.env || {}
env['NODE_ENV'] = isDev ? 'development' : 'production'
process.env = env
if (typeof localStorage !== 'undefined') {
  localStorage.debug = isDev ? '*' : ''
}

报错:ReferenceError: Property 'TextEncoder' doesn't exist, js engine: hermes

https://github.com/hapijs/joi/issues/2141

android.support.annotation包不存在的谬误

jetifier可一键解决

  1. 谬误产生在你将你的Android应用程序迁徙到应用androidx 库时
  2. import 当你应用androidx ,你须要更新你的Android源代码,用androidx.annotation 包替换所有android.support.annotation 包的语句。

https://www.qiniu.com/qfans/qnso-40380519【举荐】

疑难杂症

  • 如何同时运行模拟器和真机how to run react-native app on simulator and real device at the same time

https://stackoverflow.com/questions/51336123/how-can-i-run-two-react-native-applications

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理