Day22 - 错误捕捉、全域 CSS、共用 Layout,就用 _app.tsx 来搞定吧!

_app.tsx 可以做什麽?

App 跟 Document 皆是 Next.js 的进入点,而 Document 的层级更高,Document 中的程序码会先被执行,接着才会执行 App 中的内容。每一个页面的顶层都包含 App,如果想要让所有的页面共享同样的 Layout、错误捕捉、全域的 CSS 等,需要 override 原本 Next.js 的 App,你可以在 /pages 资料夹中建立一个 _app.tsx ,如此一来 Next.js 就会执行客制化的 _app.tsx

整合 NextAuth Provider

在前面的章节「在 Next.js 做 JWT 验证,使用既有的 Backend API 」有提到如何使用 NextAuth 进行 JWT 验证,如果想要让取得使用者验证的 session 更有效率,可以在 _app.tsx 中新增 NextAuth 的 context provider,让 useSession 可以在 SSR 的页面中优先从 context 中取值,这样就不用每次切换页面时都会重新打验证使用者的 API,整合 context provider 至 _app.tsx 有助於效能提升。

import { AppProps } from "next/app";
import { Provider } from "next-auth/client";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <Provider session={pageProps.session}>
      <Component {...pageProps} />
    </Provider>
  );
}

export default MyApp;

Error boundary

Error boundary 是一个从 React 16 版以後出现的概念,它是一个可以在 runtime 时捕捉到客户端发现的问题,在 React component tree 里面发生的错误会被 componentDidCatch 接住,然後我们可以透过一些方法记录这些错误讯息到服务器中,让我们可以尽早地发现问题。

import type { AppProps } from "next/app";

import "../styles/globals.css";
import ErrorBoundary from "./ErrorBoundary";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ErrorBoundary>
      <Component {...pageProps} />
    </ErrorBoundary>
  );
}
export default MyApp;

以下是一个简易的范例,页面中有一个按钮,点击了之後会显示 props 物件中的一个属性,但是实际上 props.someProps.a 不存在,会发生 JavaScript 的错误,最後让 error boundary 捕捉到。

import { NextPage } from "next";
import { useState } from "react";

interface Props {
  someProps: {
    a: string;
  };
}

const Home: NextPage<Props> = (props) => {
  const [visible, setVisible] = useState(false);
  return (
    <>
      {visible && <div>{props.someProps.a}</div>}
      <button onClick={() => setVisible(true)}>click me</button>
    </>
  );
};

export default Home;

但是在 Next.js 中使用 error boundary 的时候要注意,error boundary 能够捕捉的是 runtime error,但是一个 SSR 的页面发生的错误并没有办法让 componentDIdCatch 抓到错误讯息,因为 SSR 页面会在服务器端执行完毕,最後把已经渲染完的 HTML 回传给使用者,如果渲染时发生问题服务器会直接回传 HTTP 500,error boundary 没有办法 fallback 相对应的 component。

500 画面

这是一个存在与几年的 issue「Custom componentDidCatch does not work during SSR #5070」,在这样的情况下,从 Sentry - next.js 这个解决方案中,看到的不是 error boundary 在 React 中会使用的方式,不过这个会是另一个议题了。

Ant design

Ant design 是世界上第二热门的 React UI 元件库,俗话说:「如果我能看得更远, 那是因为站在巨人的肩膀上」,设计师可以基於目前已经做好的元件进一步扩充,而不是从零开始做起,在已经有非常优秀的元件库之後,我们在建立产品的前端页面就会事半功倍。

除了看到 ant design 的好处之外,我们也要了解使用它的风险,紧关它是一个拥有 74K 颗星星的开源专案,但是让人不得不提到在 2018 发生的圣诞节彩蛋 #13098 搞得许多公司沸沸扬扬,因为 ant design 的目标群众有许多事 2B 的客户,在当年突然多出了圣诞节彩蛋让许多工程师都非常纳闷可以这样开发开源专案吗?

