前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >改变渲染顺序实现按需加载,主要可以从以下几个方面入手:

改变渲染顺序实现按需加载,主要可以从以下几个方面入手:

原创
作者头像
用户1232103
修改2024-12-16 07:41:26
修改2024-12-16 07:41:26
920
举报

当你访问http://localhost:3000/app/list/testpage时,Next.js 的处理顺序是:

代码语言:ts
复制
1. 首先加载 /pages/_app.tsx(这是应用的入口文件,所有页面都会通过它包装)
   ↓
2. 加载 Layout 组件(如果有全局布局的话)
   ↓
3. 最后才加载 /pages/app/list/testpage.tsx

虽然你的 testpage.tsx看起来很简单:

但实际上它被_app.tsx包装了。让我们看看_app.tsx:

然后,应用的渲染顺序是:

代码语言:ts
复制
App (_app.tsx)
  ↓
QueryClientContext
  ↓
I18nContextProvider (初始化i18n)
  ↓
ChakraUIContext
  ↓
Layout (这里调用了setUserDefaultLng)
  ↓
TestPage (你的测试页面)
代码语言:ts
复制
Next.js 的渲染流程和相关文件如下:

入口点:
pages/_document.tsx:自定义 HTML 文档结构
pages/_app.tsx:应用的根组件,所有页面都会通过这里
next.config.js:Next.js 的配置文件,控制构建和运行时行为
页面加载顺序:
_document.tsx (如果存在)
  ↓
_app.tsx
  ↓
pages/_test/testpage.tsx
涉及的 JS 文件及其作用:
polyfills.js: 浏览器兼容性支持,来自 Next.js 核心
webpack.js: Webpack 运行时,由 Next.js 注入
main.js: Next.js 的主运行时
_app.js: 来自你的 _app.tsx 文件
testpage.js: 你的测试页面
_buildManifest.js: Next.js 的构建清单
_ssgManifest.js: 静态生成的清单

代码语言:ts
复制
访问 http://localhost:3000/_test/testpage 的渲染流程:

入口文件:
/app/src/pages/_document.tsx - 基础 HTML 结构
/app/src/pages/_app.tsx - 应用入口
/app/src/pages/_test/testpage.tsx - 页面组件
构建输出:
.next/static/chunks/pages/_app.js - 客户端 app 代码
.next/server/pages/_app.js - 服务器端 app 代码
.next/server/pages/_document.js - 服务器端文档代码

所以虽然你的测试页面很简单,但它被包裹在了多层组件中:

  • _app.tsx 提供了基础框架
  • Layout 组件提供了布局
  • 各种 Context 提供了全局状态和功能

i18n 相关的错误出现是因为:

  • _app.tsx 中初始化了 i18n
  • Layout 组件尝试使用 i18n 功能
  • 但 i18n 可能没有正确初始化完成就被使用了

这就是为什么即使你的测试页面很简单,也会遇到i18n 相关的错误。因为整个应用都被包装在了这些功能组件中。

按需加载:

修改_app.tsx,移除全局包装,改为按需加载:

代码语言:ts
复制
import type { AppProps } from 'next/app';
import dynamic from 'next/dynamic';

// 定义需要全局功能的路由
const needGlobalFeatures = ['/app', '/chat', '/login'];

// 动态导入全局组件
const GlobalWrapper = dynamic(() => import('@/components/GlobalWrapper'), {
  ssr: false
});

// 动态导入Layout
const Layout = dynamic(() => import('@/components/Layout'), {
  ssr: false
});

function App({ Component, pageProps, router }: AppProps) {
  // 判断当前路由是否需要全局功能
  const needsGlobal = needGlobalFeatures.some(path => 
    router.pathname.startsWith(path)
  );

  // 基础渲染
  const content = <Component {...pageProps} />;

  // 按需包装
  if (needsGlobal) {
    return (
      <GlobalWrapper>
        <Layout>{content}</Layout>
      </GlobalWrapper>
    );
  }

  // 简单页面直接返回
  return content;
}

export default App;

这部分我再详细一点:

代码语言:ts
复制
// 动态导入示例
const Layout = dynamic(() => import('@/components/Layout'), {
  ssr: false  // 禁用服务端渲染
});

