将 Next.js 与 Expo 一起用于 Web
编辑页面
将 Next.js 与 Expo 集成用于 Web 的指南。
For the complete documentation index, see llms.txt. Use this file to discover all available pages.
警告 使用 Next.js 并不是 Expo 通用应用开发工作流的官方组成部分。
Next.js 是一个 React 框架,提供了简单的基于页面的路由以及服务端渲染。要将 Next.js 与 Expo SDK 一起使用,我们建议使用 @expo/next-adapter 库来处理配置。
将 Expo 与 Next.js 结合使用意味着你可以在移动端和 Web 应用之间共享你现有的一些组件和 API。Next.js 有自己的 CLI,你在开发 Web 平台时需要使用它,所以你需要使用 Next.js CLI 启动 Web 项目,而不是使用 npx expo start。
Next.js 只能与 Expo 的 Web 端一起使用,因为原生应用不支持服务端渲染(SSR)。
自动设置
要快速开始,请使用 with-nextjs 模板创建一个新项目:
- npx create-expo-app -e with-nextjs- 原生:
npx expo start— 启动 Expo 项目 - Web:
npx next dev— 启动 Next.js 项目
手动设置
安装依赖
确保你的项目中已安装 expo、next、@expo/next-adapter:
- yarn add expo next @expo/next-adapter转译
配置 Next.js 以转换语言特性:
带 swc 的 Next.js。(推荐)
推荐使用带 SWC 的 Next.js。你可以配置 babel.config.js 只针对原生端:
module.exports = function (api) { api.cache(true); return { presets: ['babel-preset-expo'], }; };
你还需要通过在 next.config.js 中添加以下内容来强制 Next.js 使用 SWC:
module.exports = { experimental: { forceSwcTransforms: true, }, };
带 Babel 的 Next.js。(不推荐)
调整你的 babel.config.js,在使用 webpack 为 Web 打包时有条件地添加 next/babel:
module.exports = function (api) { // 检测 Web 的使用情况(如果 Next.js 更改了加载器,这一点将来可能会变化) const isWeb = api.caller( caller => caller && (caller.name === 'babel-loader' || caller.name === 'next-babel-turbo-loader') ); return { presets: [ // 仅在浏览器中使用 next,它会破坏你的原生项目 isWeb && require('next/babel'), 'babel-preset-expo', ].filter(Boolean), }; };
Next.js 配置
将以下内容添加到你的 next.config.js:
const { withExpo } = require('@expo/next-adapter'); module.exports = withExpo({ // transpilePackages 是 Next.js +13.1 的功能。 // 较旧版本可以使用 next-transpile-modules transpilePackages: [ 'react-native', 'react-native-web', 'expo', // 在这里添加更多 React Native/Expo 包... ], });
完整的 Next.js 配置可能如下所示:
const { withExpo } = require('@expo/next-adapter'); /** @type {import('next').NextConfig} */ const nextConfig = withExpo({ reactStrictMode: true, swcMinify: true, transpilePackages: [ 'react-native', 'react-native-web', 'expo', // 在这里添加更多 React Native/Expo 包... ], experimental: { forceSwcTransforms: true, }, }); module.exports = nextConfig;
React Native Web 样式
react-native-web 包建立在重置 CSS 样式的假设之上。以下是在 Next.js 中使用 pages 目录重置样式的方法。
import { Children } from 'react'; import Document, { Html, Head, Main, NextScript } from 'next/document'; import { AppRegistry } from 'react-native'; // 遵循 react-native-web 的设置: // https://necolas.github.io/react-native-web/docs/setup/#root-element // 另外还为各种浏览器添加了额外的 React Native 滚动和文本一致性样式。 // 强制 Next 生成的 DOM 元素填满其父元素的高度 const style = ` html, body, #__next { -webkit-overflow-scrolling: touch; } #__next { display: flex; flex-direction: column; height: 100%; } html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; } body { /* 允许你滚动到视口下方;默认值是 visible */ overflow-y: auto; overscroll-behavior-y: none; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; -ms-overflow-style: scrollbar; } `; export default class MyDocument extends Document { static async getInitialProps({ renderPage }) { AppRegistry.registerComponent('main', () => Main); const { getStyleElement } = AppRegistry.getApplication('main'); const page = await renderPage(); const styles = [ <style key="react-native-style" dangerouslySetInnerHTML={{ __html: style }} />, getStyleElement(), ]; return { ...page, styles: Children.toArray(styles) }; } render() { return ( <Html style={{ height: '100%' }}> <Head /> <body style={{ height: '100%', overflow: 'hidden' }}> <Main /> <NextScript /> </body> </Html> ); } }
import Head from 'next/head'; export default function App({ Component, pageProps }) { return ( <> <Head> <meta name="viewport" content="width=device-width, initial-scale=1" /> </Head> <Component {...pageProps} /> </> ); }
模块转译
默认情况下,React Native 生态系统中的模块不会被转译以在 Web 浏览器中运行。React Native 依赖 Metro 中的高级缓存来快速重新加载。Next.js 使用 webpack,它没有相同级别的缓存,因此默认不会转译任何 node 模块。你需要在 next.config.js 中使用 transpilePackages 选项手动标记每个你想要转译的模块:
const { withExpo } = require('@expo/next-adapter'); module.exports = withExpo({ experimental: { transpilePackages: [ // 注意:即使 `react-native` 从未在 Next.js 中使用, // 你也需要列出 `react-native`,因为 `react-native-web` // 被映射到了 `react-native`。 'react-native', 'react-native-web', 'expo', // 在这里添加更多 React Native/Expo 包... ], }, });
部署到 Vercel
这是 Vercel 推荐的将 Next.js 项目部署到生产环境的方法。
1
将 build 脚本添加到你的 package.json:
{ "scripts": { "build": "next build" } }
2
安装 Vercel CLI:
- npm i -g vercel3
部署到 Vercel:
- vercel与默认的 Expo for Web 相比的限制或差异
在 Web 端使用 Next.js 意味着你将使用 Next.js 的 webpack 配置进行打包。这会导致你的应用与网站在开发方式上存在一些核心差异。
- Expo Next.js 适配器不支持实验性的 app 目录。
- 对于原生端的基于文件的路由,我们建议使用 Expo Router。
贡献
如果你想帮助改进 Expo 对 Next.js 的支持,欢迎提交 PR 或 issue:
故障排查
无法在模块外部使用 import 语句
找出哪个模块包含 import 语句,并将其添加到 next.config.js 中的 transpilePackages 选项:
const { withExpo } = require('@expo/next-adapter'); module.exports = withExpo({ experimental: { transpilePackages: [ 'react-native', 'react-native-web', 'expo', // 在这里添加失败的包,并重启服务器... ], }, });