React Native
使用 Gemini 学习新知识效率真的很高
Watchman crawl failed. Retrying once with node crawler
Watchman crawl failed. Retrying once with node crawler. Usually this happens when watchman isn’t running. Create an empty .watchmanconfig file in your project’s root folder or initialize a git or hg repository in your project. Error: watchman –no-pretty get-sockname returned with exit code=1, signal=null,
解决办法:
# Step 1:
watchman watch-del-all
#Step 2:
watchman shutdown-server
配置全局 Android 镜像
1. 修改 gradle.properties 文件(全局配置)
修改 ~/.gradle/gradle.properties 文件(macOS/Linux)或 C:\Users<YourUserName>.gradle\gradle.properties 文件(Windows) 可以进行全局配置,影响所有使用 Gradle 的项目。
打开或创建上述 gradle.properties 文件。
添加以下配置:
distributionUrl=https\://mirrors.aliyun.com/gradle/gradle-x.x.x-all.zip
2. 使用 init.gradle 文件(更灵活的全局配置)
通过 init.gradle 文件可以进行更灵活的全局配置,例如根据不同的条件使用不同的镜像。
-
在 ~/.gradle/ 目录下创建一个名为 init.gradle 的文件(如果不存在)。
-
添加以下配置:
allprojects {
buildscript {
repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/' }
maven { url 'https://maven.aliyun.com/repository/central/' }
google()
mavenCentral()
jcenter()
}
}
repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/' }
maven { url 'https://maven.aliyun.com/repository/central/' }
google()
mavenCentral()
jcenter()
}
}
这段配置会将 Maven Central、Google 和 JCenter 仓库都指向阿里云的镜像。
- 保存文件。
Android NDK 是什么
Android NDK (Native Development Kit) 是一套工具,它允许你在 Android 应用中使用 C 和 C++ 等原生代码。 通常情况下,Android 应用主要使用 Java 或 Kotlin 编写,运行在 Android Runtime (ART) 虚拟机上。但有些情况下,使用原生代码可以带来性能上的提升或其他方面的优势。
NDK 的主要用途:
- 性能密集型任务: 对于需要高性能计算的任务,例如游戏、图形处理、音视频编解码、物理引擎等,使用 C/C++ 等原生代码通常比 Java/Kotlin 更有效率。
- 代码复用: 如果你已经有使用 C/C++ 编写的现有库或代码,可以使用 NDK 将它们集成到 Android 应用中,避免重复开发。
- 访问底层硬件: NDK 允许你直接访问一些底层硬件和系统 API,例如传感器、OpenGL ES 等,这在某些特定应用场景下非常有用。
- 平台移植: 如果你的应用需要在多个平台(包括 Android)上运行,使用 C/C++ 编写核心代码可以更容易地进行移植。
NDK 的组成部分:
- 编译器和工具链: NDK 包含用于将 C/C++ 代码编译成可在 Android 设备上运行的机器码的编译器(通常是 Clang)和其他构建工具(例如 Make、CMake)。
- 头文件和库: NDK 提供了一系列头文件和库,允许你访问 Android 平台的各种功能,例如文件 I/O、网络、图形、音频等。
- 调试器: NDK 提供了用于调试原生代码的工具,例如 GDB。
- 调试器: NDK提供了用于调试正确代码的工具,例如GDB。
- JNI (Java Native Interface): JNI 是 Java 和 C/C++ 代码之间进行通信的桥梁。通过 JNI,Java 代码可以调用 C/C++ 函数,C/C++ 代码也可以回调 Java 方法。
Disable Ccache
构建时报错:
[1/3] Building CXX object CMakeFiles/appmodules.dir/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/android/app/build/generated/autolinking/src/main/jni/autolinking.cpp.o
FAILED: CMakeFiles/appmodules.dir/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/android/app/build/generated/autolinking/src/main/jni/autolinking.cpp.o
ccache /Users/xiaoxiwang/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ --target=aarch64-none-linux-android24 --sysroot=/Users/xiaoxiwang/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -Dappmodules_EXPORTS -I/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/node_modules/react-native/ReactAndroid/cmake-utils/default-app-setup -I/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/android/app/build/generated/autolinking/src/main/jni -isystem
为什么需要禁用 ccache?
虽然 ccache 通常可以显著提高编译速度,但在某些情况下,它可能会导致问题:
- 缓存污染: 如果构建环境或依赖项发生重大变化,ccache 的缓存可能变得无效,导致构建错误或不一致的结果。
- 构建问题排查: 在排查构建问题时,禁用 ccache 可以帮助你确定问题是否与缓存有关。
- 特定构建配置: 某些特定的构建配置可能与 ccache 不兼容。
通过设置 CCACHE_DISABLE 环境变量是在 React Native 项目中禁用 ccache 的最简单和推荐的方法。
- 在终端中(临时禁用) : 在运行构建命令之前,在终端中执行以下命令:
export CCACHE_DISABLE=1
npx react-native run-android
或者
CCACHE_DISABLE=1 npx react-native run-android
- 在 shell 配置文件中(永久禁用): 为了永久禁用 ccache,你可以将 export CCACHE_DISABLE=1 添加到你的 shell 配置文件中,
例如
/.bashrc、/.zshrc 或 ~/.profile。然后,执行 source ~/.bashrc(或相应的命令)来使更改生效。
expo run:android
和 expo start --dev-client
命令的区别
expo run:android
和 expo start --dev-client
是在使用 Expo 开发 React Native 应用时常用的两个命令,它们的功能有所不同,理解它们的区别对于高效开发至关重要。
expo start --dev-client
命令
- 作用: 启动 Metro Bundler (JavaScript 打包器) 和开发服务器。这个服务器负责提供你的 JavaScript 代码和静态资源给运行在设备或模拟器上的应用程序。
- 连接方式: 应用程序通过网络连接到这个开发服务器。这意味着你的设备或模拟器需要能够访问运行开发服务器的计算机。
- 实时更新: 当你修改 JavaScript 代码并保存时,Metro Bundler 会自动重新打包代码,应用程序会自动刷新,实现实时更新 (Fast Refresh)。
- 开发者菜单: 应用程序可以通过摇晃设备(真机)或使用快捷键(模拟器)打开开发者菜单,其中包含一些调试选项,例如 “Reload”、“Debug JS Remotely” 等。
- 不构建原生应用: 这个命令本身不构建原生 Android 或 iOS 应用。它只是启动一个开发服务器,供已经构建好的应用连接。
- 通常与
expo-dev-client
配合使用: 虽然也可以与 Expo Go 配合使用,但为了获得最佳开发体验,尤其是需要使用原生模块或进行更深入调试时,通常与 expo-dev-client 配合使用。
expo run:android
命令
- 作用: 构建并安装 Android 应用程序到连接的 Android 设备或模拟器。这个命令会: 如果尚未安装 expo-dev-client,则会提示你安装。 根据你的项目配置(包括 JavaScript 代码和原生代码),构建一个包含 expo-dev-client 的 Android 应用程序。 将构建好的应用程序安装到你的设备或模拟器上。
- 构建原生应用: 这个命令会构建原生 Android 应用(.apk 文件)。
- 需要设备或模拟器: 这个命令需要在连接的 Android 设备或运行的 Android 模拟器上执行。
- 首次运行较慢: 首次运行此命令时,由于需要构建应用程序,因此速度较慢。后续运行速度会加快,因为只会重新打包更改的部分。
- 配合
expo start --dev-client
使用: 构建完成后,通常需要运行 expo start –dev-client 命令来启动开发服务器,以便应用程序可以连接并进行实时更新。
bun add expo-linear-gradient
vs bunx expo install expo-linear-gradient
npx expo install expo-linear-gradient
和 bun add expo-linear-gradient
都是在 React Native 项目中安装 expo-linear-gradient 包的命令,但它们使用的包管理器不同,并且针对的项目类型也有所侧重。
npx expo install expo-linear-gradient
:
-
使用的包管理器:
Expo CLI (expo)
。 -
适用的项目类型:主要用于
Expo managed wrokflow
项目 -
工作方式:
npx
是Node
包执行器,用于执行本地或全局安装的 npm 包。expo install
是Expo CLI
提供的命令,它不仅会安装指定的包,还会自动处理一些必要的配置,例如更新 package.json 文件、安装兼容的依赖项等。- 对于 Expo managed workflow 项目,expo install 会确保安装的包与当前 Expo SDK 版本兼容。这是非常重要的,因为 Expo SDK 版本和依赖包之间存在一定的兼容性要求。
-
优点:
- 简单方便,尤其是在
Expo managed workflow
项目中。 - 自动处理依赖关系和配置,减少手动操作。
- 确保依赖包与 Expo SDK 版本兼容。
- 简单方便,尤其是在
-
缺点:
- 只能在安装了 Expo CLI 的环境中使用。
- 不适用于 bare React Native 项目(除非使用 Expo prebuild)。
bun add expo-linear-gradient
:
- 使用的包管理器: Bun。
- 适用项目类型: 适用于使用 Bun 作为包管理器的 React Native 项目。包括 Expo managed workflow 项目(在安装了 Bun 的情况下),以及 bare React Native 项目。
- 工作方式:
- bun add 是 Bun 提供的命令,用于安装包。
- Bun 是一个新兴的 JavaScript 运行时和包管理器,旨在提供更快的速度和更好的性能。
- 优点:
- 速度通常比 npm 和 yarn 快。
- 支持多种包管理器格式(npm、yarn、pnpm)。
- 缺点:
- 相对较新,生态不如 npm 成熟。
- 需要单独安装 Bun。
(NOBRIDGE) ERROR Warning: Error: Maximum update depth exceeded.
在使用Zustnd
时,出现以下错误警告信息:
(NOBRIDGE) ERROR Warning: Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
This error is located at:
in ArtistsScreen
in Unknown (created by Route(index))
in Suspense (created by Route(index))
in Route (created by Route(index))
in Route(index) (created by SceneView)
in StaticContainer
in EnsureSingleNavigator (created by SceneView)
in SceneView (created by NativeStackNavigator)
in RNSScreenContentWrapper (created by ScreenContentWrapper)
in ScreenContentWrapper (created by DebugContainer)
in DebugContainer
in RNSScreen (created by Animated(Anonymous))
in Animated(Anonymous)
in Suspender (created by Freeze)
in Suspense (created by Freeze)
in Freeze (created by DelayedFreeze)
in DelayedFreeze
in InnerScreen (created by Screen)
in Screen
in ScreenStackItem (created by SceneView)
in SceneView (created by NativeStackView)
in RNSScreenStack (created by ScreenStack)
in Unknown (created by ScreenStack)
in ScreenStack (created by NativeStackView)
in RCTView (created by View)
in View (created by SafeAreaProviderCompat)
in SafeAreaProviderCompat (created by NativeStackView)
in NativeStackView (created by NativeStackNavigator)
in PreventRemoveProvider (created by NavigationContent)
in NavigationContent
in Unknown (created by NativeStackNavigator)
in NativeStackNavigator
in Unknown (created by ArtistsScreenLayout)
in RCTView (created by View)
in View (created by ArtistsScreenLayout)
in ArtistsScreenLayout
in Unknown (created by Route(artists))
in Suspense (created by Route(artists))
in Route (created by Route(artists))
in Route(artists) (created by SceneView)
in StaticContainer
in EnsureSingleNavigator (created by SceneView)
in SceneView (created by BottomTabNavigator)
in RCTView (created by View)
in View (created by Screen)
in RCTView (created by View)
in View (created by Animated(View))
in Animated(View) (created by Background)
in Background (created by Screen)
in Screen (created by BottomTabView)
in RNSScreen (created by Animated(Anonymous))
in Animated(Anonymous)
in Suspender (created by Freeze)
in Suspense (created by Freeze)
in Freeze (created by DelayedFreeze)
in DelayedFreeze
in InnerScreen (created by Screen)
in Screen (created by MaybeScreen)
in MaybeScreen (created by BottomTabView)
in RNSScreenNavigationContainer (created by ScreenContainer)
in ScreenContainer (created by MaybeScreenContainer)
in MaybeScreenContainer (created by BottomTabView)
...
原有的实现方式:
export const useArtists = () => {
return useLibraryStore((state) => {
return state.tracks.reduce((acc, track) => {
const existingArtist = acc.find((artist) => artist.name === track.artist);
if (existingArtist) {
existingArtist.tracks.push(track);
} else {
acc.push({
name: track.artist ?? "Unknown",
tracks: [track],
});
}
return acc;
}, [] as Artist[]);
});
};
const artists = useArtists();
const filteredArtists = useMemo(() => {
if (!search) return artists;
return artists.filter(artistNameFilter(search));
}, [artists, search]);
(NOBRIDGE) ERROR Warning: Error: Maximum update depth exceeded.
当组件触发无限循环重新渲染时,通常会发生此错误。
问题在于 useLibraryStore
如何更新 useArtists
hook 内的状态。
当前的 useArtists
选择器每次调用时都会创建一个新的Artist
对象数组,即使 state.tracks
没有更改也是如此。这是因为reduce
总是创建一个新数组。
解决方案:使用 useMemo
将 useArtists
选择器的结果缓存起来,可以避免无限循环。
export const useTracks = () => useLibraryStore((state) => state.tracks);
export const useArtists = () => {
return useTracks().reduce((acc, track) => {
const existingArtist = acc.find((artist) => artist.name === track.artist);
if (existingArtist) {
existingArtist.tracks.push(track);
} else {
acc.push({
name: track.artist ?? "Unknown",
tracks: [track],
});
}
return acc;
}, [] as Artist[]);
};
const artists = useArtists();
const filteredArtists = useMemo(() => {
if (!search) return artists;
return artists.filter(artistNameFilter(search));
}, [artists, search]);
React Native 新架构中的 JSI
React Native 的 JSI(JavaScript Interface)是 React Native 新架构中的一个关键组成部分,它带来了许多重要的影响,主要体现在性能、灵活性和跨平台一致性等方面。
- 性能提升
-
消除 Bridge 带来的开销:
- 在旧架构中,JavaScript 和原生代码之间的通信依赖于 Bridge,数据需要在两者之间进行序列化和反序列化,这会带来性能开销。
- JSI 允许 JavaScript 直接调用原生代码,消除了这种序列化和反序列化的过程,从而提高了通信效率。
-
同步调用:
- JSI 使得 JavaScript 可以同步调用原生模块,这对于需要高性能的场景非常有用。
- 例如,在数据存储、图像处理等场景中,同步调用可以显著提高性能。
- 更灵活的集成
- 直接调用原生模块:
- JSI 允许 JavaScript 直接持有对 C++ 宿主对象的引用,并对其进行调用,这使得原生模块的集成更加灵活。
- 开发者可以更方便地使用原生平台的特性和功能。
- 引擎替换:
- JSI 使得 JavaScript 引擎(如 JSC)可以更容易地被替换成其他引擎(如 V8)。
- 这为 React Native 的未来发展提供了更大的灵活性。
- 跨平台一致性
- 共享的 C++ 核心:
- JSI 为 React Native 提供了一个共享的 C++ 核心,这有助于在不同平台上保持一致的行为和性能。
- 开发者可以更方便地编写跨平台的原生模块。
- 对开发者带来的影响
- 原生模块开发:
- JSI 的引入,给原生模块的开发带来了新的工作量。
- 原生模块的开发者需要使用C++来与javascript进行交互。
- 性能优化:
- JSI 为开发者提供了更多优化 React Native 应用性能的手段。
- 开发者可以利用 JSI 编写高性能的原生模块,从而提高应用的整体性能。
总结
JSI 的引入,是 React Native 架构的一次重大升级,它为 React Native 带来了更高的性能、更大的灵活性和更好的跨平台一致性。