// 判断逻辑示例
function App({ Component, pageProps, router }: AppProps) {
  // 这里的判断逻辑需要您根据实际需求来写
  const needsLayout = router.pathname !== '/app/list/testpage';
  const needsI18n = router.pathname.startsWith('/app/');
  
  // 基础内容
  let content = <Component {...pageProps} />;
  
  // 按需包装组件
  if (needsLayout) {
    content = <Layout>{content}</Layout>;
  }
  
  if (needsI18n) {
    content = <I18nWrapper>{content}</I18nWrapper>;
  }
  
  return content;
}

具体的判断条件,我举个例子

代码语言:ts
复制
// 例如:哪些页面需要什么功能
const featureRules = {
  layout: {
    include: ['/app', '/chat'],  // 需要布局的路径
    exclude: ['/app/list/testpage']  // 不需要布局的路径
  },
  i18n: {
    include: ['/app/list'],
    exclude: ['/app/list/testpage']
  }
};

判断函数

代码语言:ts
复制
function needsFeature(pathname: string, feature: 'layout' | 'i18n') {
  const rules = featureRules[feature];
  
  // 如果在排除列表中,直接返回false
  if (rules.exclude.some(path => pathname.startsWith(path))) {
    return false;
  }
  
  // 检查是否在包含列表中
  return rules.include.some(path => pathname.startsWith(path));
}

动态加载逻辑

代码语言:ts
复制
function App({ Component, pageProps, router }: AppProps) {
  // 使用您定义的判断逻辑
  const shouldLoadLayout = needsFeature(router.pathname, 'layout');
  const shouldLoadI18n = needsFeature(router.pathname, 'i18n');

  let content = <Component {...pageProps} />;

  // 根据判断结果动态加载
  if (shouldLoadLayout) {
    content = <Layout>{content}</Layout>;
  }

  if (shouldLoadI18n) {
    content = <I18nWrapper>{content}</I18nWrapper>;
  }

  return content;
}

这样,您可以:

1.自定义哪些路径需要哪些功能

2. 定义功能加载的优先级和顺序

  • 根据实际需求调整判断逻辑

这只是一个框架,具体的判断规则需要您根据项目需求来实现。

Completed(4 files changed)

创建一个轻量级的全局包装组件GlobalWrapper.tsx

代码语言:ts
复制
import { ReactNode } from 'react';
import { QueryClient, QueryClientProvider } from 'react-query';
import { appWithTranslation } from 'next-i18next';
import I18nContextProvider from '@/web/context/I18n';

interface Props {
  children: ReactNode;
}

function GlobalWrapper({ children }: Props) {
  const queryClient = new QueryClient();

  return (
    <QueryClientProvider client={queryClient}>
      <I18nContextProvider>
        {children}
      </I18nContextProvider>
    </QueryClientProvider>
  );
}

export default appWithTranslation(GlobalWrapper);

在具体页面中按需导入功能:

代码语言:ts
复制
import { useEffect, useState } from 'react';
import dynamic from 'next/dynamic';

// 按需导入i18n
const I18nFeature = dynamic(() => import('@/components/features/I18n'), {
  ssr: false
});

export default function TestPage() {
  const [needsI18n, setNeedsI18n] = useState(false);

  useEffect(() => {
    // 根据实际需求决定是否加载i18n
    setNeedsI18n(true);
  }, []);

  return (
    <div>
      {needsI18n ? (
        <I18nFeature>
          <h1>Test Page with i18n</h1>
        </I18nFeature>
      ) : (
        <h1>Simple Test Page</h1>
      )}
    </div>
  );
}

使用路由配置来控制功能加载:

代码语言:ts
复制
// routes.config.ts
export const routeConfig = {
  '/app/list/testpage': {
    needsI18n: false,
    needsLayout: false,
    needsAuth: false
  },
  '/app/list': {
    needsI18n: true,
    needsLayout: true,
    needsAuth: true
  }
  // ... 其他路由配置
};

创建自定义Hook 来管理功能加载:

代码语言:ts
复制
// useFeatures.ts
import { useRouter } from 'next/router';
import { routeConfig } from './routes.config';

export function useFeatures() {
  const router = useRouter();
  const config = routeConfig[router.pathname] || {};

  return {
    needsI18n: config.needsI18n ?? false,
    needsLayout: config.needsLayout ?? false,
    needsAuth: config.needsAuth ?? false
  };
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档