在开发前端项目时,通常会有一个 src 目录,项目开发中自己写的页面,组件,各种封装全部放在src目录下。统一放在一个目录里,既是一种习惯,也方便大家的管理。
其实使用 Expo 新建的 React Native 项目,也默认支持 src 目录。
// 使用template blank参数,创建一个空白的React Naitve项目
// 项目名字输入:dk-bms-app
npx create-expo-app@latest --template blank
// 如果需要创建TypeScript项目,可以使用
npx create-expo-app@latest --template blank-typescript
新建 src 目录
在根目录新建一个 src 目录,然后将 app、assets、components、hooks、utils 等目录移动到 src 目录中。
按 ctrl + c 将服务停止,运行 npx expo start 重启服务。
修改 app.json
刷新项目时,在终端会提示找不到图片,需要将原 ./assets/ 路径修改为 ./src/assets。
npm i -D prettier
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"singleQuote": true,
"printWidth": 100
}
{
"scripts": {
//...
"web": "expo start --web",
"format": "prettier --write \"**/*.{js,json,md,ts,jsx,tsx}\""
},
}
很多时候项目层级很深,就要一层层的写../,非常容易出错,而且代码可读性也很差。
配置@来代替src目录,需要创建个Babel配置文件,Expo 官方文档里已经给出了命令,运行:
npx expo customize babel.config.js
npm i -D babel-plugin-module-resolver
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
[
'module-resolver',
{
root: ['./src'],
alias: {
'@': './src',
},
},
],
],
};
};
// 重启服务并清除缓存
npx expo start --clear
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"exclude": ["node_modules", "**/node_modules/*"]
}
npm i expo@latest
npx expo install --fix
这个包本来不装也没事,项目里会通过其他包的依赖项自动安装。但是在新版本里,让它自动安装会出现问题。
在 package.json 里配置 react-dom 和 react 版本号一致,为 19.1.0。
{
"dependencies": {
"react": "19.1.0",
"react-dom": "19.1.0",
},
}
执行依赖安装命令:
npm i
npx expo-doctor
import { useState } from 'react';
import { RefreshControl, ScrollView } from 'react-native';
export default function Index() {
const [refreshing, serRefreshing] = useState(false);
const onRefresh = () => {
setRefreshing(true);
// 模拟重新请求接口
console.log('刷新发起请求了');
setTimeout(() => {
setRefreshing(false);
}, 2000);
};
return (
<ScrollView
style={styles.container}
contentContainerStyle={styles.contentContainer}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={'#1f99b0'} />
}
>
<View>内容</View>
</ScrollView>
);
}
import { useState } from 'react';
export default function Index() {
const [page, setPage] = useState(1);
/**
* 加载更多
*/
const onEndReached = async () => {
// 页面数 + 1
const nextPage = page + 1;
setPage(nextPage);
// console.log(nextPage)
// 请求接口
// console.log(data)
// 将新数据追加到现有列表中
setData((prevData) => ({
articles: [...prevData.articles, ...data.articles],
}));
};
return (
<FlatList
style={styles.container}
contentContainerStyle={styles.contentContainer}
data={articles}
keyExtractor={(item) => item.id.toString()}
renderItem={renderItem}
ItemSeparatorComponent={renderSeparator}
ListEmptyComponent={<NoData />}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={'#1f99b0'} />
}
onEndReached={onEndReached}
onEndReachedThreshold={0.1}
/>
);
}
这种方式会跳出当前APP去打开浏览器。
这种方法,除了打开网页,有时还会用来打开深度链接地址,跳转到其他 App 上。
npx expo install expo-linking
import * as Linking from 'expo-linking';
<Cell
title="常用站点"
// 使用 Linking
onPress={() => {
Linking.openURL('https://lgk.pub');
}}
/>;
用这种方式,依然还是在当前 App 里,并没有跳到单独的浏览器中。
在iOS上:页面是从底部弹出的。左上角有一个勾,点击后会关闭页面了。
在Android上:页面是左右切换打开的。点击左上角的叉后,就跳转回来了。
npx expo install expo-web-browser
import * as WebBrowser from 'expo-web-browser';
// 使用 WebBrowser
<Cell
title="常用站点"
//...
// 使用 WebBrowser
onPress={async () => {
await WebBrowser.openBrowserAsync('https://lgk.pub');
}}
/>;
import { useState } from 'react';
import { View, StyleSheet, Text } from 'react-native';
import { WebView } from 'react-native-webview';
export const CustomWebView = ({ url }) => {
const [webViewHeight, setWebViewHeight] = useState(0);
const [hasError, setHasError] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const onMessage = (event) => {
const height = parseFloat(event.nativeEvent.data);
if (!isNaN(height) && height > 0) {
setWebViewHeight(height);
}
};
const onError = () => {
setHasError(true);
setIsLoading(false);
};
const onLoadEnd = () => {
setIsLoading(false);
};
const injectedJavaScript = `
window.addEventListener('load', function() {
const height = document.documentElement.scrollHeight;
window.ReactNativeWebView.postMessage(String(height));
});
true;
`;
return (
<View style={styles.container}>
{isLoading && !hasError && (
<View style={{ width: '100%', height: '100%', marginTop: '20%', alignItems: 'center' }}>
<Text style={{ color: '#333', fontSize: 16, textAlign: 'center' }}>加载中...</Text>
</View>
)}
{hasError ? (
<View style={{ width: '100%', height: '100%', marginTop: '10%', alignItems: 'center' }}>
<View style={{ width: '100%', padding: 20 }}>
<Text style={{ color: '#333', fontSize: 16, fontWeight: '700', textAlign: 'center' }}>
无法连接到网页
</Text>
<Text style={{ color: '#333', fontSize: 16, paddingVertical: 10 }}>请确保:</Text>
<Text style={{ color: '#333', fontSize: 16, marginLeft: 10 }}>1. URL地址正确</Text>
<Text style={{ color: '#333', fontSize: 16, marginLeft: 10 }}>
2. 网页服务器正在运行
</Text>
<Text style={{ color: '#333', fontSize: 16, marginLeft: 10 }}>
3. URL链接可访问: {url}
</Text>
</View>
</View>
) : (
<WebView
source={{ uri: url }}
style={{ height: webViewHeight || 300 }}
onMessage={onMessage}
onError={onError}
onLoadEnd={onLoadEnd}
injectedJavaScript={injectedJavaScript}
scrollEnabled={false}
javaScriptEnabled={true}
domStorageEnabled={true}
originWhitelist={['*']}
/>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
});


npm install -g eas-cli
expo login
eas build:configure
eas build --platform android