需要减少NextJS站点的CLS以提高站点性能。

huangapple go评论73阅读模式
英文:

Need to reduce CLS of the NextJS site to improve site performance

问题

我正在使用NextJS和Mantine开发网站。我已经使用createStyles来添加样式。另外,我尽量多地使用Mantine组件。

当我将网站部署到Vercel时,我发现除了CLS之外,所有网站性能参数都很好,但CLS非常糟糕;非常糟糕,以至于您在第一次启动网站时可以感受到内容的移动。

仅举一个示例,以下是Header的代码:

import { useState } from 'react';
import {
  createStyles, Container, Anchor, Group, ActionIcon, useMantineColorScheme
} from '@mantine/core';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import Sun from '@/public/svg/sun.svg';
import MoonStars from '@/public/svg/moonStars.svg';

const useStyles = createStyles((theme) => ({
  inner: {
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'space-between',
  },

  burger: {
    [theme.fn.largerThan('sm')]: {
      display: 'none',
    }
  },

  links: {
    fontWeight: 700,
    textTransform: 'uppercase',
    textDecoration: 'none',
    fontSize: theme.fontSizes.md,
  },

  mainLink: {
    color: theme.colorScheme === 'dark' ? theme.colors.dark[1] : theme.colors.gray[6],
    borderBottom: `2px solid transparent`,
    transition: 'border-color 100ms ease, color 100ms ease',

    '&:hover': {
      color: theme.colorScheme === 'dark' ? theme.white : theme.black,
      textDecoration: 'none',
    },
  },

  mainLinkActive: {
    color: theme.colorScheme === 'dark' ? theme.white : theme.black,
    borderBottomColor: theme.colors[theme.primaryColor][theme.colorScheme === 'dark' ? 5 : 6],
  },

  sideText: {
    color: theme.colorScheme === 'dark' ? theme.white : theme.black,
  }
}));

export default function Component() {
  const { colorScheme, toggleColorScheme } = useMantineColorScheme();
  const { classes, cx } = useStyles();
  let pathname = usePathname();
  const [active, setActive] = useState(() => {
    if (pathname.split('/')[1] === '') return 0;
    if (pathname.split('/')[1] === 'blog') return 1;
  });

  let mainLinks = [
    { "link": "/", "label": "About" },
    { "link": "/blog", "label": "Blog" },
  ]

  const mainItems = mainLinks.map((item, index) => (
    <Anchor component={Link} href={item.link} key={item.label} py='xl' px='md'
      className={cx(classes.mainLink, { [classes.mainLinkActive]: index === active }, classes.links)}
      onClick={() => setActive(index)} >
      {item.label}
    </Anchor>
  ));

  const changeColorScheme = () => {
    toggleColorScheme();
    localStorage.setItem('color-scheme', colorScheme === 'dark' ? 'light' : 'dark');
  }

  return (
    <Container pos='relative' mb='2rem'>
      <Container pos='sticky' className={classes.inner}>
        <div className={classes.links}>
          <Group spacing={0} >
            {mainItems}
          </Group>
        </div>
        <Group position="center" my="xl">
          <ActionIcon
            onClick={() => changeColorScheme()}
            size="lg"
            sx={(theme) => ({
              backgroundColor:
                theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
              color: theme.colorScheme === 'dark' ? theme.colors.yellow[4] : theme.colors.blue[6],
            })}>
            {colorScheme === 'dark' ? <Sun /> : <MoonStars />}
          </ActionIcon>
        </Group>
        {/* <Burger opened={opened} onClick={toggle} className={classes.burger} size="sm" /> */}
      </Container>
    </Container>
  );
}

如果您想自己尝试,可以在此处查找已部署的网站

我能想到的一个主要问题是Mantine可能会将所有组件转换为仅JS并且发送过多的JS,使其变得动态化,从而导致所有内容的移动。

如果使用Mantine组件导致问题,我准备切换到原生HTML组件,但我需要确定是否是Mantine导致了问题。

请帮助找出到底是什么导致高的内容移动,从而导致高的CLS,以及如何改进/防止内容移动。

英文:

I am using NextJS with Mantine to develop a site. I have used createStyles to add stylings. Also I try to use as many mantine components as possible.

When I deployed the site on vercel, I found out all site performance parameters were great except CLS, which was terrible; so terrible that you can feel the content shift while starting the site for the first time.

Just for an example, the code for Header is given below:

import { useState } from &#39;react&#39;;
import {
  createStyles, Container, Anchor, Group, ActionIcon, useMantineColorScheme
} from &#39;@mantine/core&#39;;
import Link from &#39;next/link&#39;;
import { usePathname } from &#39;next/navigation&#39;;
import Sun from &#39;@/public/svg/sun.svg&#39;;
import MoonStars from &#39;@/public/svg/moonStars.svg&#39;;

