Next.js: [RFC]动态路由

创建于 2019-06-19  ·  90评论  ·  资料来源: vercel/next.js

动态路线

背景

动态路由(也称为URL蛞蝓或漂亮的/清洁的网址)已经是一个长期要求的功能Next.js.的

当前的解决方案涉及在应用程序的前面放置一个L7代理自定义服务器用户界面中间件。 这些解决方案都无法提供足够的人体工程学开发人员经验。

此外,使用自定义服务器的用户无意中退出了高级框架级别的功能,例如每页无服务器功能。

目标

  1. 利用约定提供易于理解的URL Slug支持
  2. 涵盖在野外观察到的大多数用例
  3. 无需自定义服务器来支持/blog/:post
  4. 尽可能验证<Link />路线转换
  5. 避免需要路由清单的实现
  6. 路由必须可通过文件系统表达

提案

Next.js应该支持与整个URL段匹配的命名URL参数。 这些路由将通过文件系统表示:

  1. []包裹的文件名或目录名称将被视为命名参数
  2. 明确的路段优先于动态路段,从左到右匹配
  3. 路由参数是必需的,从不可选
  4. 路由参数将合并到query对象中(可通过withRoutergetInitialPropsrouter withRouter )-这些参数不能被查询参数覆盖

为了帮助理解此建议,让我们检查以下文件树:

pages/
├── [root].js
├── blog/
│ └── [id].js
├── customers/
│ ├── [customer]/
│ │ ├── [post].js
│ │ ├── index.js
│ │ └── profile.js
│ ├── index.js
│ └── new.js
├── index.js
└── terms.js

Next.js将产生以下路由,并按以下顺序注册:

;[
  { path: '/', page: '/index.js' },
  { path: '/blog/:id', page: '/blog/[id].js' },
  { path: '/customers', page: '/customers/index.js' },
  { path: '/customers/new', page: '/customers/new.js' },
  { path: '/customers/:customer', page: '/customers/[customer]/index.js' },
  {
    path: '/customers/:customer/profile',
    page: '/customers/[customer]/profile.js',
  },
  { path: '/customers/:customer/:post', page: '/customers/[customer]/[post].js' },
  { path: '/terms', page: '/terms.js' },
  { path: '/:root', page: '/[root].js' },
]

使用范例

这些示例都假定文件名为pages/blog/[id].js

使用<Link />导航到页面

<Link href="/blog/[id]" as="/blog/how-to-use-dynamic-routes">
  <a>
    Next.js: Dynamic Routing{' '}
    <span role="img" aria-label="Party Popper">
      🎉
    </span>
  </a>
</Link>

上面的示例将过渡到/blog/[id].js页面,并将以下query对象提供给_Router_:

{
  id: 'how-to-use-dynamic-routes'
}

从_Router_读取命名参数

import { useRouter } from 'next/router'

function BlogPost() {
  const router = useRouter()
  // `blogId` will be `'how-to-use-dynamic-routes'` when rendering
  // `/blog/how-to-use-dynamic-routes`
  const blogId = router.query.id
  return <main>This is blog post {blogId}.</main>
}

export default BlogPost

注意:您也可以使用withRouter

读取getInitialProps命名参数

function BlogPost({ blogText }) {
  return <main>{blogText}</main>
}

BlogPost.getInitialProps = async function({ query }) {
  // `blogId` will be `'how-to-use-dynamic-routes'` when rendering
  // `/blog/how-to-use-dynamic-routes`
  const blogId = query.id

  const { text } = await fetch(
    '/api/blog/content?id=' + encodeURIComponent(blogId)
  ).then(res => res.json())

  return { blogText: text }
}

export default BlogPost

注意事项

可选的路由参数无法通过文件系统表达。

您可以通过创建一个导出参数版本的存根页面来仿真可选的路由参数(反之亦然)。 这在检查文件系统时增加了应用程序路由的可见性。

// pages/blog/comments.js
// (the optional version of `pages/blog/[id]/comments.js`)
export { default } from './[id]/comments.js'

命名参数不能出现在路由名称的中间。

这意味着名为blog-[id].js将被_literally_解释,而不与/blog-1匹配。 您可以将页面重组为/blog/[id].js也可以将整个URL段变成一个命名参数,然后处理在应用程序代码中剥离blog-的情况。

备择方案

用_insert符号here_而不是[]表示网址段

很少有符号可用来表示文件系统上的命名参数。 不幸的是,定义命名参数( :name )的最公认的方法不是有效的filename

