[vercel/next.js]Link API 打破了直觉

2024-05-15 255 views
9
功能要求

我想要一个更直观的LinkAPI,它尊重我们对原生标签的期望a

您的功能请求与问题相关吗?请描述一下。

当前的链接 API 如下所示:

<Link href="/about">
  <a>here</a>
</Link>

由于以下几个原因,这是有问题的:

  1. 它打破了 a11y 的直觉。当我看到a没有 的标签时href,警钟就会响起,因为这是一种用户体验反模式。事实上,这个 API 无法满足anchor-is-valideslint-plugin-jsx-a11y 规则(参见https://github.com/evcohen/eslint-plugin-jsx-a11y/issues/402)。
  2. 它需要一个cloneElement应该避免的调用,因为它的行为令人困惑。 (我怎么知道我的a标签会被赋予href?这个 API 没有任何暗示。)
描述您想要的解决方案

我想要一个不需要您键入a不带标签的解决方案href,也不需要您复制href信息。

描述您考虑过的替代方案

有一些不错的选择:

  1. a如果没有给出子项,则默认标记:
<Link href="/about" />

这是react-router使用的方法,并且效果很好。


  1. 使用渲染道具:
<Link href="/about">
  {href => <a href={href}>here</a>}
</Link>

此方法安抚了anchor-is-valid规则,并且不需要用户复制 href 信息,因为构建 href 会传递到他们正在渲染的组件。


  1. 组件注入:
<Link href="/about" component={a} />

1 和 2 的组合足以覆盖所有合理的用例。

额外的背景信息

https://github.com/zeit/next.js/commit/6431f5fce2593d8cadb81841eb0e717facbe4aa6

回答

6

@timneutkens 你会接受使用类似方法的 PR 吗?这将是一个相当大的突破性改变,但在我看来这是值得的。我认为当前的next/linkAPI 是 atm 的下一个弱点之一。

0

这方面有什么进展吗?尝试设置测试时遇到同样的问题a11y

8

是的,我同意/回应@WestonThayer 的观点!拥有辅助功能插件的目的是审核我们不支持重要规则的情况。如果 Next 的基本链接结构不允许我们检查我们是否合规,我们将不得不继续在我们的流程中手动执行此检查,或者稍后在我们的构建过程中处理它(增加更多不必要的开发时间)。

9

这不是styled-jsx的限制吗?由链接呈现的锚点不会应用样式。

2

关于此问题的任何消息,或者不禁用 href 规则的良好解决方法?

3

@renet 从我们得到的链接中构建我们想要的链接。限制是——它破坏了 styled-jsx for a,所以你需要一个替代的 CSS-in-JS 解决方案。

"jsx-a11y/anchor-is-valid": [
  "error",
  {
    "components": ["Link"]
  }
],
"jsx-a11y/anchor-has-content": [
  "error",
  {
    "components": ["Link"]
  }
]
import NextLink, { LinkProps as NextLinkProps } from "next/link";
import { ComponentProps } from "react";

export interface LinkProps
  extends NextLinkProps,
    Omit<ComponentProps<"a">, keyof NextLinkProps> {}

export const Link = ({
  href,
  as,
  replace,
  scroll,
  shallow,
  passHref,
  prefetch,
  ...rest
}: LinkProps) => (
  <NextLink
    href={href}
    as={as}
    replace={replace}
    scroll={scroll}
    shallow={shallow}
    passHref={passHref}
    prefetch={prefetch}
  >
    {/* href is passed by NextLink */}
    {/* eslint-disable-next-line jsx-a11y/anchor-is-valid, jsx-a11y/anchor-has-content */}
    <a {...rest} />
  </NextLink>
);
7

2018 年的这个问题仍然具有现实意义。原帖中提出的所有三个解决方案都很棒。它们被称为重大变更,但我要指出的是,当前的 api 可以继续受到支持,使其成为非重大变更。只需添加这 3 个选项中的任何一个即可使 Next.js 与 jsx-a11y 完美配合。

9

我认为这非常重要。现在,Next.js 正在与 Gatsby 竞争,我很快意识到由于缺乏辅助功能支持,我无法使用 Next.js。可访问性非常重要,应该成为优先事项(并且不仅仅是遵守 jsx-a11y)。

7

也遇到了这个。我将 nx.dev 与 nextjs 一起使用,它为我设置了 jsx-a11y。我不想全局禁用 eslint 规则,因为会有没有链接父级的锚链接,例如外部链接

2

用老学校的碰撞击中这个。

2021 年这仍然是一个问题,对于大型团队和项目来说,去掉 a11y 护栏绒毛并不是一个选择。

2

另一种(但很hacky)解决方案是启用该passHref属性并将内部设置href为虚拟值。 Next.js 将使用外部组件的正确值覆盖虚拟值,因此一切都会正常工作并且会通过jsx-a11y/anchor-is-valid规则。

  <Link href="/my-amazing-page" passHref>
    <a href="dummy">Go to my amazing page</a>
  </Link>
9

2021年仍然是一个问题

我们仍在努力解决这个问题。这比您想象的要复杂,因为您没有考虑到数十万现有的 Next.js 应用程序无法升级到具有本问题中建议的解决方案的版本。

在 Next.js 10 中,我们引入了https://nextjs.org/blog/next-10#automatic-resolving-of-href作为实现这一目标的增量步骤。并且有一个开放的 RFC 可以在此基础上构建:https://github.com/vercel/next.js/discussions/8207,以便能够<Link to="">自动处理<a>始终被注入的情况。当人们升级时,使用href这种行为会导致问题,但可能会编写一个代码模块。

https://twitter.com/timneutkens/status/1295363065056301056

3

如果有一个 codemod 就太好了。另一种是 eslint 插件,它从 a11y 扩展而来,但如果锚点包含在 Link 标签中,则允许没有 href 的锚点

2

很高兴看到这方面的进展。在等待长期解决方案时,我一直在使用一个实用程序组件Link来一起处理和组件的创建a。通过这种方式,您可以将实用程序组件添加到 eslint 配置中的要验证的组件列表中,并获得 linting 的安全性和<Link>api 可以移动之前的功能。

8

关于这个问题我有一个疑问:

省略 a 标签并仅在链接组件中包含文本似乎可行,因为next/link 的代码如下:

    // Deprecated. Warning shown by propType check. If the childen provided is a string (<Link>example</Link>) we wrap it in an <a> tag
    if (typeof children === 'string') {
      children = <a>{children}</a>
    }

使用此方法作为解决方法有哪些缺点? Styled-jsx 上面已经提到了,也不可能给 Link-Component 一个类。

PS:评论说,,Warning shown by propType check.但没有向我显示警告(使用打字稿)。

0

? ?

5

我正在研究这个问题,我想这是否是一个很好的解决方法:

import React, { forwardRef, ReactNode } from 'react';
import Link, { LinkProps } from 'next/link';

interface NextLinkProps extends LinkProps {
  children?: ReactNode;
  className?: string;
  style?: CSSProperties;
  prefetch?: boolean;
  target?: '_blank';
  shallow?: boolean;
  scroll?: boolean;
}
export default forwardRef(
  (
    {
      href,
      children,
      shallow = false,
      className,
      style,
      target,
      scroll = true,
      ...props
    }: NextLinkProps,
    ref: any,
  ) => {
    return (
      <Link href={href} passHref shallow={shallow} scroll={scroll}>
        <a
          {...props}
          ref={ref}
          href="dummy"
          style={{
            textDecoration: 'none',
            color: 'var(--color-text)',
            ...style,
          }}
          className={className}
          target={target}
          rel={target ? 'noreferrer' : undefined}
        >
          {children}
        </a>
      </Link>
    );
  },
);
8

大家好,有什么关于如何正确处理这个问题的消息吗?

5

另一种(但很hacky)解决方案是启用该passHref属性并将内部设置href为虚拟值。 Next.js 将使用外部组件的正确值覆盖虚拟值,因此一切都会正常工作并且会通过jsx-a11y/anchor-is-valid规则。

  <Link href="/my-amazing-page" passHref>
    <a href="dummy">Go to my amazing page</a>
  </Link>

@timneutkens - 如果这个临时解决方案包含在 Next.js 的链接文档中,那就太好了。