将 Next.js 与 Expo 网页版集成的指南。
使用 Next.js 并不是 Expo 通用应用开发工作流程的官方部分。
Next.js 是一个 React 框架,提供简单的基于页面的路由以及服务器端渲染。要将 Next.js 与 Expo SDK 结合使用,我们建议使用 @expo/next-adapter
库来处理配置。
¥Next.js is a React framework that provides simple page-based routing as well as server-side rendering. To use Next.js with the Expo SDK, we recommend using @expo/next-adapter
library to handle the configuration.
将 Expo 与 Next.js 结合使用意味着你可以在移动和 Web 应用中共享一些现有组件和 API。Next.js 有自己的 CLI,你在为 Web 平台进行开发时需要使用它,因此你需要使用 Next.js CLI 而不是使用 npx expo start
来启动你的 Web 项目。
¥Using Expo with Next.js means you can share some of your existing components and APIs across your mobile and web app. Next.js has its own CLI that you'll need to use when developing for the web platform, so you'll need to start your web projects with the Next.js CLI and not with npx expo start
.
Next.js 只能与 Expo for Web 一起使用,因为不支持原生应用的服务器端渲染 (SSR)。
¥Next.js can only be used with Expo for web as there is no support for Server-Side Rendering (SSR) for native apps.
¥Automatic setup
要快速开始,请使用 with-nextjs 模板创建一个新项目:
¥To quickly get started, create a new project using with-nextjs template:
-
npx create-expo-app -e with-nextjs
本国的:npx expo start
- 启动 Expo 项目
¥Native: npx expo start
— start the Expo project
网址:npx next dev
— 启动 Next.js 项目
¥Web: npx next dev
— start the Next.js project
¥Manual setup
¥Install dependencies
确保你的项目中安装了 expo
、next
、@expo/next-adapter
:
¥Ensure you have expo
, next
, @expo/next-adapter
installed in your project:
-
yarn add expo next @expo/next-adapter
¥Transpilation
配置 Next.js 来转换语言特性:
¥Configure Next.js to transform language features:
建议将 Next.js 与 SWC 结合使用。你可以将 babel.config.js 配置为仅考虑原生:
¥Using Next.js with SWC is recommended. You can configure the babel.config.js to only account for native:
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
你还必须通过将以下内容添加到 next.config.js 来进行 强制 Next.js 使用 SWC:
¥You will also have to force Next.js to use SWC by adding the following to your next.config.js:
module.exports = {
experimental: {
forceSwcTransforms: true,
},
};
调整 babel.config.js 以在与 webpack 打包时有条件地添加 next/babel
:
¥Adjust your babel.config.js to conditionally add next/babel
when bundling with webpack for web:
module.exports = function (api) {
// Detect web usage (this may change in the future if Next.js changes the loader)
const isWeb = api.caller(
caller =>
caller && (caller.name === 'babel-loader' || caller.name === 'next-babel-turbo-loader')
);
return {
presets: [
// Only use next in the browser, it'll break your native project
isWeb && require('next/babel'),
'babel-preset-expo',
].filter(Boolean),
};
};
¥Next.js configuration
将以下内容添加到你的 next.config.js 中:
¥Add the following to your next.config.js:
const { withExpo } = require('@expo/next-adapter');
module.exports = withExpo({
// transpilePackages is a Next.js +13.1 feature.
// older versions can use next-transpile-modules
transpilePackages: [
'react-native',
'expo',
// Add more React Native/Expo packages here...
],
});
完全合格的 Next.js 配置可能如下所示:
¥The fully qualified Next.js config may look like:
const { withExpo } = require('@expo/next-adapter');
/** @type {import('next').NextConfig} */
const nextConfig = withExpo({
reactStrictMode: true,
swcMinify: true,
transpilePackages: [
'react-native',
'expo',
// Add more React Native/Expo packages here...
],
experimental: {
forceSwcTransforms: true,
},
});
module.exports = nextConfig;
¥React Native Web styling
react-native-web
包建立在重置 CSS 样式的假设之上。以下是如何使用页面目录重置 Next.js 中的样式。
¥The package react-native-web
builds on the assumption of reset CSS styles. Here's how you reset styles in Next.js using the pages directory.
import { Children } from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { AppRegistry } from 'react-native';
// Follows the setup for react-native-web:
// https://necolas.github.io/react-native-web/docs/setup/#root-element
// Plus additional React Native scroll and text parity styles for various
// browsers.
// Force Next-generated DOM elements to fill their parent's height
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 {
/* Allows you to scroll below the viewport; default value is 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} />
</>
);
}
¥Transpiling modules
默认情况下,React Native 生态系统中的模块不会转译为在 Web 浏览器中运行。React Native 依赖 Metro 中的高级缓存来快速重新加载。Next.js 使用 webpack,它没有相同级别的缓存,因此默认情况下不会转译任何节点模块。你必须使用 next.config.js 中的 transpilePackages
选项手动标记要转译的每个模块:
¥By default, modules in the React Native ecosystem are not transpiled to run in web browsers. React Native relies on advanced caching in Metro to reload quickly. Next.js uses webpack, which does not have the same level of caching, so no node modules are transpiled by default. You will have to manually mark every module you want to transpile with the transpilePackages
option in next.config.js:
const { withExpo } = require('@expo/next-adapter');
module.exports = withExpo({
experimental: {
transpilePackages: [
// NOTE: Even though `react-native` is never used in Next.js,
// you need to list `react-native` because `react-native-web`
// is aliased to `react-native`. Adding `react-native-web` will not work.
'react-native',
'expo',
// Add more React Native/Expo packages here...
],
},
});
¥Deploy to Vercel
这是 Vercel 将 Next.js 项目部署到生产环境的首选方法。
¥This is Vercel's preferred method for deploying Next.js projects to production.
1
将 build
脚本添加到你的 package.json:
¥Add a build
script to your package.json:
{
"scripts": {
"build": "next build"
}
}
2
安装 Vercel CLI:
¥Install the Vercel CLI:
-
npm i -g vercel
3
部署到 Vercel:
¥Deploy to Vercel:
-
vercel
¥Limitations or differences compared to the default Expo for Web
在 Web 中使用 Next.js 意味着你将与 Next.js webpack 配置打包在一起。这将导致你开发应用与网站的方式存在一些核心差异。
¥Using Next.js for the web means you will be bundling with the Next.js webpack config. This will lead to some core differences in how you develop your app vs your website.
Expo Next.js 适配器不支持实验应用目录。
¥Expo Next.js adapter does not support the experimental app directory.
对于原生上基于文件的路由,我们建议使用 Expo 路由。
¥For file-based routing on native, we recommend using Expo Router.
¥Contributing
如果你想帮助改善 Expo 中的 Next.js 支持,请随时打开 PR 或提交问题:
¥If you would like to help make Next.js support in Expo better, feel free to open a PR or submit an issue:
¥Troubleshooting
¥Cannot use import statement outside a module
找出哪个模块有 import 语句并将其添加到 next.config.js 中的 transpilePackages
选项中:
¥Figure out which module has the import statement and add it to the transpilePackages
option in next.config.js:
const { withExpo } = require('@expo/next-adapter');
module.exports = withExpo({
experimental: {
transpilePackages: [
'react-native',
'expo',
// Add the failing package here, and restart the server...
],
},
});