在双对开本中,https://github.com/farshed/duofolio/blob/master/src/screens/EpubReader.js我们创建了一个静态本地服务器来托管,然后使用http获取文件。
let newServer=new StaticServer(0,ExternalStorageDirectoryPath,serverConfig(;
世博会上有类似的东西吗?一些小型本地服务器,我可以";主机";文件在?
摘要
你好,从2023年起,只要你使用开发构建(运行Expo预构建并公开/ios和/android文件夹(,你就可以将库@dr.pogodin/react-native-static-server
与Expo一起使用。该库是主react本地静态服务器的派生,该服务器仍在维护中。
1.开发建设
在您的Managed Expo项目中,运行npx expo prebuild --clean
以基于Expo config(app.json(配置重新生成/ios和/android目录,请在此处查看更多信息。
有关生成开发构建(APK测试而不是使用Expo Go应用程序(的更多信息,请查看Expo的文档。
2.静态服务器库
现在,随着ios和android文件夹的公开,您可以像在普通的React Native应用程序中一样手动编辑。现在您需要遵循@dr.pogodin/react-native-static-server
文档,不要忘记添加";将服务器资产静态绑定到应用程序中";适用于iOS和Android。
3.React Native文件系统
添加库react-native-fs
也是强制性的,因此您必须在/ios和/android文件夹中进行额外配置,并为ios运行pod install
。点击此处查看更多信息。
4.预装吊钩
如果你使用EAS Build构建应用程序,你必须创建一个预安装挂钩来安装CMake。
我就是这样做的:
- 首先向package.json添加一个脚本:
"eas-build-pre-install": "./pre-install",
- 然后将
pre-install
脚本文件添加到根文件夹中:
#!/usr/bin/env bash
# This script will follow the script eas-build-pre-install located in package.json and will be executed before EAS Build runs npm install on the EAS Build platform.
echo "running pre install..."
if [[ "$EAS_BUILD_PLATFORM" == "android" ]]; then
sudo apt-get install --yes cmake
elif [[ "$EAS_BUILD_PLATFORM" == "ios" ]]; then
HOMEBREW_NO_AUTO_UPDATE=1 brew install cmake
fi
这将运行一个额外的步骤,确保CMake在机器内部,然后机器将构建应用程序(在使用EAS build时(。
5.用法
你可以效仿他们,或者创建自己的解决方案。在我的情况下,我创建了一个钩子,这样我就可以从任何其他组件调用并托管多个网站:
import * as React from 'react';
import { Platform } from 'react-native';
import RNFS from 'react-native-fs';
import Server, {
extractBundledAssets,
getActiveServer,
STATES,
} from '@dr.pogodin/react-native-static-server';
import { ServerContextData, ServerProviderProps } from './types';
const ServerContext = React.createContext<ServerContextData>({} as ServerContextData);
const ServerProvider = ({ children }: ServerProviderProps) => {
const serverRef = React.useRef<Server | null>();
const [origin, setOrigin] = React.useState<string>('');
const extractFiles = React.useCallback(async (fileDir: string, gameId: string) => {
if (Platform.OS !== 'android') return;
let extract = true;
try {
const versionD = await RNFS.readFile(`${fileDir}/version`, 'utf8');
const versionA = await RNFS.readFileAssets('webroot/version', 'utf8');
if (versionA === versionD) {
extract = false;
} else {
await RNFS.unlink(fileDir);
}
} catch (error) {
console.log(error, 'extractFiles');
}
if (extract) {
console.log('Extracting web server assets...');
await extractBundledAssets(fileDir, `webroot/${gameId}`);
}
}, []);
const stopServer = React.useCallback(async () => {
if (!serverRef.current) return;
await serverRef.current?.stop();
serverRef.current = null;
setOrigin('');
}, []);
const startServer = React.useCallback(
async (gameId: string) => {
const activeServer = getActiveServer();
console.log('active', activeServer);
console.log('serverRef', serverRef.current?.state, serverRef.current?.id);
if (activeServer || serverRef.current?.state === STATES.ACTIVE) {
await stopServer();
}
const fileDir: string = Platform.select({
android: `${RNFS.DocumentDirectoryPath}/webroot/${gameId}`,
ios: `${RNFS.MainBundlePath}/webroot/${gameId}`,
windows: `${RNFS.MainBundlePath}\webroot/${gameId}`,
default: '',
});
serverRef.current = new Server({ fileDir, stopInBackground: true });
const serverId = serverRef.current.id;
await extractFiles(fileDir, gameId);
serverRef.current?.addStateListener((newState, details, error) => {
console.log(
`Server #${serverId}.n`,
`New state: "${STATES[newState]}".n`,
`Details: "${details}".`
);
if (error) console.error(error);
});
const res = await serverRef.current?.start();
if (res && serverRef.current) {
setOrigin(res);
}
},
[stopServer, extractFiles]
);
const value: ServerContextData = React.useMemo(
() => ({ origin, startServer, stopServer }),
[origin, startServer, stopServer]
);
return <ServerContext.Provider value={value}>{children}</ServerContext.Provider>;
};
const useServer = (): ServerContextData => {
const context = React.useContext(ServerContext);
if (!context) {
throw new Error('useServer must be within a ServerProvider');
}
return context;
};
export { ServerProvider, useServer };
然后你导入ServerProvider
并将其包装在你的应用程序中:
<ServerProvider> {children} </ServerProvider>
要使用钩子,只需从具有Web视图的组件中调用即可加载内容:
import { useServer } from 'hooks';
import * as React from 'react';
import { SafeAreaView, View } from 'react-native';
import { WebView } from 'react-native-webview';
import { styles } from './styles';
export const TestScreen = () => {
const { origin, startServer, stopServer } = useServer();
const webView = React.useRef<WebView>(null);
React.useEffect(() => {
startServer('test');
return () => {
stopServer();
};
}, []);
return (
<SafeAreaView>
<View style={styles.webview}>
{origin && <WebView ref={webView} originWhitelist={['*']} source={{ uri: origin }} />}
</View>
</SafeAreaView>
);
};
我想就是这样。对我来说,服务器运行得很好。请记住,一次只能加载一个服务器实例。
请不要说,在我的情况下,我在webroot
文件夹中有子文件夹,每个文件夹有一个站点,所以我可以根据我传递给startServer
函数的游戏ID加载它们。