React Native開發(fā)初探

環(huán)境安裝

參見官網(wǎng):

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

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

Notes

項(xiàng)目創(chuàng)建

npx react-native@latest init rnProject

Notes:項(xiàng)目名稱只支持駝峰,不支持連字符。

項(xiàng)目運(yùn)行

npm run android

  • 往模擬器或真機(jī)安裝APK:包運(yùn)行所需要的資源。

  • 引用原生依賴,需要重新運(yùn)行該命令。

  • --mode=release臨時打包,輸出路徑android\app\build\outputs\apk\release

npm run start

  • 運(yùn)行Metro打包JS代碼,啟動熱更新,在模擬器或真機(jī)實(shí)時查看改動。

  • --reset-cache清除Metro緩存,重新編譯JS代碼。

清除運(yùn)行緩存

  • "./gradlew" clean

    • 切換到android目錄下,在命令行執(zhí)行該命令,可清除gradle緩存。
  • npx react-native start --reset-cache

    • 清除Metro緩存,重新編譯JS代碼

    • 應(yīng)用場景:環(huán)境報(bào)錯、開發(fā)者工具出現(xiàn)問題

頁面適配

方案一:

// 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 {
  StyleSheet,
  Text,
  Dimensions,
  PixelRatio,
  View,
} from 'react-native';
const { width: layoutWidth, height: layoutHeight } = Dimensions.get('window') // 獲取到設(shè)備pd
const ratio = PixelRatio.get() // 像素密度
const pixelWidth = PixelRatio.getPixelSizeForLayoutSize(layoutWidth) // pd轉(zhuǎn)px
const pixelHeight = PixelRatio.getPixelSizeForLayoutSize(layoutHeight) // pd轉(zhuǎn)px
const designWidth = 950 // 設(shè)計(jì)圖尺寸

/**
 * 設(shè)備以px展示,以1/ratio縮放,通過translateX、translateY重置transform原點(diǎn)
 * | 設(shè)計(jì)圖 | 設(shè)備 |
 * | 20px | x |
 * | 950px | pixelWidth |
 * 同等占比:x = 20 * pixelWidth / 950
 * ============================================
 * 結(jié)合縮放比例:
 * scale = pixelWidth / (designWidth * ratio)
 * 其余組件可按設(shè)計(jì)圖設(shè)置像素即可
*/
const styles = StyleSheet.create({
  adapter: {
    width: pixelWidth,
    height: pixelHeight,
    transform: [
      {
        translateX: -pixelWidth * 0.5,
      },
      {
        translateY: -pixelHeight * 0.5
      },
      {
        scale: pixelWidth / (ratio * designWidth)
      },
      {
        translateX: pixelWidth * 0.5,
      },
      {
        translateY: pixelHeight * 0.5
      }
    ]
  },
  fullscreen: {
    width: designWidth,
    height:90,
    backgroundColor: 'red'
  },
  halfscreen: {
    width: designWidth / 2,
    height:90,
    backgroundColor: 'green'
  },
  quaterscreen: {
    width: designWidth / 4,
    height:90,
    backgroundColor: 'blue'
  }
});

function App(): JSX.Element {
  return (
    <View style={styles.adapter}>
      <View style={styles.fullscreen}>
        <Text>1</Text>
      </View>
      <View style={styles.halfscreen}>
        <Text>2</Text>
      </View>
      <View style={styles.quaterscreen}>
        <Text>3</Text>
      </View>
    </View>
  );
}

export default App;

結(jié)構(gòu)&樣式

原生組件:

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

  • 只有特定的組件才有交互樣式與事件,如Button、TouchableHighlight、TouchableOpacity,其余組件無法綁定onPress事件

    • 一般不選用Button,而使用自定義Button組件,因?yàn)樵?code>Button樣式不好調(diào)節(jié)
  • 不支持svg,需要借助第三方庫,e.g.react-native-svgreact-native-svg-transformer

  • 結(jié)構(gòu)搭建:將HTML的標(biāo)簽用法完全忘記,重新根據(jù)文檔學(xué)習(xí)使用方法。

樣式:

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

  • 只能使用組件規(guī)范的樣式,否則不起作用

  • lineHeight不能使文字居中,請使用justifyContent

  • 針對Text組件,需要單獨(dú)定義相關(guān)樣式,不會繼承父級非Text組件Text樣式,e.g.不會從父級繼承color

  • 不支持漸變色、投影等效果,需要借助第三方庫,e.g.react-native-linear-gradient、react-native-shadow-2

Notes:第三方庫的安裝,需要重新啟動項(xiàng)目,否則,會報(bào)模塊找不到。

嵌套ScrollView

  • flatlist嵌套在scrollView無法滾動,父子級都需要設(shè)置nestedScrollEnabled屬性

  • scrollView必須設(shè)定一個高度,否則會使用默認(rèn)高度,并非由內(nèi)容撐開。

    • 注意stylecontentContainerStyle的區(qū)別

    • scrollView高度設(shè)定:專設(shè)View組件包裹,scrollView高度設(shè)置為flex:1

代碼調(diào)試