在调查现有技术时,用于表示参数的最常见符号是_$[]

我们排除了_因为_通常表示内部路由无法公开路由(例如_app_document/_src/_logs )。
我们还排除了$因为它是bash中用于参数扩展的标记。

利用path-to-regexp获得全面支持

表示正则表达式的大多数符号都不是有效的文件名。 此外,复杂的正则表达式对优先级排序的路由很敏感。 文件系统不能表达顺序,也不能包含正则表达式符号。

将来,我们可能会允许在next.config.js或类似名称中定义的path-to-regexp路由。 目前,这超出了此提案的范围。

未来探索

捕获所有参数

将来,我们可能会考虑添加包罗万象的参数。 到目前为止,我们知道这些参数必须位于URL%来表示全部路由(例如pages/website-builder/[customerName]/%.tsx )。

最有用的评论

投票:要表达对可选参数的兴趣,请在此评论中加上“ +1”。

注意:可选参数已经可以在此RFC中使用,只是它们没有明确的语法(请参见警告)。

所有90条评论

投票:要表达对可选参数的兴趣,请在此评论中加上“ +1”。

注意:可选参数已经可以在此RFC中使用,只是它们没有明确的语法(请参见警告)。

投票:要表达对全部参数的兴趣,请在此评论中加上“ +1”。

注意:请在此线程中共享所有参数的用例! 我们希望更多地了解问题空间。

保留3

在ricardo.ch上,我们为每个路由使用一个语言环境前缀,这会使路由更加复杂。

有效路线示例:

  • / -具有自动检测到的语言环境的主页
  • /:locale -带有强制区域设置的主页
  • /:locale/search -搜索页面
  • /:locale/article/:id -文章页面

您认为可以支持此类前缀参数吗?

目前,我们使用https://www.npmjs.com/package/next-routes

另一件事:对于文章页面,我们还支持ID之前的一个子弹,例如/de/article/example-article-123 ,其中ID为123。这是通过使用next-routes进行的非常复杂的正则表达式完成的,看看如何用文件系统API来表示。

@ValentinH使用文件系统API都可以使用提供的路由-给定您提供的路由:

  • / => pages/index.js
  • /:locale => pages/$locale/index.js
  • /:locale/search => pages/$locale/search.js
  • /:locale/article/:id => pages/$locale/article/$id.js

我们还支持ID之前的子弹,例如/ de / article / example-article-123,其中ID为123

上面解决了这个用例:

命名参数不能出现在路由名称的中间。

这意味着名为blog-$id.js将按字面意义进行解释,而不与/blog-1匹配。 您可以将页面重组为/blog/$id.js也可以将整个URL段变成一个命名参数,并处理在应用程序代码中剥离blog-的情况。

此解决方案不符合您的需求吗? 我们很乐意进一步了解您的特定要求。

非常感谢您的回答。

我没想到要同时使用$locale/index.js作为文件夹和文件,这真的很整洁!

关于“中间的命名参数”,我忽略了它,因为我认为让子弹动态化是不同的。 但是,您是完全正确的,您提到的段落可以解决此问题。 去除应用程序代码中的段将是可行的方法🙂

这样的事情(解析来自.hidden .files / .folders的参数)是否可能?

pages/
├── .root.js
├── blog/
│ ├── .id/
│ │ ├── index.js
│ │ └── comments.js <-- optional?
├── customers/
│ ├── .customer/
│ │ ├── .post/
│ │ │ └── index.js
│ │ ├── index.js
│ │ └── profile.js
│ ├── index.js
│ └── new.js
├── index.js
└── terms.js

还是离开$,以便人们可以找到他们的文件:D但总是使用$ folder来表示参数?

pages/
├── $root.js
├── blog/
│ ├── $id/
│ │ ├── index.js
│ │ └── comments.js <-- optional?
├── customers/
│ ├── $customer/
│ │ ├── $post/
│ │ │ └── index.js
│ │ ├── index.js
│ │ └── profile.js
│ ├── index.js
│ └── new.js
├── index.js
└── terms.js

我曾经在使用npm软件包的应用程序中为可选参数提供此用例。 这些可以有范围。 路线如下:

  • /packages/express
  • /packages/express/dependencies
  • /packages/@babel/core
  • /packages/@babel/core/dependencies

因此,基本上,scope参数是可选的,但是当它以@开头时,它也是一个范围。
因此, /packages/express/dependencies/packages/@babel/core具有相同数量的细分,但在一种情况下是/dependenciesexpress ,在另一种情况下是/index@babel/core

