将 Next.js 与 Expo for Web 结合使用
将 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 with swc. (Recommended)
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'],
};
};
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,
},
};
Next.js with Babel. (Not recommended)
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
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',
'react-native-web',
'expo',
// Add more React Native/Expo packages here...
],
});
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',
'react-native-web',
'expo',
// Add more React Native/Expo packages here...
],
experimental: {
forceSwcTransforms: true,
},
});
module.exports = nextConfig;
React Native Web styling
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
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`.
'react-native',
'react-native-web',
'expo',
// Add more React Native/Expo packages here...
],
},
});
Deploy to Vercel
This is Vercel's preferred method for deploying Next.js projects to production.
1
Add a build
script to your package.json:
{
"scripts": {
"build": "next build"
}
}
2
Install the Vercel CLI:
-
npm i -g vercel
3
Deploy to Vercel:
-
vercel
与默认 Web 版 Expo 相比的限制或差异
¥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
不能在模块外部使用 import 语句
¥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',
'react-native-web',
'expo',
// Add the failing package here, and restart the server...
],
},
});