我们有类似的要求,即应用需要为多个租户页面提供服务,但每个租户的数据都不同。这意味着应用在构建时无法访问数据,只能在运行时访问。
我们利用getStaticPaths 和getStaticProps 来做到这一点。
此方法返回一个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 变量。 (根据您的具体用例,您可以选择使用或不使用它)
此方法根据租户的 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,
};
}
我们还利用Incremental Static Regeneration,确保我们每revalidate 秒刷新一次页面。
页面
单模板租户
当所有租户共享相同的组件时,这非常简单:
const SingleTenantPage = ({ data }) => {
return <Component {...data} />;
};
export default SingleTenantPage;
多模板租户
实际的页面,只是解析props(从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 值,因为数据不会经常更改。由于所有动态导入,我们唯一需要注意的是有点复杂测试用例。
上述解决方案与您考虑的用例非常相似,尽管没有额外的环境变量 (IS_BUILD)