"RN 开发就像谈恋爱,你永远不知道下一秒会遇到什么 bug。" —— 某 RN 开发者,在第 N 次
npm install失败后的感悟
前言:为什么 RN 开发需要"邪修"?
React Native 是一个神奇的框架:
- 它让你用 JavaScript 写原生应用 —— 听起来很美好
- 它让你一套代码跑两端 —— 理论上是这样
- 它让你体验"Learn once, write anywhere" —— 实际上是"Learn once, debug everywhere"
每个 RN 开发者都经历过:
node_modules删了重装,重装了再删- iOS 能跑 Android 崩,Android 能跑 iOS 白屏
- 升级个版本,整个项目原地爆炸
所以,我们需要一些..."非常规手段"来生存。
免责声明:本文技巧可能导致代码审查者当场去世,请谨慎使用。
第一章:环境配置的玄学
1.1 问题:环境配置是一门玄学
# RN 开发者的日常
npm install
# 失败
rm -rf node_modules && npm install
# 还是失败
rm -rf node_modules package-lock.json && npm install
# 依然失败
# 终极大招
rm -rf node_modules package-lock.json
rm -rf ios/Pods ios/Podfile.lock
rm -rf android/.gradle android/app/build
watchman watch-del-all
npm cache clean --force
npm install
cd ios && pod install --repo-update && cd ..
# 如果还是失败,重启电脑
# 如果重启还是失败,重装系统
# 如果重装还是失败,换电脑
1.2 邪修技巧:一键清理脚本
#!/bin/bash
# 邪修秘籍第一式:核弹级清理脚本
# 保存为 nuke.sh,chmod +x nuke.sh
echo "🔥 开始核弹级清理..."
# 清理 node_modules
echo "💣 清理 node_modules..."
rm -rf node_modules
rm -rf package-lock.json
rm -rf yarn.lock
# 清理 iOS
echo "💣 清理 iOS..."
rm -rf ios/Pods
rm -rf ios/Podfile.lock
rm -rf ios/build
rm -rf ~/Library/Developer/Xcode/DerivedData
# 清理 Android
echo "💣 清理 Android..."
rm -rf android/.gradle
rm -rf android/app/build
rm -rf android/build
# 清理缓存
echo "💣 清理缓存..."
watchman watch-del-all 2>/dev/null || true
rm -rf $TMPDIR/react-* 2>/dev/null || true
rm -rf $TMPDIR/metro-* 2>/dev/null || true
rm -rf $TMPDIR/haste-* 2>/dev/null || true
npm cache clean --force
# 重新安装
echo "📦 重新安装依赖..."
npm install
# iOS Pod 安装
echo "📦 安装 iOS Pods..."
cd ios && pod install --repo-update && cd ..
echo "✅ 清理完成!试试 npm run ios 或 npm run android"
// package.json 里加个快捷命令
{
"scripts": {
"nuke": "bash nuke.sh",
"ios": "react-native run-ios",
"android": "react-native run-android",
"clean-ios": "cd ios && rm -rf Pods Podfile.lock build && pod install && cd ..",
"clean-android": "cd android && ./gradlew clean && cd .."
}
}
使用方法:当一切都不工作的时候,npm run nuke
第二章:样式的骚操作
2.1 问题:StyleSheet 写到手抽筋
// 正常人写的样式
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
paddingHorizontal: 16,
paddingTop: 20,
},
title: {
fontSize: 24,
fontWeight: "bold",
color: "#333",
marginBottom: 12,
},
subtitle: {
fontSize: 16,
color: "#666",
marginBottom: 8,
},
// 还有 100 个样式...
})
2.2 邪修技巧:工具函数一把梭
// 邪修秘籍第二式:样式工具函数
import { StyleSheet, Dimensions, Platform } from "react-native"
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get("window")
// 响应式尺寸(以 375 宽度为基准)
const scale = SCREEN_WIDTH / 375
export const s = (size: number) => Math.round(size * scale)
// 快速生成间距
export const spacing = {
xs: s(4),
sm: s(8),
md: s(16),
lg: s(24),
xl: s(32),
}
// 快速生成字体样式
export const typography = {
h1: { fontSize: s(32), fontWeight: "bold" as const, color: "#333" },
h2: { fontSize: s(24), fontWeight: "bold" as const, color: "#333" },
h3: { fontSize: s(20), fontWeight: "600" as const, color: "#333" },
body: { fontSize: s(16), color: "#333" },
caption: { fontSize: s(14), color: "#666" },
small: { fontSize: s(12), color: "#999" },
}
// 快速生成 Flex 布局
export const flex = {
row: { flexDirection: "row" as const },
col: { flexDirection: "column" as const },
center: { justifyContent: "center" as const, alignItems: "center" as const },
between: { justifyContent: "space-between" as const },
around: { justifyContent: "space-around" as const },
start: { alignItems: "flex-start" as const },
end: { alignItems: "flex-end" as const },
wrap: { flexWrap: "wrap" as const },
grow: { flex: 1 },
}
// 快速生成阴影(iOS 和 Android 统一)
export const shadow = (elevation: number = 4) => ({
...Platform.select({
ios: {
shadowColor: "#000",
shadowOffset: { width: 0, height: elevation / 2 },
shadowOpacity: 0.1,
shadowRadius: elevation,
},
android: {
elevation,
},
}),
})
// 快速生成圆角
export const rounded = {
sm: { borderRadius: s(4) },
md: { borderRadius: s(8) },
lg: { borderRadius: s(16) },
full: { borderRadius: 9999 },
}
// 使用示例 const styles = StyleSheet.create({ container: { ...flex.grow, ...flex.col, backgroundColor: '#fff', padding: spacing.md, }, card: { ...rounded.md, ...shadow(4), backgroundColor: '#fff', padding: spacing.md, marginBottom: spacing.sm, }, title: { ...typography.h2, marginBottom: spacing.xs, }, });
### 2.3 更邪的技巧:内联样式生成器
```tsx
// 邪修秘籍第三式:链式样式生成器
class StyleBuilder {
private style: Record<string, any> = {};
// 布局
flex(value: number = 1) { this.style.flex = value; return this; }
row() { this.style.flexDirection = 'row'; return this; }
col() { this.style.flexDirection = 'column'; return this; }
center() {
this.style.justifyContent = 'center';
this.style.alignItems = 'center';
return this;
}
between() { this.style.justifyContent = 'space-between'; return this; }
// 间距
p(value: number) { this.style.padding = s(value); return this; }
px(value: number) { this.style.paddingHorizontal = s(value); return this; }
py(value: number) { this.style.paddingVertical = s(value); return this; }
m(value: number) { this.style.margin = s(value); return this; }
mx(value: number) { this.style.marginHorizontal = s(value); return this; }
my(value: number) { this.style.marginVertical = s(value); return this; }
mb(value: number) { this.style.marginBottom = s(value); return this; }
mt(value: number) { this.style.marginTop = s(value); return this; }
// 尺寸
w(value: number | string) {
this.style.width = typeof value === 'number' ? s(value) : value;
return this;
}
h(value: number | string) {
this.style.height = typeof value === 'number' ? s(value) : value;
return this;
}
size(w: number, h?: number) {
this.style.width = s(w);
this.style.height = s(h ?? w);
return this;
}
// 背景和边框
bg(color: string) { this.style.backgroundColor = color; return this; }
rounded(value: number = 8) { this.style.borderRadius = s(value); return this; }
border(width: number = 1, color: string = '#ddd') {
this.style.borderWidth = width;
this.style.borderColor = color;
return this;
}
// 文字
text(size: number, color: string = '#333') {
this.style.fontSize = s(size);
this.style.color = color;
return this;
}
bold() { this.style.fontWeight = 'bold'; return this; }
// 阴影
shadow(elevation: number = 4) {
Object.assign(this.style, shadow(elevation));
return this;
}
// 构建
build() { return this.style; }
}
// 快捷函数
export const $ = () => new StyleBuilder();
// 使用:像写 Tailwind 一样爽
<View style={$().flex().col().bg('#fff').p(16).build()}>
<View style={$().row().between().mb(12).build()}>
<Text style={$().text(18).bold().build()}>标题</Text>
<Text style={$().text(14, '#999').build()}>更多</Text>
</View>
<View style={$().bg('#f5f5f5').rounded(8).p(12).shadow(2).build()}>
<Text style={$().text(16).build()}>内容</Text>
</View>
</View>
代码审查者:这什么鬼写法? 你:这叫"声明式样式构建器",Tailwind 同款思路,懂?
第三章:状态管理的野路子
3.1 问题:Redux 写到怀疑人生
// Redux 经典三件套
// actions.ts
export const SET_USER = "SET_USER"
export const setUser = (user) => ({ type: SET_USER, payload: user })
// reducer.ts
const initialState = { user: null }
export default function userReducer(state = initialState, action) {
switch (action.type) {
case SET_USER:
return { ...state, user: action.payload }
default:
return state
}
}
// 使用
dispatch(setUser({ name: "test" }))
// 就为了存个用户信息,写了三个文件...
3.2 邪修技巧:Zustand 一把梭
// 邪修秘籍第四式:Zustand 极简状态管理
import { create } from "zustand"
import { persist, createJSONStorage } from "zustand/middleware"
import AsyncStorage from "@react-native-async-storage/async-storage"
// 用户状态
interface UserState {
user: User | null
token: string | null
setUser: (user: User | null) => void
setToken: (token: string | null) => void
logout: () => void
}
export const useUserStore = create<UserState>()(
persist(
(set) => ({
user: null,
token: null,
setUser: (user) => set({ user }),
setToken: (token) => set({ token }),
logout: () => set({ user: null, token: null }),
}),
{
name: "user-storage",
storage: createJSONStorage(() => AsyncStorage),
}
)
)
// 应用状态
interface AppState {
theme: "light" | "dark"
language: string
isLoading: boolean
setTheme: (theme: "light" | "dark") => void
setLanguage: (lang: string) => void
setLoading: (loading: boolean) => void
}
export const useAppStore = create<AppState>((set) => ({
theme: "light",
language: "zh",
isLoading: false,
setTheme: (theme) => set({ theme }),
setLanguage: (language) => set({ language }),
setLoading: (isLoading) => set({ isLoading }),
}))
// 使用:简单到哭
function ProfileScreen() {
const { user, logout } = useUserStore()
const { theme, setTheme } = useAppStore()
return (
<View>
<Text>{user?.name}</Text>
<Button
title='切换主题'
onPress={() => setTheme(theme === "light" ? "dark" : "light")}
/>
<Button title='退出登录' onPress={logout} />
</View>
)
}
3.3 更简单的方案:useContext + useReducer
// 邪修秘籍第五式:原生 Hook 也能很香
import React, { createContext, useContext, useReducer, ReactNode } from "react"
// 定义状态和动作
type State = {
user: User | null
token: string | null
theme: "light" | "dark"
}
type Action =
| { type: "SET_USER"; payload: User | null }
| { type: "SET_TOKEN"; payload: string | null }
| { type: "SET_THEME"; payload: "light" | "dark" }
| { type: "LOGOUT" }
const initialState: State = {
user: null,
token: null,
theme: "light",
}
function reducer(state: State, action: Action): State {
switch (action.type) {
case "SET_USER":
return { ...state, user: action.payload }
case "SET_TOKEN":
return { ...state, token: action.payload }
case "SET_THEME":
return { ...state, theme: action.payload }
case "LOGOUT":
return { ...state, user: null, token: null }
default:
return state
}
}
// 创建 Context
const AppContext = createContext<{
state: State
dispatch: React.Dispatch<Action>
} | null>(null)
// Provider
export function AppProvider({ children }: { children: ReactNode }) {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
)
}
// 自定义 Hook
export function useApp() {
const context = useContext(AppContext)
if (!context) throw new Error("useApp must be used within AppProvider")
return context
}
// 更方便的 Hook
export function useUser() {
const { state, dispatch } = useApp()
return {
user: state.user,
setUser: (user: User | null) =>
dispatch({ type: "SET_USER", payload: user }),
logout: () => dispatch({ type: "LOGOUT" }),
}
}
第四章:性能优化的黑魔法
4.1 问题:列表卡成 PPT
// 性能杀手写法
<FlatList
data={items}
renderItem={({ item }) => (
// 每次渲染都创建新函数
<TouchableOpacity onPress={() => handlePress(item)}>
<View style={{ padding: 16 }}>
{" "}
{/* 内联样式 */}
<Image source={{ uri: item.image }} style={{ width: 50, height: 50 }} />
<Text>{item.title}</Text>
</View>
</TouchableOpacity>
)}
/>
4.2 邪修技巧:性能优化三板斧
// 邪修秘籍第六式:FlatList 性能优化
import React, { memo, useCallback, useMemo } from "react"
import {
FlatList,
View,
Text,
Image,
TouchableOpacity,
StyleSheet,
} from "react-native"
// 1. 使用 memo 包裹列表项
const ListItem = memo(
({ item, onPress }: { item: Item; onPress: (item: Item) => void }) => {
return (
<TouchableOpacity onPress={() => onPress(item)} activeOpacity={0.7}>
<View style={styles.itemContainer}>
<Image source={{ uri: item.image }} style={styles.itemImage} />
<View style={styles.itemContent}>
<Text style={styles.itemTitle}>{item.title}</Text>
<Text style={styles.itemSubtitle}>{item.subtitle}</Text>
</View>
</View>
</TouchableOpacity>
)
}
)
// 2. 优化后的列表
function OptimizedList({ items }: { items: Item[] }) {
// 使用 useCallback 缓存函数
const handlePress = useCallback((item: Item) => {
console.log("Pressed:", item.id)
}, [])
// 使用 useCallback 缓存 renderItem
const renderItem = useCallback(
({ item }: { item: Item }) => (
<ListItem item={item} onPress={handlePress} />
),
[handlePress]
)
// 使用 useCallback 缓存 keyExtractor
const keyExtractor = useCallback((item: Item) => item.id.toString(), [])
// 使用 useMemo 缓存 getItemLayout(如果高度固定)
const getItemLayout = useMemo(
() => (_: any, index: number) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
}),
[]
)
return (
<FlatList
data={items}
renderItem={renderItem}
keyExtractor={keyExtractor}
getItemLayout={getItemLayout}
// 性能优化配置
removeClippedSubviews={true}
maxToRenderPerBatch={10}
windowSize={5}
initialNumToRender={10}
// 避免不必要的重渲染
extraData={null}
/>
)
}
const ITEM_HEIGHT = 80
const styles = StyleSheet.create({
itemContainer: {
flexDirection: "row",
padding: 16,
height: ITEM_HEIGHT,
backgroundColor: "#fff",
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: "#eee",
},
itemImage: {
width: 48,
height: 48,
borderRadius: 24,
},
itemContent: {
flex: 1,
marginLeft: 12,
justifyContent: "center",
},
itemTitle: {
fontSize: 16,
fontWeight: "600",
color: "#333",
},
itemSubtitle: {
fontSize: 14,
color: "#666",
marginTop: 4,
},
})
4.3 图片优化
// 邪修秘籍第七式:图片加载优化
import FastImage from "react-native-fast-image"
// 使用 FastImage 替代 Image
;<FastImage
source={{
uri: imageUrl,
priority: FastImage.priority.normal,
cache: FastImage.cacheControl.immutable,
}}
style={styles.image}
resizeMode={FastImage.resizeMode.cover}
/>
// 预加载图片
FastImage.preload([
{ uri: "https://example.com/image1.jpg" },
{ uri: "https://example.com/image2.jpg" },
])
// 清理缓存
FastImage.clearMemoryCache()
FastImage.clearDiskCache()
第五章:原生模块的求生指南
5.1 问题:需要调用原生功能
当产品经理说"这个功能很简单"的时候,你就知道要写原生代码了。
5.2 邪修技巧:能用库就用库
// 邪修秘籍第八式:能不写原生就不写
// 常用原生功能的库(2026年还在维护的)
const essentialLibraries = {
// 相机
camera: "react-native-vision-camera",
// 图片选择
imagePicker: "react-native-image-picker",
// 文件系统
fs: "react-native-fs",
// 设备信息
deviceInfo: "react-native-device-info",
// 权限
permissions: "react-native-permissions",
// 推送通知
push: "@react-native-firebase/messaging",
// 本地存储
storage: "@react-native-async-storage/async-storage",
// 加密存储
secureStorage: "react-native-keychain",
// 网络状态
netInfo: "@react-native-community/netinfo",
// 剪贴板
clipboard: "@react-native-clipboard/clipboard",
// 分享
share: "react-native-share",
// 二维码
qrcode: "react-native-qrcode-scanner",
// 地图
maps: "react-native-maps",
// 定位
geolocation: "react-native-geolocation-service",
// 生物识别
biometrics: "react-native-biometrics",
// WebView
webview: "react-native-webview",
// 视频播放
video: "react-native-video",
// 动画
animation: "react-native-reanimated",
// 手势
gesture: "react-native-gesture-handler",
}
// 安装命令生成器
function generateInstallCommand(libs: string[]) {
const packages = libs.map((lib) => essentialLibraries[lib]).filter(Boolean)
console.log(`npm install ${packages.join(" ")}`)
console.log("\n# iOS 还需要:")
console.log("cd ios && pod install && cd ..")
}
5.3 实在要写原生代码
// 邪修秘籍第九式:最简原生模块模板
// === iOS (MyModule.m) ===
/*
#import <React/RCTBridgeModule.h>
@interface MyModule : NSObject <RCTBridgeModule>
@end
@implementation MyModule
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(doSomething:(NSString *)param
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
@try {
// 你的原生代码
NSString *result = [NSString stringWithFormat:@"Result: %@", param];
resolve(result);
} @catch (NSException *exception) {
reject(@"error", exception.reason, nil);
}
}
@end
*/
// === Android (MyModule.kt) ===
/*
package com.yourapp
import com.facebook.react.bridge.*
class MyModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
override fun getName() = "MyModule"
@ReactMethod
fun doSomething(param: String, promise: Promise) {
try {
val result = "Result: $param"
promise.resolve(result)
} catch (e: Exception) {
promise.reject("error", e.message)
}
}
}
*/
// === JS 调用 ===
import { NativeModules } from "react-native"
const { MyModule } = NativeModules
async function callNative() {
try {
const result = await MyModule.doSomething("test")
console.log(result)
} catch (error) {
console.error(error)
}
}
第六章:调试的野路子
6.1 Console 大法
// 邪修秘籍第十式:调试工具集
// 带颜色的 console(在 Chrome DevTools 中有效)
const log = {
info: (msg: string, ...args: any[]) =>
console.log(`%c[INFO] ${msg}`, "color: #2196F3", ...args),
success: (msg: string, ...args: any[]) =>
console.log(`%c[SUCCESS] ${msg}`, "color: #4CAF50", ...args),
warn: (msg: string, ...args: any[]) =>
console.log(`%c[WARN] ${msg}`, "color: #FF9800", ...args),
error: (msg: string, ...args: any[]) =>
console.log(`%c[ERROR] ${msg}`, "color: #F44336", ...args),
}
// 性能计时
const perf = {
start: (label: string) => console.time(label),
end: (label: string) => console.timeEnd(label),
}
// 打印组件渲染
function useRenderLog(componentName: string) {
const renderCount = React.useRef(0)
renderCount.current++
console.log(`[Render] ${componentName}: ${renderCount.current}`)
}
// 打印 Props 变化
function usePropsLog(props: Record<string, any>, componentName: string) {
const prevProps = React.useRef(props)
React.useEffect(() => {
const changes: string[] = []
Object.keys(props).forEach((key) => {
if (prevProps.current[key] !== props[key]) {
changes.push(key)
}
})
if (changes.length > 0) {
console.log(`[Props Changed] ${componentName}:`, changes)
}
prevProps.current = props
})
}
6.2 临时 UI 调试
// 给任何组件加边框
const debugStyle = __DEV__ ? { borderWidth: 1, borderColor: "red" } : {}
// 调试组件
function DebugView({
children,
label,
}: {
children: ReactNode
label?: string
}) {
if (!__DEV__) return <>{children}</>
return (
<View style={{ borderWidth: 1, borderColor: "red" }}>
{label && (
<Text
style={{
position: "absolute",
top: -10,
left: 4,
backgroundColor: "red",
color: "white",
fontSize: 10,
paddingHorizontal: 4,
}}
>
{label}
</Text>
)}
{children}
</View>
)
}
// 使用
;<DebugView label='Header'>
<Header />
</DebugView>
写在最后:RN 开发的生存法则
- 环境问题先清缓存 —— 90% 的问题都能解决
- 能用库就用库 —— 不要重复造轮子
- 性能优化要趁早 —— 别等卡了再优化
- 原生代码能不写就不写 —— 写了就是坑
- 保持版本更新 —— 但不要第一时间更新
记住:能跑就是胜利。
互动话题
- 你遇到过最离谱的 RN bug 是什么?
- 你有什么 RN 开发的独门秘籍?
- RN vs Flutter,你站哪边?
欢迎在评论区分享你的"邪修"经验!
本文仅供娱乐和学习参考。如因使用本文技巧导致项目爆炸,作者概不负责。