最后,它是通过以下路线在react-router解决的:

<Switch>
  <Route path={`/packages/`} exact component={PackagesOverview} />
  <Route path={`/packages/:name(@[^/]+/[^/]+)`} component={PackageView} />
  <Route path={`/packages/:name`} component={PackageView} />
</Switch>

我不确定我是否在RFC中看到此用例的解决方案。

至于全部用例,我正在考虑与递归嵌套数据(如文件夹结构,树视图,树图)进行任何深层链接。

我的2美分:文件名中的美元符号不是一个好主意,因为它们被贝壳用作标记。 您将使尝试运行rm $root.js人们感到困惑。 下划线似乎是一个不错的选择。

更广泛地说:像许多人一样,我过去尝试利用文件系统作为对此的解决方案。 最终,我认为文件系统永远无法提供您想要的完整表达。 例如,声明性路由器通常使您可以为动态参数指定验证模式。 在这种情况下,架构的一部分位于文件系统上,另一部分位于代码中。 关注点分离是一件好事,但是在这种情况下,这是技术上的限制,而不是其他任何事情。

@ValentinH一样,我们使用$ locale var,但这是可选的。

我们应该使用/page.ts和/page/$locale/page.ts吗?

因为我们可以使用“默认”语言环境或预定义的语言环境(用户设置),所以在这些情况下,我们不使用$ locale参数。

但是我们有更多用例:/ car / search / $ optional-filter-1 / $ optional-filter-2 / $ optional-filter-3

其中,可选过滤器1:红色;可选过滤器2:brand-ford等。

对于可选参数,则类似于/ $ required-param /和/ $$ optional-param /?

很棒,这即将在路线图上出现!

我不得不支持@timdp 。 当您甚至不能touch $file这将导致很多混乱。 您需要记住每次交互时都转义。 touch \$file; vim $file将在没有文件的情况下打开vim(因为$ file不是定义的变量)。
同样,shell中的制表符补全将列出所有变量,再次带来混乱。

我提出了两种选择,我认为它们提供了正确的关联并且应该在shell中工作:

  • =对于=customer它可以读作page is a customer =customer 。 您甚至可以在脑海中扭曲它,使其成为刚刚伸出来的冒号,从而类似于命名参数的最常见形式。
  • @因为它读起来也不错。 a customer@customer

另一种选择是使用花括号(除非它们在某些文件系统上是保留字符)。 此参数语法也是“现有技术”,并且被许多其他路由器使用:

pages/
├── {root}.js
├── blog/
│ └── {id}.js
├── customers/
│ ├── {customer}/
│ │ ├── {post}.js
│ │ ├── index.js
│ │ └── profile.js
│ ├── index.js
│ └── new.js
├── index.js
└── terms.js

这将允许在路由段的中间具有参数,并且每个段具有多个参数,因为可以清楚地知道参数的开始位置和结束位置,例如/product-{productId}-{productColor}

太激动了,动态路由即将来到Next.js!

关于命名参数的语法,这已在Spectrum上进行了讨论: https : [brackets] 。 Nuxt还将在版本3中实现这一点。让不同的框架对基于动态文件系统的路由使用相同的格式听起来是一件好事。

关于<Link />的用法,我认为开发人员很容易忘记同时设置hrefas属性。 我知道不可能将它们“合并”到href属性中,因为它会带来重大变化,但是我觉得可以用一种更优雅的方式解决它。

不幸的是,Bash使用花括号将命令分组。

我同意@ stephan281094关于<Link />用法,这将是错误的来源。

动态路由是一个非常有用的功能,因此你们真的很惊奇,并提出了解决方案和强大的支持!