const useStyles = createStyles((theme) =&gt; ({
  inner: {
    display: &#39;flex&#39;,
    alignItems: &#39;center&#39;,
    justifyContent: &#39;space-between&#39;,
  },

  burger: {
    [theme.fn.largerThan(&#39;sm&#39;)]: {
      display: &#39;none&#39;,
    }
  },

  links: {
    fontWeight: 700,
    textTransform: &#39;uppercase&#39;,
    textDecoration: &#39;none&#39;,
    fontSize: theme.fontSizes.md,
  },

  mainLink: {
    color: theme.colorScheme === &#39;dark&#39; ? theme.colors.dark[1] : theme.colors.gray[6],
    borderBottom: `2px solid transparent`,
    transition: &#39;border-color 100ms ease, color 100ms ease&#39;,

    &#39;&amp;:hover&#39;: {
      color: theme.colorScheme === &#39;dark&#39; ? theme.white : theme.black,
      textDecoration: &#39;none&#39;,
    },
  },

  mainLinkActive: {
    color: theme.colorScheme === &#39;dark&#39; ? theme.white : theme.black,
    borderBottomColor: theme.colors[theme.primaryColor][theme.colorScheme === &#39;dark&#39; ? 5 : 6],
  },

  sideText: {
    color: theme.colorScheme === &#39;dark&#39; ? theme.white : theme.black,
  }
}));

export default function Component() {
  const { colorScheme, toggleColorScheme } = useMantineColorScheme();
  const { classes, cx } = useStyles();
  let pathname = usePathname();
  const [active, setActive] = useState(() =&gt; {
    if (pathname.split(&#39;/&#39;)[1] === &#39;&#39;) return 0;
    if (pathname.split(&#39;/&#39;)[1] === &#39;blog&#39;) return 1;
  });

  let mainLinks = [
    { &quot;link&quot;: &quot;/&quot;, &quot;label&quot;: &quot;About&quot; },
    { &quot;link&quot;: &quot;/blog&quot;, &quot;label&quot;: &quot;Blog&quot; },
  ]

  const mainItems = mainLinks.map((item, index) =&gt; (
    &lt;Anchor component={Link} href={item.link} key={item.label} py=&#39;xl&#39; px=&#39;md&#39;
      className={cx(classes.mainLink, { [classes.mainLinkActive]: index === active }, classes.links)}
      onClick={() =&gt; setActive(index)} &gt;
      {item.label}
    &lt;/Anchor&gt;
  ));

  const changeColorScheme = () =&gt; {
    toggleColorScheme();
    localStorage.setItem(&#39;color-scheme&#39;, colorScheme === &#39;dark&#39; ? &#39;light&#39; : &#39;dark&#39;);
  }

  return (
    &lt;Container pos=&#39;relative&#39; mb=&#39;2rem&#39;&gt;
      &lt;Container pos=&#39;sticky&#39; className={classes.inner}&gt;
        &lt;div className={classes.links}&gt;
          &lt;Group spacing={0} &gt;
            {mainItems}
          &lt;/Group&gt;
        &lt;/div&gt;
        &lt;Group position=&quot;center&quot; my=&quot;xl&quot;&gt;
          &lt;ActionIcon
            onClick={() =&gt; changeColorScheme()}
            size=&quot;lg&quot;
            sx={(theme) =&gt; ({
              backgroundColor:
                theme.colorScheme === &#39;dark&#39; ? theme.colors.dark[6] : theme.colors.gray[0],
              color: theme.colorScheme === &#39;dark&#39; ? theme.colors.yellow[4] : theme.colors.blue[6],
            })}&gt;
            {colorScheme === &#39;dark&#39; ? &lt;Sun /&gt; : &lt;MoonStars /&gt;}
          &lt;/ActionIcon&gt;
        &lt;/Group&gt;
        {/* &lt;Burger opened={opened} onClick={toggle} className={classes.burger} size=&quot;sm&quot; /&gt; */}
      &lt;/Container&gt;
    &lt;/Container&gt;
  );
}

You can find the website deployed here if you want to try it yourself

One major issue I can think of is that maybe mantine is converting all components into only JS and shipping too much js is making it dynamic and hence all the shifting

If using mantine components is causing the issue, I am ready to shift to native HTML components, but I need to be sure if Mantine's the one causing the issue.

Please help figure out what exactly is causing the high content shift leading to high CLS and how to improve/prevent content shift.

答案1

得分: 2

  1. 我能想到的一个主要问题是,也许 Mantine 正在将所有组件转换为仅使用 JS,并且发送太多的 JS 使其变得动态化,从而导致所有的转换。

  2. 这就是问题所在。HTML 和 CSS 总是比 JavaScript 快。由于您的 Mantime 组件依赖于 JavaScript,因此您的 HTML 和 CSS 会首先加载,从而导致一闪而过的未样式化内容。

  3. 最佳方法是切换到原生 HTML,但这可能非常困难。

  4. 另一种很好的方法是使用一个无头 UI 库,比如 Radix UIBase UI。它们的大多数组件不需要 JavaScript,并且已经遵循 WAI-ARIA 辅助功能标准。您仍然需要手动定义您的 CSS 样式。

  5. 然而,最简单的方法是在项目根目录中创建一个名为 loading.tsx 的文件,以减少您的 CLS。这里是相关的 文档

  6. 最后,这是一篇关于如何减少 CLS 的好文章,提供了一些通用的建议。 减少 CLS 的提示

英文:

> One major issue I can think of is that maybe mantine is converting all components into only JS and shipping too much js is making it dynamic and hence all the shifting

This is what's wrong. HTML and CSS is always faster than JavaScript. Since your Mantime components depend on JavaScript, your HTML and CSS load first causing a flash of un-styled content.

  1. The best approach would be to switch to native HTML but that would be very difficult.

  2. Another great approach would be to use a headless UI library like Radix UI or Base UI. Most of there components don't require JavaScript and already follow WAI-ARIA accessibility standards. You would still have to manually define your CSS styles.

  3. However, the easist approach would be to create a loading.tsx file in the root of your project to reduce your CLS. Here are the docs for that.

Finally, here's a good article with general tips to reduce CLS.

huangapple
  • 本文由 发表于 2023年7月10日 17:26:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/76652412.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定