此外,在 2021 年 2 月时 ant design 的官方 GitHub repo 没理由地突然被移除,包含官方网站 ant.design 也是突然间消失在网路上,後来索性很快有其他备援,所以没有让这件事耽搁太久。但是也许很多人就会开始怀疑这个元件库的可靠性,前後发生很严重的事情,难免会影响大家对它的信心。

以上是题外话,除了发生以上两件大事之外,我们不得不说它是一个非常优秀的元件库,现在我们想要在 Next.js 整合 ant design 的话该怎麽办呢?

Next.js 目前尚未内建支援 less

在目前 Next.js 11.1.2 版本中,Next.js 已经内建支援 .css.scss.sass 的档案格式,读者们可以从 next-evn.d.ts 中的 next/types/global 型别定义党看到 TypeScript 支援以上三种不同格式的档案。

但是 Next.js 尚未支援 less 的档案,因此在使用 ant design 时需要修改预设的 theme 就会遇到重重困难,现在可以看到 #23185 即在提供 Next.js 对 less 档案的支援,可是似乎内建 less 还需要一段时间才会发生。

如果想要在 Next.js 11.1.2 版本中使用 ant design,社群有相对应的解决方案为 next-with-less ,这个插件可以让我们以最少量的设定整合 ant design 的 theme。

设定 next-with-lessnext-compose-plugins

next-with-less 的使用方式就像是 HOC,需要包在 config 外面,但是我们不可能只需要设定 less 的支援,还需要有其他的设定,所以为了整合其他设定,还需要使用 next-compose-plugins 这个套件。我们使用以下指定安装两个套件:

yarn add -D next-with-less next-compose-plugins

接着我们需要修改 next.config.js 中的设定:

// next.config.ts
const withPlugins = require("next-compose-plugins");

const withLess = require("next-with-less");

const plugins = [
  /* ...other plugins... */
  [
    withLess,
    {
      lessLoaderOptions: {
        lessOptions: {
          modifyVars: {
            "primary-color": "#00ccb4",
            "border-radius-base": "4px",
          },
        },
      },
    },
  ],
  /* ...other plugins... */
];

module.exports = withPlugins(plugins, {
  reactStrictMode: true,
});

然後,为了在专案中能够使用 ant design 的样式,我们需要在 pages/_app.tsx 中引入 antd.less 这个档案,这样才能让专案全域都可以拿到 ant design 的样式:

// _app.tsx
import type { AppProps } from "next/app";
import "antd/dist/antd.less";

function MyApp({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}
export default MyApp;

最後,测试看看 ant design 的预设元件 theme 是不是已经被修改,在一个页面中使用「按钮元件」,因为 primary-colorborder-radius-base 都被新的设定覆盖,所以你在画面上就可以看到修改预设 theme 後的元件。

import { NextPage } from "next";
import { Button } from "antd";

const Home: NextPage = () => {
  return <Button type="primary">my button</Button>;
};

export default Home;

修改预设样式的 button

Reference


<<:  Day 22 - 物件导向与向量3 - class + mouseInpress 设定

>>:  【Day 21】Hook 04:useContext

D28-(9/28)-大台北(9908)-稳定的民生必需品,瓦斯

注:发文日和截图的日期不一定是同一天,所以价格计算上和当日不同,是很正常的。 声明:这一系列文章并无...

从 JavaScript 角度学 Python(24) - 继承

前言 前一篇已经提前预告接下来将会讲继承了,所以这一篇当然就是会介绍继承啦~(废话) 继承的概念 继...

Day 28: 初始化要测试的component

来,今天我们来聊一下怎麽帮redux在测试时添加初始化的状态资料,借用昨天的程序,并增加store的...

如何使用2Captcha的谷歌拓展程序绕过验证码

导读:一般来说,使用自动绕过谷歌验证码的服务的人群是程序员,尤其是自动化程序开发者,爬虫工程师等。当...

Day22 CSS版型设计完成网页!

前面的几篇文章已经让我们可以完成一个属於你的网页!包括从页首、网页内容、页尾、排版,这些所教的工具如...