react-native-debugger的使用

  • 連接react-native-debugger,需要應(yīng)用程序開啟Debug模式
    • 真機(jī):搖一搖手機(jī),出現(xiàn)操作面板,選擇Debug
開啟Debug模式
  • 模擬器:模擬器聚焦后,使用Ctrl + m打開操作面板
    • 雙擊rreload
  • 使用react-native-debugger時,如果發(fā)出網(wǎng)絡(luò)請求,可能會在Network面板發(fā)現(xiàn)沒有request發(fā)起,請求(成功/失?。┗卣{(diào)沒有執(zhí)行。

    • react-native-debugger非Chrome控制臺面板中右鍵,開啟Enable Network Inspect
開啟網(wǎng)絡(luò)審查
  • 使用react-native-debugger時,如果發(fā)現(xiàn)Components面板始終空白,使用npx react-native start --reset-cache清除緩存啟動項(xiàng)目

  • react-native-debugger的使用,需要同版本react-devtoolsreact-devtools-core作為開發(fā)依賴。

當(dāng)前實(shí)例獲取

類同Chrome開發(fā)者工具,在RN任意調(diào)試工具的Console面板:

  • 通過$reactNative等同于import $reactNative from "react-native",可以使用react-native庫中的方法。
image.png
  • 通過$r可以獲取當(dāng)前選中節(jié)點(diǎn)實(shí)例
image.png

多種調(diào)試工具切換

在模擬器中開啟debug后,調(diào)試每次都會自動默認(rèn)打開debugger-ui頁面,如何關(guān)閉:

  1. 在啟動項(xiàng)目npm run start的命令行中使用shift + d切換到模擬器并打開調(diào)試面板,此時點(diǎn)擊debug面板選項(xiàng)不再自動調(diào)起debugger-ui頁面。

  2. 通過取消選中小勾號來解決它Maintain Priority

  3. 避免直接在模擬器中通過Ctrl + M喚起面板,選擇debug,這樣會默認(rèn)調(diào)起debugger-ui

UI審查

開啟Android Studio:

  • 通過右下角的Layout Inspector,選擇模擬器或真機(jī)。
UI審查:選擇設(shè)備
  • 點(diǎn)擊結(jié)構(gòu),可以查看組件屬性

  • 可以通過該面板的右上角設(shè)置,切換單位。

UI審查
  • 可以通過給組件賦值testID、aria-label,進(jìn)行組件識別。

日志查看

開啟Android Studio:

  • 通過下方的logcat可以設(shè)備日志,可用于分析應(yīng)用程序崩潰原因。

  • 可進(jìn)行日志篩選,e.g.package:com.awesomeproject level:error

日志審查

外部字體

http://m.itdecent.cn/p/6000eb97d53b

  • 不像H5一樣,針對不同的font-weight設(shè)置@``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");}

只能通過字體文件名設(shè)置不同的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或其他元素包裹,實(shí)現(xiàn)旋轉(zhuǎn)動畫

  3. 如果頁面內(nèi)有大量運(yùn)算,==Animated會被阻塞,因?yàn)?code>Animated是在JS線程運(yùn)行,而非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'],
  })

常用第三方庫

項(xiàng)目需要重啟才可以,否則會報(bào)模塊找不到

  • 漸變色

    • 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 —— 只能有一個子節(jié)點(diǎn),多個子節(jié)點(diǎn)需要包裹

  • 投影

yarn add react-native-shadow-2 —— 包裹唯一子節(jié)點(diǎn)不能使用margin,會導(dǎo)致Shadow不對齊,如果需要設(shè)置margin,使用View包裹Shadow元素

第三方庫使用帶來的問題

UseEffect vs. UseFocusEffect

使用react-native-navigation在同一路由棧中切換頁面,頁面沒有銷毀,所以,useEffect的清除回調(diào)不會被觸發(fā),需要考慮使用[useFocusEffect](https://reactnavigation.org/docs/bottom-tab-navigator)替代useEffect

環(huán)境變量設(shè)置

使用react-native-dotenv或其他第三方庫設(shè)置環(huán)境變量,會出現(xiàn)報(bào)錯: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 ? '*' : ''
}

報(bào)錯:ReferenceError: Property 'TextEncoder' doesn't exist, js engine: hermes

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

android.support.annotation包不存在的錯誤

jetifier可一鍵解決

  1. 錯誤發(fā)生在你將你的Android應(yīng)用程序遷移到使用androidx 庫時

  2. import 當(dāng)你使用androidx ,你需要更新你的Android源代碼,用androidx.annotation 包替換所有android.support.annotation 包的語句。

https://www.qiniu.com/qfans/qnso-40380519【推薦】

react-native-webview Android postMessage 不工作

在安卓環(huán)境下,通過postmessage通信,需要使用document監(jiān)聽,ios下通過window監(jiān)聽。
https://github.com/react-native-webview/react-native-webview/issues/356

疑難雜癥

  • 如何同時運(yùn)行模擬器和真機(jī)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

  • 調(diào)試工具flipperReact Devtools面板始終無法連接到應(yīng)用程序

進(jìn)行一下端口映射即可:adb reverse tcp:8097 tcp:8097

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容