#javascript #reactjs #typescript #next.js
Вопрос:
Я хочу использовать одно и то же приложение для разных клиентов, где для каждого есть своя база данных (которая может быть локальной). В результате на этапе сборки у меня нет данных (например, в CI/CD), которые я мог бы использовать для создания статических сайтов.
Я подумал о том, чтобы пропустить создание сайтов с getStaticProps
помощью переменной среды. При создании сайтов я мог бы сказать Next.js чтобы не использовать какие-либо данные, что-то вроде этого:
export const getStaticProps: GetStaticProps<HomePageProps> = async () => {
const isBuildPhase = process.env.IS_BUILD;
const data = isBuildPhase ? null : await fetchData();
return {
props: {
data: data ?? null,
},
revalidate: 5 * 60,
};
};
Теперь я хочу создавать сайты только во время выполнения (с next start
), потому что во время выполнения встроенное приложение имеет доступ к своей базе данных. В каждой getStaticProps
переменной среды будет настроена так, чтобы данные извлекались. Когда приложение запускается изначально, оно будет генерировать все статические сайты при доступе к ним.
Есть ли большие недостатки у такого подхода?
Есть ли, возможно, лучшие решения этой проблемы?
Комментарии:
1. В качестве альтернативы вы рассматривали возможность использования
getServerSideProps
вместо этого?2. @juliomalves Конечно, я использую рендеринг на стороне сервера в некоторых частях приложения, но наличие статических сайтов, на мой взгляд, намного лучше для пользователя.
Ответ №1:
У нас было аналогичное требование, когда приложение должно было обслуживать несколько страниц арендаторов, но данные каждого арендатора разные. Это означало, что приложение не имело доступа к данным во время сборки и только во время выполнения.
Мы использовали getStaticPaths
и getStaticProps
для этого.
getStaticPaths
Этот метод возвращает fallback
параметр (true || false || blocking)
, который мы можем использовать, чтобы решить, показывать загрузчик в UX или блокировать, пока не загрузится фактическая страница.
export async function getStaticPaths() {
// Return empty paths because we don't want to generate anything on build
// { fallback: blocking } will server-render pages
// on-demand if the path doesn't exist.
return {
paths: [],
fallback: 'blocking',
};
}
При использовании этого метода нет необходимости поддерживать IS_BUILD
переменную env. (В зависимости от вашего конкретного варианта использования вы можете использовать его или нет)
getStaticProps
Этот метод выполняет фактическую выборку данных на основе параметров пути URL-адреса для клиента.
export async function getStaticProps({ params }) {
// Run your data fetching code here
const data = await fetch(params);
return {
props: data,
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
notFound: !data,
};
}
Мы также используем Инкрементную статическую регенерацию, чтобы убедиться, что мы обновляем страницу каждую revalidate
секунду.
Страницы
Арендаторы с одним Шаблоном
Когда все арендаторы используют одни и те же компоненты, это довольно просто:
const SingleTenantPage = ({ data }) => {
return <Component {...data} />;
};
export default SingleTenantPage;
Арендаторы с несколькими шаблонами
Фактическая страница просто анализирует реквизиты (переданные с getStaticProps
), а затем загружает соответствующий компонент для этой страницы.
Таким образом, мы используем единый маршрут для нескольких арендаторов. example.com/app/tenantA
, example.com/app/tenantB
, example.com/app/tenantC
все 3 маршрута могут быть обслужены вне pages/app/[slug]/index.js
.
Используя dynamic
импорт, мы следим за тем, чтобы загружался только компонент для конкретного клиента. (также помогает в разделении кода)
// Dynamic Import so we load only the required bundles
const templates = {
tenantA: dynamic(() => import(`../templates/tenantA`)),
tenantB: dynamic(() => import(`../templates/tenantB`)),
tenantC: dynamic(() => import(`../templates/tenantC`)),
};
const MultiTenantPage = ({ data }) => {
// Provided template is present in the data
// template: 'tenantA' || 'tenantB' || 'tenantC'
const { template, ...rest } = data || {};
// If the template doesn't exist, show a 404 Page instead
const Component = templates[template] || (() => <Error statusCode="404" />);
return <Component {...rest} />;
};
export default MultiTenantPage;
Страница с несколькими арендаторами хорошо работает для нас. Мы используем большее revalidate
значение, потому что данные меняются не так часто. Единственное предостережение, которое у нас было, — это немного сложные тестовые случаи из-за всего динамического импорта.
Приведенное выше решение очень похоже на то, что вы рассмотрели для своего варианта использования, хотя и без дополнительной переменной env ( IS_BUILD
)
Комментарии:
1. Это такой хороший ответ, спасибо! Один вопрос: я думал
getStaticPaths
, что это работает только для динамических страниц (например[someId].tsx
). Как вы используете его для нединамических (напримерindex.tsx
)? В моем случае эти сайты также могут отображать данные о конкретных клиентах.2. Вы правы.
getStaticPaths
работает только для динамических страниц, потому что он должен возвращать путь. Это сделано специально. (ИМО больше похоже на ограничение). Но есть обходной путь: используя динамическую страницу[index].tsx
, возвращаясь[]
getStaticPaths
, добавьте регистрацию вgetStaticProps
соответствии с маршрутомif (params.path !== '/customer')
и проверьте путь. Вы все равно можете сохранить один путь для своей страницы и вернуть 404 для пути, который не соответствует вашему оператору if.3. Не могли бы вы подробнее остановиться на этом? Как бы можно было создать страницу индекса таким образом? Вот пример с двумя ссылками, одна на
/test
и одна на/test/1
—/test/1
работает только опция: stackblitz.com/edit/nextjs-pwjzop?file=pages/index.js4. Используйте маршрут «Поймать всех «.
[[...index]].js
и вы можете посетить оба/test
и/test/1
использовать один файл.5. Я думаю, что ваше предложение технически работает, но я не буду его использовать. Это просто кажется слишком банальным и, вероятно, приносит больше вреда, чем пользы. Например, когда вы изначально хотите посетить такой статичный сайт, вы не перенаправляетесь (нажмите на одну из кнопок): stackblitz.com/edit/nextjs-yn8leh?file=pages/index.js Я думаю, мне придется подождать, пока Next.js решив интегрировать такую функцию напрямую, я создал запрос на функцию: github.com/vercel/next.js/discussions/28959 Ты все равно считаешь, что я должен принять твой ответ?