在此主题上,通配符路由也将是该建议的有价值的补充。 您确实提到了“包罗万有”参数作为将来要研究的内容,但是它并未涵盖您可能想要执行诸如/category/* ,该情况可能具有N个级别,并且您想要所有他们呈现category页面。

是否可以安全地使用: ? 如果是这样,那将是我的投票,因为每个人都已经熟悉express的约定。

由于$与shell变量冲突,我个人强烈反对。

是否可以安全地使用: ? 如果是这样,那将是我的投票,因为每个人都已经熟悉express的约定。

显然:在Windows中是禁止使用的字符,因此它可能不安全。 与_一起使用也不是很理想,因为在URL中可以使用下划线。 我认为[brackets]是一个不错的解决方案,是因为它可以为以后的工作做更多证明。 如果Next.js将来希望支持post-12345类的路由,则使用此语法可以在不引入重大更改的情况下完成。

因此,要避免的字符列表为:

  • 与文件系统冲突: :*"<>|
  • 与shell变量冲突: $
  • 与bash大括号扩展{}冲突

还要别的吗?

由于以下几个原因,这并不能消除我们需要集中式路由文件的麻烦:

  • 我们有一个自动生成的站点地图,仅文件系统不足以对其进行定义。
  • 我们使用命名路由,而目标“页面”则由数据确定,而不是在构建时就知道。 根据名称和参数确定要加载哪个页面的逻辑由路由配置驱动。

由于以下原因,我们还会生成页面文件夹:

  • 我们使用中继,这意味着涉及GraphQL的模块需要唯一地命名。 因此,我们经常不能使路由段名称与模块名称相同。 index.js绝对不是唯一的,而且我看到我们有多个常见细分的地方,例如edit
  • 我们更喜欢将一次性的页面特定组件放置在页面模块本身的同级中,而Next.js不允许在页面文件夹中放置它们。

本质上,我们的模式是使用集中式路由配置来生成我们的pages文件夹,该文件夹包含的文件只不过是从代码库其他位置导入/导出模块而已。

为此,我的重点更多地放在该建议是否可以简单地用作我们现有页面生成过程的增强输出格式上,以便使我们至少可以获得不需要自定义服务器的好处。

我已经在其他地方遍历了一些用例: https

我没有看到的主要内容是支持文件系统中未表达的隐式参数,例如URL覆盖。

假设我们有一个这样的网址:

/some-vanity-url/

在当前的Next.js术语中,我们希望它映射到具有许多查询参数的产品页面,例如Product.js?id=foo&language=en

同样,在我们的网站上,大多数国家/地区的“站点”都由顶级网段限定范围,例如esie ,但gb站点安装时没有该网段。 这意味着所有gb页面都有一个隐式country参数,而对于所有其他国家/地区,它都是显式的。

另一个缺点是,因为在我们的例子中,同一“页面”可以存在于URL体系结构的多个安装点中,所以最终我们将得到比实际更多的捆绑(即几个重复的入口点)在实践中需要。

总体而言,该建议似乎可以在大多数常见用例中很好地使用,但是在_all_情况下,它并没有消除对路由配置或自定义服务器的需求。 但是假设这不能代替我今天使用框架的能力,我并不反对这是首选的快乐路径API。

我支持{id}建议。 它允许多个参数,我认为它看起来要好得多。 它也更适合React。

我赞成file/&param.js字符。 直接来自url,看起来与文件系统或bash不会冲突。

我会使用_并且可能允许在next.config.js覆盖那些确实需要其他东西的人。

赞赏这项工作。 想要了一段时间! ❤️

惊人! 🎉🎉🎉

我唯一的问题是Link需要hrefas参数。

我相信我们可以只写<Link to="blog/123" /> :由于Nextjs已经知道基于pages文件夹中文件的所有路由,因此可以轻松地将其转换为"/blog/$id"

因此,要避免的字符列表为:

&是bash中的控制运算符,它在异步子shell中运行参数的左侧。 纯文本: open pages/&customer将在后台运行open pages/ ,在前景Shell中运行命令customer

这看起来真的很酷。

看起来这确实会创建大量单个文件目录(如原始示例中的/blog/$id )。 如果您想要两个尾随的路由参数(即/git/compare/$hash1/$hash2 ),则将变得更加麻烦。

我也不喜欢发送博客文章的文件名是$id.js 。 将其命名为blog.js更具描述性。

也许结合@customRoute装饰器?

// pages/blog.js
import {useRouter, @customRoute} from 'next/router'

@customRoute('/blog/:id')
function BlogPost() {
  const router = useRouter()
  // `blogId` will be `'how-to-use-dynamic-routes'` when rendering
  // `/blog/how-to-use-dynamic-routes`
  const blogId = router.query.id
  return <main>This is blog post {blogId}.</main>
}

export default BlogPost

这似乎也为建议的全部参数提供了一种更清洁的解决方案。

装饰器无法应用于函数(自从我上次阅读以来可能会更改吗?)并且该建议可能还有很长的路要走

好吧,假设您走这条路,您可能会按照现在配置AMP的方式来做:

// /pages/blog.js
export const config = {
  amp: true,
  dynamicRoute: true // adds a [blog] property to the query object
  // dynamicRoute: /\d+/ // could even support regex if you want
};

但是,我认为类似的东西可以稍后添加,如果它在某些时候似乎有用的话。 我想我希望看到一个基本的支持,就像RFC中描述的那样。 得到一些实际的用法,然后完善它的中断点。 我还认为,应避免使用的唯一字符是文件系统字符。 这些才是构建此功能的真正障碍。

请确保使用对无服务器解决方案友好的角色! (在Aws上,有些字符可能会引起麻烦)

我不讨厌使用组件键导出配置对象。

您也可以只使用HOC

function BlogPost(props) {
    return <div />
}

export default withCustomRoute(BlogPost, "/blog/:id")

如果我们向页面添加一些静态字段(如getInitialProps)怎么办?

// pages/blog.js
import {useRouter} from 'next/router'

function BlogPost() {
  const router = useRouter()
  // `blogId` will be `'how-to-use-dynamic-routes'` when rendering
  // `/blog/how-to-use-dynamic-routes`
  const blogId = router.query.id
  return <main>This is blog post {blogId}.</main>
}

// By default it would be as it is now
BlogPost.route = '/blog/:id';

export default BlogPost

@ dmytro-lymarenko在浏览器中导航到/blog会发生什么? 404?

因为这需要在编译时确定,所以我想您需要可以静态分析的东西。 HOC或静态属性将不会。

您需要可以静态分析的东西。 HOC或静态属性不会

到目前为止给出的每个静态属性示例都可以进行静态分析(尽管您当然可以轻松解决问题)。 我们可以坚持要求您导出函数并以静态可分析的方式在其上设置route属性。 运行时可以检查在运行时设置但未被我们的静态分析器捕获的路由属性,并发出警告/引发错误。

当您在浏览器中导航到/ blog时会发生什么? 404?

@kingdaro-海事组织,是的。 如果要同时使用/blog/blog/:blogId路径,则可以使用目录。 您正在重载该路径,因此目录结构是合理的。

pages/
├── blog/
│ ├── $id.js
│ └── index.js

好吧,假设您走这条路,您可能会按照现在配置AMP的方式来做:

// /pages/blog.js
export const config = {
  amp: true,
  dynamicRoute: true // adds a [blog] property to the query object
  // dynamicRoute: /\d+/ // could even support regex if you want
};

但是,我认为类似的东西可以稍后添加,如果它在某些时候似乎有用的话。 我想我希望看到一个基本的支持,就像RFC中描述的那样。 得到一些实际的用法,然后完善它的中断点。 我还认为,应避免使用的唯一字符是文件系统字符。 这些才是构建此功能的真正障碍。

我认为使用config是个坏主意,因为您需要浏览多个文件才能看到实际的动态内容。 如果将其设置在文件系统中,则一眼就能看到它。

我想知道是否应该考虑多个标准路由解决方案。

对于那些刚接触Next / React的人或希望快速启动并运行一个简单应用程序的人来说,基于文件的简单路由是一个不错的卖点,但它可能会受到限制。 在我看来,试图将动态路由转换为这种模式可能会破坏这种简单性并导致不必要的复杂性,而这一切都是为了使所有内容都基于文件。

在阅读了此讨论并思考了我自己对Next.js的用法之后,我认为对替代(补充)路由系统的一流支持可能是解决此问题的最佳方法。

我喜欢这个线程中一些开箱即用的想法(例如使用装饰器的建议),但是这些想法肯定有自己的问题。 我希望我们能提出一些很棒的东西👍

我不讨厌使用组件键导出配置对象。

您也可以只使用HOC

function BlogPost(props) {
    return <div />
}

export default withCustomRoute(BlogPost, "/blog/:id")

这很酷,但是我想知道是否将路由信息拆分到许多文件中,例如
这可能变得难以管理。

我提出本地配置(在文件中)与全局配置( route.js )的最初想法是解决我的第一个评论中提到的特定情况(深嵌套的文件,它们是目录中的唯一文件,非语义文件名和全包参数)。

如果严格地在这些上下文中使用,那么它就不会那么混乱了,因为URL直接映射到文件系统,并且本地配置仅解决“额外”参数。

也就是说,我不确定我是否会尝试限制用户按其意愿进行操作。 我们可以将计算出的路由表漂亮地打印到控制台,甚至将其保存到某些预定文件中。 这应该足以帮助解决故障排除路线

@merelinguist我不相信Windows(如摘要表中所写)禁止= 。 您正在链接回如何禁止: ,但是根据Microsoft Windows文件命名文档,允许使用相同的字符。

我已经在生产中使用的项目中移植了动态路线(希望本周可以上线)。

但是,有一个特定的问题,新的

{ path: '/api/:customer', page: '/api/$customer/index.js' }

我刚刚在[email protected]尝试过,但

@remy尚未实现,它已列在我的清单中,以便尽快执行

我们还不仅应考虑Windows和Linux系统,还应考虑其他因素:
https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations

我想添加有关我的提案的更多信息:

如果我们向页面添加一些静态字段(如getInitialProps)怎么办?

// pages/blog.js
import {useRouter} from 'next/router'

function BlogPost() {
  const router = useRouter()
  // `blogId` will be `'how-to-use-dynamic-routes'` when rendering
  // `/blog/how-to-use-dynamic-routes`
  const blogId = router.query.id
  return <main>This is blog post {blogId}.</main>
}

// By default it would be as it is now
BlogPost.route = '/blog/:id';

export default BlogPost
  1. 开发人员无法将运行时变量用于该路由属性
const route = `/blog/${somethingElse}`;
BlogPost.route = route; // is not allowed
  1. 当我们使用当前的RFC构建页面清单时(文件夹中包含一些字符来标识它是动态的),如果我们通过读取文件并在页面上找到静态route属性来构建页面清单,则看不到任何区别。 lingui的工作方式相同:它们不允许Trans的id为动态
<Trans id="msg.docs" /* id can only be static string */>
   Read the <a href="https://lingui.js.org">documentation</a>
   for more info.
 </Trans>

