Next.js App目录 Antd和Styled-components的安装
如果你在 Next.js 当中使用了 App Router,那么在安装Antd和Styled-components上就和普通前端有一定的差别。
文章内容大多来自官网
Styled-components的安装
Next App目录中的客户端组件只支持以下CSS-in-JS库:
kuma-ui
@mui/material
pandacss
styled-jsx
styled-components
style9
tamagui
tss-react
vanilla-extract
目前(2023/8/18)Next团队正在对emotion
库进行支持,如果需要可在GitHub寻求解决方法
注册
首先, styled-components
使用 API 创建一个全局注册表组件来收集呈现期间生成的所有 CSS 样式规则,以及一个用于返回这些规则的函数。然后使用 useServerInsertedHTML
挂钩将注册表中收集的样式注入到根布局中的 <head>
HTML 标记中。
// lib/registry-style.tsx
'use client'
import React, { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { StyleRegistry, createStyleRegistry } from 'styled-jsx'
export default function StyledJsxRegistry({
children,
}: {
children: React.ReactNode
}) {
// Only create stylesheet once with lazy initial state
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [jsxStyleRegistry] = useState(() => createStyleRegistry())
useServerInsertedHTML(() => {
const styles = jsxStyleRegistry.styles()
jsxStyleRegistry.flush()
return <>{styles}</>
})
return <StyleRegistry registry={jsxStyleRegistry}>{children}</StyleRegistry>
}
然后将children
包裹在注册的样式组件内:
// app/layout/tsx
import StyledComponentsRegistry from './lib/registry'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</body>
</html>
)
}
更多了解:
During server rendering, styles will be extracted to a global registry and flushed to the
<head>
of your HTML. This ensures the style rules are placed before any content that might use them. In the future, we may use an upcoming React feature to determine where to inject the styles.在服务器渲染期间,样式将被提取到全局注册表并刷新到 HTML 中
<head>
。这可确保样式规则放置在可能使用它们的任何内容之前。将来,我们可能会使用即将推出的 React 功能来确定在何处注入样式。
During streaming, styles from each chunk will be collected and appended to existing styles. After client-side hydration is complete,
styled-components
will take over as usual and inject any further dynamic styles.在流式传输期间,将收集每个区块中的样式并将其附加到现有样式中。客户端补水完成后,
styled-components
将像往常一样接管并注入任何进一步的动态样式。
We specifically use a Client Component at the top level of the tree for the style registry because it's more efficient to extract CSS rules this way. It avoids re-generating styles on subsequent server renders, and prevents them from being sent in the Server Component payload.
我们专门在样式注册表的树顶层使用客户端组件,因为以这种方式提取 CSS 规则更有效。它避免在后续服务器渲染中重新生成样式,并防止它们在服务器组件有效负载中发送。
Antd安装
为了让antd组件库在Next.js应用中能够更好的工作,提供更好的用户体验,需要将antd首屏样式按需抽离并知入到HTML中,以避免页面闪动
安装@ant-design/cssinjs
npm install @ant-design/cssinjs --save
注册
'use client';
import React from 'react';
import { StyleProvider, createCache, extractStyle } from '@ant-design/cssinjs';
import { useServerInsertedHTML } from 'next/navigation';
const StyledComponentsRegistry = ({ children }: { children: React.ReactNode }) => {
const cache = createCache();
useServerInsertedHTML(() => (
<style id="antd" dangerouslySetInnerHTML={{ __html: extractStyle(cache, true) }} />
));
return <StyleProvider cache={cache}>{children}</StyleProvider>;
};
export default StyledComponentsRegistry;
嵌套包裹
const RootLayout = ({ children }: { children: React.ReactNode }) => (
<html lang="en">
<body className={inter.className}>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</body>
</html>
);
antd样式缓存丢失问题
当我使用以上方式注册antd的样式,在开发环境中没有问题,但是经过yarn build
和yarn start
运行时,问题就出现了,但是显示都是打包后的报错,不能准确定位问题的根源。
首先,我们需要配置SourceMap
来查看问题的出错位置:
/** @type {import('next').NextConfig} */
const nextConfig = {
/*********/
productionBrowserSourceMaps: true,
/*********/
};
module.exports = nextConfig;
我们根据报错信息和位置,可以推断出来,大概是cssinjs的缓存出了问题,没有找到缓存,然后我们去查找注册时关于缓存的代码,最关键的还是这一段代码,:
const cache = createCache();
我最后的解决方法是将样式缓存放在外层:
'use client';
import React from 'react';
import { StyleProvider, createCache, extractStyle } from '@ant-design/cssinjs';
import { useServerInsertedHTML } from 'next/navigation';
import Entity from '@ant-design/cssinjs/lib/Cache';
// 创建全局的样式缓存
const cache = createCache();
const StyledComponentsRegistry = ({ children }: { children: React.ReactNode }) => {
// const cache = createCache();
useServerInsertedHTML(() => (
<style id="antd" dangerouslySetInnerHTML={{ __html: extractStyle(cache as Entity, true) }} />
));
return <StyleProvider cache={cache}>{children}</StyleProvider>;
};
export default StyledComponentsRegistry;