通过已经列出的前缀@符号前缀?

我怀疑它是否有价值,但是您会与Nuxt持平,这意味着某人从一个或另一个切换到另一个将立即知道它是如何工作的。

另外,有人考虑过将前缀作为用户选项吗? 这使人们更难理解另一个项目,但这意味着如果我愿意,我可以使用前缀query__{...}或其他名称。

只是一个想法。

根据@remy的建议,为什么不完全打开Next解析来自文件系统的路由的API。 为用户提供所需的最大(或最小)灵活性,并激发可靠的第三方路由解决方案。

@now.json路由配置做通用路由与nextjs过这里

我希望Zeit小组也可以在客户端库上开源路由解析器。

看着Nuxt,我认为_id.js还不错。 是的,正如您提到的,我们已经使用_app_document.js ,并且不能公开路由。 但是动态路由也可以被视为不可路由,因为这是许多页面的模板

对于静态站点导出,如何处理?

(没关系)

我还认为,如果Next.js将生成的路由打印到单个文件(可能默认情况下是隐藏的),将很有帮助。 至少,它可以为从事项目工作的人提供有用的参考,但也可以为以后进行一些强大的动态路由打开方便之门。

即,如果它在运行时使用该文件进行路由处理,那么用户添加/更改路由(例如,进行复杂的模式匹配)非常容易,而又不会失去基于文件系统的API的好处。

这将给如何跟踪已手动更改的路线带来一些挑战,但是如果解决,我认为这将是迄今为止的最佳解决方案。

@ scf4 Next.js已经能够使用定制服务器选项执行复杂的路由。 使用现有工具,几乎可以用相同数量的代码来实现您的建议。

是的,很公平。

我认为无论如何,只有一个可以编辑的路由文件是一个更好的选择!

我写了一些关于使用文件系统进行路由的想法,但是可以在这里总结一下我的发现:

  • [param]似乎最安全(由Sapper使用)。
  • :对Express用户很熟悉,但是我可以_sworn_我在Windows FS上遇到问题。
  • ${param}用于Shell中的变量和括号扩展,因此在CLI中使用时可能会出现更多问题。
  • _ _可以工作,但是作为“私有”指示器太普遍了。

我个人在将文件( /^index\. )和黑名单( /^_/ )列入白名单方面有更好的经验,但这将是与/pages的向后兼容性问题。

通过最近的讨论来支持API路由(#7297),这可能是在/routes新家中同时支持/api/pages /routes

但是,尽管这是一个强大的“功能”,但Next.js生态系统足够大,可以增加_cremental_功能,而“嘿,如果我们不得不再次这样做,我们将以这种方式”进行设计。

zsh使用方括号( [example] )进行模式匹配,因此也不可行。

查看文件名生成中的示例

zsh使用方括号[]进行模式匹配,因此也不可行。

似乎他们只是在https://github.com/zeit/next.js/pull/7623中做到了

感谢您的注意。 我也在那里发表了评论。

我尝试了[id] ,只是在路径中使用它很痛苦(例如cd \[id\]/view.js )。 在我看来,双下划线__id (例如cd __id/view.js )也可以正常工作,并且可以与内部文件/文件夹(例如_app.js )区分开(稍微有点混乱)。

@AaronDDM您使用的是zsh吗? 您无需在bash中转义[]

是的,这对我来说也是zsh事情-与这些目录进行交互非常烦人。

$ mkdir [asdf]
zsh: no matches found: [asdf]
$ mkdir \[asdf\]
$ cd [asdf]
zsh: no matches found: [asdf]
$ cd \[asdf\]

而且由于zsh将成为macOS Catalina中的默认外壳程序,也许毕竟应该为此做些事情...

同意__id.js

嗯,真的不喜欢__ ,只是对我而言并不好。

@merelinguist em,Jest使用__tests__作为默认测试文件夹,我认为在某些情况下__是有意义的。

@YUFENGWANG也许,但是如果可能的话,我希望使用单个字符。 最终,我认为最好的解决方案是:

  1. 合理的跨平台默认值,例如=
  2. next.config.js来自定义使用的特殊路由字符
  3. 在哪些情况下哪些字符有问题的文档

同意使用单个字符,但我希望配置为零。 我的猜测是,即使您在文档中描述了许多问题,也会遇到很多人

还请注意, =由zsh保留。 从文档

如果单词以无引号“ =”开头并且设置了EQUALS选项,则该单词的其余部分将作为命令的名称。 如果使用该名称存在命令,则该单词将替换为该命令的完整路径名。

只是一个想法; 使用后缀呢? 例如[email protected]或类似内容就足够了。 只要字符有效,这可以解决必须在shell和文件系统之间进行转义和工作的问题。

到目前为止,它们可以在zsh和bash中运行而无需转义:

[email protected]
example~.js
example=.js

哦不是后缀,而是表示结尾URL参数的一种方法。

因此[email protected]变成blog/:id

compare@[email protected]变成compare/:a/:b

这可以解决上面我反对的深层嵌套的单个文件目录,并使整个路由定义文件系统保持基础。

它看起来不那么花哨,但类似于以下内容:

/blogs/_var_blog-id/index.js
/blogs/_var_blog-id.js

前缀_var_尝试模仿JS变量声明。 还是一定要超短,一个角色?

~字符怎么样?

/blogs/~id

使用~作为前缀也是不可行的,因为它用于在兼容POSIX的shell中扩展到主文件夹。

[0-9a-zA-Z-._] (regex)不匹配的任何字符都不能被视为跨操作系统,shell和文件系统的前缀安全。

某些字符也不是安全内联的。 查看有关替代的zsh

另外,我认为我们不应该为它的外观而努力,而是要直观,易读且易于交流。

  • [params].js使用方括号似乎更优雅,并且被广泛使用。 (使用者是nuxt v3吗?
  • 下划线前缀pages/_helper.js通常用于私有函数,也许不应渲染。 这使我们可以在pages文件夹中创建帮助器组件

恕我直言:这似乎是更大问题的临时解决方案。 尽管首先要有一个基于文件结构的路由,但是当您有数百个路由,参数等时,它的伸缩性就不好。拥有一个路由配置文件(每个目录中可能都有一个routes.js文件)是更好的长期解决方案。 我个人对nextjs很着迷,因为它具有开箱即用的功能(SSR,速度等),而不是从文件创建路由的便捷性。

@mmahalwy,您砸到了头。

Next.js已经生成了一个路由配置(基于文件系统)。 我相信,使此配置更加明确和/或允许用户“弹出”该配置(如果他们希望的话)将是这里最无缝的解决方案

@mmahalwy @ scf4 FWIW,文件系统路由的重要依据是消除对具有集中式文件的需要。 实际上,很容易有人争辩说,Next.js的用于链接和路由的API的全部就是围绕此约束而设计的。

路由配置的问题在于,最终您不得不将其发送给客户端,如果路由数量从数百条增加到数千条,这可能意味着相当庞大的代码束。

不过,也有不少共同的用例(据我已经能够告诉,从过去几个月中无数次的讨论与@timneutkens这个问题)真的不能没有一个集中的配置来解决。 我在较早的评论中列出了其中一些,但还有更多。

最简单的方法是创建一个由CMS驱动的博客,作者可以在其中创建指向网站页面的链接。 他们将仅使用简单的旧URL创建链接,而无需了解底层页面模块是什么。 使用集中式路由配置,可以很容易地反向匹配URL并确定要加载的页面(我自己的库, next-route-resolver旨在支持此用例,以及我提出的所有其他用例) 。

我看不到如何在没有路由配置的情况下使我正在工作的站点正常工作,因此我的重点一直是寻找方法,以将路由配置保持在文件大小公差范围内。 对于其他人来说,文件系统路由可能绰绰有余。 我认为只有一个解决方案可以解决所有问题的路由不是问题,而是平衡权衡。

因此,正如我之前提到的,就此提案而言,只要将其作为解决路由问题的解决方案出售就可以了,因为这会引起误解:)

@AndrewIngram我知道您来自哪里,但是这个限制限制了nextjs的功能。 Nextjs提供了很多开箱即用的功能,因此对于任何新项目或公司使用它来说都应该很容易。 但是,挑战在于,路由选择很难使它在将来变得无法使用(作为一家大公司,您总是在考虑项目失去兴趣或维护时的退出策略)。

@mmahalwy我认为您误解了我的观点。 我同意您的观点,我认为文件系统路由不足以解决已解决的路由问题,如果将其呈现为这样的话,将会感到失望。 我确实认为它可以为一组特定的用例提供改进,但是我也认为对于那些愿意采用其他折衷方案的人(例如您和我),也应该有某种路线清单格式。 。

对于那些希望进行集中式或高级路由配置的用户,使用定制服务器和/或外部软件包是否可以很好地处理? 您希望在这里添加什么?

这似乎与该RFC无关。 我认为,包括OP在内的任何人都没有建议这是路由的最终解决方案。 这只是基于文件系统的路由改进。

在过去的几周里,我一直在为一个小型项目使用动态路线(使用$尽管我注意到3天前在金丝雀仓库中它已移至[param] ,但无论如何)。

我_just_开始使用getRequestHandler ,我认为它没有在服务器端获取动态路由。

是一个错误,还是故意的(即对getRequestHandler一些更改),还是使用getRequestHandler完全关闭了动态路由(现在我想起来就有意义了……) ?

对于那些希望进行集中式或高级路由配置的用户,使用定制服务器和/或外部软件包是否可以很好地处理? 您希望在这里添加什么?

这里的目标之一是避免创建自定义服务器的需要,即使仅仅是为了使其更易于与Now(现在需要将所有动态路由作为其配置的一部分)之类的服务一起使用。

这似乎与该RFC无关。 我认为,包括OP在内的任何人都没有建议这是路由的最终解决方案。 这只是基于文件系统的路由改进。

实际上,这里还有一些其他上下文。 这项提案已经进行了很长时间,根据我所见过的与之相关的许多讨论(包括我直接参与的讨论),在某种程度上,由于不再需要使用这些方法,因此有人对其进行了大肆宣传。管理库,例如下一条路线和我自己的路线。 我认为强调该RFC不能满足的用例

FWIW我们在Pinterest使用[param]样式基于FS的路线(尽管不是Next)。 到目前为止,它的缩放比例非常好。 最大的批评是Jest将[]为正则表达式对,因此很难将测试定向到参数处理程序。

@chrislloyd考虑到有人在使用zsh或以不同方式解释这些内容的工具,您在不同环境中使用这种格式在路径/文件中使用此格式创建和管理文件有什么经验?

好像[]用于zsh中的模式匹配(并且,正如您对Jest所说),您将需要转义这些路径。 如果您知道这一点,这并不是什么大问题,但是鉴于初学者应该可以使用和理解它,因此我怀疑这是正确的格式。

我有一个使用!作为必需参数的想法,例如/pages/id!.js?作为可选参数,例如在/pages/posts/id?.js

像上面的讨论一样,它对前缀没有任何问题,并且对!表示必需的参数以及?表示可选参数很熟悉。

Windows不允许在文件名中使用问号,并且两者都不允许? 和! 在Bash中有特殊含义。

API路线现在支持动态参数#7629🚀

@remy getRequestHandler应该可以处理动态路由-我刚刚在本地确认了。 您能否提出一个单独的bug /问题并进行复制,以便我们进行调查? :祈祷:

嗨,大家好! 感谢您对此RFC的不可思议的回应。

此RFC已在Next.js 9中实现并稳定发布。
您可以在博客文章中阅读有关它的更多

将来,我们将发布新的RFC,以解决此处提供的所有高级反馈。 我们将在此处发布更新。

此页面是否有帮助?
0 / 5 - 0 等级