Next.js: [RFC] Rotas Dinâmicas

Criado em 19 jun. 2019  ·  90Comentários  ·  Fonte: vercel/next.js

Rotas Dinâmicas

fundo

O roteamento dinâmico (também conhecido como URL Slugs ou Pretty / Clean URLs) é um recurso solicitado há muito tempo do Next.js.

As soluções atuais envolvem colocar um proxy L7 , servidor customizado ou middleware de usuário na frente de seu aplicativo. Nenhuma dessas soluções oferece uma experiência de desenvolvedor suficientemente _ergonômica_.

Além disso, os usuários que buscam um servidor personalizado inadvertidamente optam por não receber recursos avançados de nível de estrutura, como funções sem servidor por página.

Metas

  1. Aproveite a convenção para fornecer suporte a URL Slug que seja fácil de raciocinar sobre
  2. Abrange a maioria dos casos de uso observados na natureza
  3. Elimine a necessidade de um servidor personalizado para oferecer suporte a /blog/:post
  4. Valide <Link /> transições de rota quando possível
  5. Evite uma implementação que requer um manifesto de rota
  6. As rotas devem ser expressas por meio do sistema de arquivos

Proposta

Next.js deve oferecer suporte a parâmetros de URL nomeados que correspondam a um segmento de URL inteiro . Essas rotas seriam expressas por meio do sistema de arquivos:

  1. Um nome de arquivo ou diretório que está envolvido com [] seria considerado um parâmetro nomeado
  2. Segmentos de rota explícitos teriam prioridade sobre segmentos dinâmicos, combinados da esquerda para a direita
  3. Parâmetros de rota seriam obrigatórios , nunca opcionais
  4. Os parâmetros de rota serão mesclados no objeto query (acessível por getInitialProps ou router por meio de withRouter ) - esses parâmetros não podem ser substituídos por um parâmetro de consulta

Para ajudar a entender esta proposta, vamos examinar a seguinte árvore de arquivos:

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

Next.js produziria as seguintes rotas, registradas na seguinte ordem:

;[
  { 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' },
]

Exemplos de uso

Todos esses exemplos pressupõem uma página com o nome de arquivo pages/blog/[id].js :

Navegando para a página com <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>

O exemplo acima fará a transição para a página /blog/[id].js e fornecerá o seguinte objeto query ao _Router_:

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

Lendo parâmetros nomeados de _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

Nota: você também pode usar withRouter .

Lendo parâmetros nomeados em 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

Ressalvas

Os parâmetros de rota opcionais não são expressos por meio do sistema de arquivos.

Você pode emular um parâmetro de rota opcional criando uma página de stub que exporta a versão do parâmetro (ou vice-versa). Isso aumenta a visibilidade das rotas do seu aplicativo ao inspecionar o sistema de arquivos.

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

Parâmetros nomeados não podem aparecer no meio de um nome de rota.

Isso significa que uma página chamada blog-[id].js seria interpretada _literalmente_ e não correspondida por /blog-1 . Você pode reestruturar sua página para /blog/[id].js ou transformar todo o segmento de URL em um parâmetro nomeado e lidar com a remoção de blog- no código do seu aplicativo.

Alternativas

Denote URL Slugs com _inserir símbolo aqui_ em vez de []

Existem poucos símbolos disponíveis para uso para representar um parâmetro nomeado no sistema de arquivos. Infelizmente, a maneira mais reconhecida de definir um parâmetro nomeado ( :name ) não é

Durante o levantamento da técnica anterior, os símbolos mais comuns usados ​​para denotar um parâmetro foram _ , $ e [] .

Excluímos _ porque _ é tipicamente indicativo de uma rota interna que não é publicamente roteável (por exemplo, _app , _document , /_src , /_logs ).
Também descartamos $ porque é um sigilo no bash para expansão de parâmetro.

Aproveite path-to-regexp para um suporte abrangente

A maioria dos símbolos necessários para expressar regex não são

No futuro, podemos permitir path-to-regexp rotas definidas em next.config.js ou similar. Atualmente, isso está fora do escopo desta proposta.

Exploração Futura

Parâmetros pega-tudo

No futuro, podemos considerar a adição de parâmetros abrangentes. Com o que sabemos até agora, esses parâmetros devem estar no final da URL e potencialmente usariam % para denotar uma rota pega-tudo (por exemplo, pages/website-builder/[customerName]/%.tsx ).

Comentários muito úteis

Enquete : Para expressar interesse em parâmetros opcionais , reaja com um "+1" neste comentário.

Nota : Parâmetros opcionais já são possíveis com este RFC, eles apenas não têm uma sintaxe explícita (consulte a seção Advertências ).

Todos 90 comentários

Enquete : Para expressar interesse em parâmetros opcionais , reaja com um "+1" neste comentário.

Nota : Parâmetros opcionais já são possíveis com este RFC, eles apenas não têm uma sintaxe explícita (consulte a seção Advertências ).

Enquete : para expressar interesse em parâmetros abrangentes , reaja com um "+1" neste comentário.

Nota : Compartilhe seu caso de uso para parâmetros abrangentes neste tópico! Adoraríamos entender melhor o espaço-problema.

reservado 3

Em ricardo.ch, usamos um prefixo de local para cada rota, o que torna o roteamento um pouco mais complexo.

Exemplo de rotas válidas:

  • / - página inicial com localidade detectada automaticamente
  • /:locale - página inicial com localidade forçada
  • /:locale/search - página de pesquisa
  • /:locale/article/:id - página do artigo

Você acha que esses parâmetros de prefixo podem ser suportados?

No momento, usamos https://www.npmjs.com/package/next-routes

Outra coisa: para a página do artigo, também suportamos um slug antes do id como /de/article/example-article-123 onde o id seria 123. Isso é feito por meio de uma regex bastante complexa usando next-routes e eu não veja como isso pode ser expresso com uma API de sistema de arquivos.

@ValentinH as rotas fornecidas são possíveis usando a API do sistema de arquivos - de acordo com as rotas fornecidas:

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

também suportamos um slug antes do id como / de / article / example-article-123 onde o id seria 123

Este caso de uso é abordado acima:

Parâmetros nomeados não podem aparecer no meio de um nome de rota.

Isso significa que uma página chamada blog-$id.js seria interpretada literalmente e não correspondida por /blog-1 . Você pode reestruturar suas páginas para /blog/$id.js ou transformar todo o segmento de URL em um parâmetro nomeado e lidar com a remoção de blog- no código do seu aplicativo.

Esta solução não atende às suas necessidades? Adoraríamos saber mais sobre seus requisitos específicos.

Muito obrigado pela resposta.

Não pensei em usar $locale/index.js como pasta e como arquivo, isso é muito legal!

Em relação ao "parâmetro nomeado no meio", ignorei porque achei que ter o slug dinâmico era diferente. No entanto, você está absolutamente certo e isso é abordado pelo parágrafo que você mencionou. Listrar o slug no código do aplicativo será o caminho a percorrer 🙂

Algo como isso (analisar parâmetros de .hidden .files / .folders) seria possível?

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

ou deixe $ para que se encontrem seus arquivos: D mas sempre use $ folder para indicar um parâmetro?

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

Eu costumava ter esse caso de uso para parâmetros opcionais em um aplicativo que funcionava com pacotes npm. Eles podem, opcionalmente, ter um escopo. Existem rotas como:

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

Então, basicamente, o parâmetro de escopo é opcional, mas também é apenas um escopo quando começa com @ .
Portanto, /packages/express/dependencies e /packages/@babel/core têm a mesma quantidade de segmentos, mas em um caso é /dependencies de express e no outro é /index de @babel/core .

No final foi resolvido em react-router com as seguintes rotas:

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

Não tenho certeza se vejo uma solução para esse caso de uso neste RFC.

Quanto aos casos de uso abrangentes, estou pensando em qualquer link profundo em dados aninhados recursivamente, como estruturas de pasta, visualizações em árvore, mapas de árvore.

Meus 2 centavos: cifrões em nomes de arquivos são uma má ideia porque são usados ​​por conchas como um sigilo. Você vai confundir as pessoas tentando executar rm $root.js . Os sublinhados parecem uma alternativa decente.

De forma mais ampla: como muitas pessoas, tentei alavancar o sistema de arquivos como uma solução para isso no passado. Em última análise, acho que o sistema de arquivos nunca oferecerá a expressividade total que você procura. Por exemplo, roteadores declarativos geralmente permitem que você especifique um padrão de validação para um parâmetro dinâmico. Nesse caso, parte do esquema reside no sistema de arquivos e outra parte no código. A separação de interesses é uma coisa boa, mas, neste caso, é uma limitação técnica mais do que qualquer outra coisa.

Como @ValentinH , usamos $ locale var, mas é opcional.

Devemos usar /page.ts e /page/$locale/page.ts?

Como podemos usar uma localidade "padrão" ou uma localidade predefinida (configurações do usuário), nesses casos não usamos o parâmetro $ locale.

Mas temos mais casos de uso: / car / search / $ optional-filter-1 / $ optional-filter-2 / $ optional-filter-3

Onde optional-filter-1: color-red, optional-filter-2: brand-ford, etc ...

E para parâmetros opcionais, algo como / $ required-param / e / $$ optional-param /?

É incrível que isso esteja aparecendo no roteiro!

Eu tenho que gritar para apoiar touch $file isso causará muita confusão. Você precisa se lembrar de como escapar a cada interação. touch \$file; vim $file abrirá o vim sem um arquivo (porque $ file não é uma variável definida).
Da mesma forma, o preenchimento da guia em um shell listará todas as variáveis, mais uma vez trazendo confusão.

Estou propondo duas alternativas que acho que dão as associações certas e deveriam funcionar em shells:

  • = Pode ser lido como page is a customer para =customer . Você pode até mesmo contorcê-lo mentalmente até ficar com dois pontos esticados, assemelhando-se à forma mais comum de parâmetros nomeados.
  • @ porque também é lido bem. a customer para @customer

Outra opção seria usar chaves (a menos que sejam caracteres reservados em alguns sistemas de arquivos). Esta sintaxe de parâmetro também é "técnica anterior" e é usada por muitos outros roteadores:

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

Isso permitiria ter parâmetros no meio do segmento de rota e vários parâmetros por segmento, pois é claro onde o parâmetro começa e onde termina, por exemplo, /product-{productId}-{productColor} .

Estou tão animado que as rotas dinâmicas estão chegando ao Next.js

Com relação à sintaxe para parâmetros nomeados, isso é algo que foi discutido no Spectrum: https://spectrum.chat/next-js/general/rfc-move-parameterized-routing-to-the-file-system~ce289c5e-ff66 -4a5b-8e49-08548adfa9c7. Pode valer a pena usar isso como entrada para a discussão aqui. Pessoalmente, gosto de como o Sapper está fazendo isso usando [brackets] . Isso também é algo que a Nuxt implementará na versão 3. Ter diferentes frameworks usando o mesmo formato para rotas baseadas em sistema de arquivos dinâmico parece uma coisa boa.

Com relação ao uso de <Link /> , acho que os desenvolvedores se esquecerão facilmente de definir os atributos href e as . Eu entendo que não é possível "mesclar" isso no atributo href porque isso introduziria uma alteração significativa, mas sinto que poderia ser resolvido de uma forma mais elegante.

Os colchetes são infelizmente usados ​​pelo Bash para agrupar comandos.

Eu concordo com @ stephan281094 em relação ao uso de <Link /> , será fonte de erros.

O roteamento dinâmico é um recurso extremamente útil, então é realmente incrível que vocês tenham investigado e encontrado uma solução, adereços enormes!

Ainda neste tópico, as rotas curinga também seriam uma adição valiosa à proposta. Você mencionou os parâmetros abrangentes como algo a ser investigado no futuro, mas não abrange casos em que você pode querer fazer algo como /category/* , que pode ter um número N de níveis, e você quer todos para renderizar a página category .

É possível usar : com segurança? Se sim, esse seria o meu voto, pois todos já estão familiarizados com essa convenção pelo expresso.

Devido ao conflito de $ com as variáveis ​​do shell, eu pessoalmente me oponho fortemente.

É possível usar : com segurança? Se sim, esse seria o meu voto, pois todos já estão familiarizados com essa convenção pelo expresso.

Aparentemente : é um caractere proibido no Windows, então provavelmente não é seguro. Indo com _ também não é o ideal, pois sublinhados podem ser usados ​​em URLs. Acho que [brackets] é uma boa solução porque é mais à prova de futuro. Se Next.js deseja oferecer suporte a rotas como post-12345 no futuro, usando essa sintaxe, isso pode ser feito sem a introdução de uma alteração significativa.

Portanto, uma lista de personagens a evitar seria:

  • Conflitos com sistemas de arquivos: : , * , " , < , > , |
  • Conflitos com variáveis ​​de shell: $
  • Conflitos com a expansão da chave bash { , }

Algo mais?

Isso não eliminaria nossa necessidade de ter um arquivo de rota centralizado por alguns motivos:

  • Temos um mapa do site gerado automaticamente e o sistema de arquivos sozinho não é suficiente para defini-lo.
  • Usamos rotas nomeadas e as "páginas" de destino são determinadas por dados, em vez de algo que pode ser conhecido no momento da criação. A lógica para descobrir qual página carregar com base no nome e parâmetros é orientada pela configuração da rota.

Também geramos nossa pasta de páginas por estes motivos:

  • Usamos Relay, e isso significa que os módulos que envolvem GraphQL precisam ter nomes exclusivos. Por essa razão, muitas vezes não podemos ter os nomes dos segmentos de rota iguais aos nomes dos módulos. index.js definitivamente não é único, e vejo lugares onde teríamos vários segmentos comuns, como edit .
  • Preferimos co-localizar componentes específicos de página como irmãos dos próprios módulos de página, o que Next.js não permite dentro da pasta de páginas.

Essencialmente, nosso padrão é usar nossa configuração de rota centralizada para gerar nossa pasta de páginas, que contém arquivos que não fazem nada mais do que importar / exportar módulos de outro lugar na base de código.

Para esse fim, meu foco é mais em saber se esta proposta pode funcionar simplesmente como um formato de saída aprimorado para nosso processo de geração de página existente, para que possamos pelo menos obter o benefício de não precisar de um servidor personalizado.

Examinei alguns dos meus casos de uso em outro lugar: https://gist.github.com/AndrewIngram/8d4c4ccd9bd10415a375caacade9f5ca

A principal coisa que não estou vendo é o suporte a parâmetros implícitos que não são expressos no sistema de arquivos, por exemplo, substituições de URL.

Digamos que temos um URL como este:

/some-vanity-url/

Nos termos atuais do Next.js, gostaríamos que ele fosse mapeado para uma página de produto com vários parâmetros de consulta, por exemplo, Product.js?id=foo&language=en .

Da mesma forma, em nosso website, a maioria dos "sites" de países tem como escopo um segmento de nível superior, por exemplo, es ou ie , mas o site gb é montado sem esse segmento. Isso significa que todas as páginas gb têm um parâmetro country implícito, enquanto para todos os outros países é explícito.

A outra desvantagem é que, como em nosso caso, a mesma 'página' pode existir em vários pontos de montagem na arquitetura de URL, vamos acabar com um número maior de pacotes (ou seja, vários pontos de entrada duplicados) do que realmente precisa na prática.

No geral, esta proposta parece funcionar bem para a maioria dos casos de uso comuns, mas não elimina a necessidade de uma configuração de rota ou servidor personalizado em _todos_ os casos. Mas presumindo que isso não substitua minha capacidade de usar a estrutura da maneira que faço hoje, não tenho nenhuma objeção real de que esta seja a API preferida do happy-path.

Eu apoio a sugestão de {id} . Ele permite vários parâmetros e acho que parece muito melhor. Também se ajusta melhor ao React.

Sou a favor do personagem file/&param.js . Retirado diretamente de urls e não parece que está em conflito com sistemas de arquivos ou bash.

Eu usaria _ e talvez permitiria uma substituição em next.config.js para aqueles que realmente precisam de algo diferente.

Aprecie o trabalho nisso. Estou querendo isso há um tempo! ❤️

Surpreendente! 🎉🎉🎉

Meu único problema aqui é que Link precisa de href e as params.

Acredito que poderíamos escrever <Link to="blog/123" /> : como Nextjs já conhece todas as rotas com base nos arquivos da pasta pages, ele poderia facilmente traduzi-lo em "/blog/$id" .

Portanto, uma lista de personagens a evitar seria:

& é um operador de controle em bash que executa o lado esquerdo do argumento em um subshell assíncrono. Texto simples: open pages/&customer executaria open pages/ no plano de fundo e o comando customer no shell do primeiro plano.

Isso parece muito legal.

Parece que isso criará um número significativo de diretórios de arquivo único (como /blog/$id no exemplo original). Isso fica ainda mais complicado se você quiser dois parâmetros de rota finais (ou seja, /git/compare/$hash1/$hash2 ).

Também não adoro que o nome do arquivo para rasgar uma postagem de blog seja $id.js . Ter o nome de blog.js seria muito mais descritivo.

Talvez combinar com um decorador @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

Isso parece fornecer uma solução mais limpa para os parâmetros abrangentes propostos também.

Decoradores não podem ser aplicados a funções (talvez isso tenha mudado desde a última vez que li?) E a proposta provavelmente está muito longe de qualquer maneira

Bem, suponha que você vá por esse caminho, provavelmente faria da maneira como o AMP está configurado agora:

// /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
};

No entanto, acho que coisas como essa podem ser adicionadas mais tarde, se parecer útil em algum momento. Acho que prefiro ver um suporte básico para começar, assim como está descrito no RFC. Obtenha algum uso real com isso e, em seguida, refine onde quebra. Também acho que os únicos caracteres que devem ser levados em consideração para evitar são os do sistema de arquivos. Esses são os verdadeiros bloqueadores para a construção desse recurso.

Por favor, certifique-se de usar um personagem que seja amigável com soluções sem servidor! (No Aws, existem alguns personagens que podem causar problemas)

Exportar um objeto de configuração com uma chave de componente é algo que não odeio.

Você também pode usar apenas um HOC

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

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

e se adicionarmos algum campo estático à página (como 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 O que acontece quando você navega para /blog no navegador? A 404?

Como isso precisa ser determinado em tempo de compilação, acho que você precisa de algo que seja estaticamente analisável. um HOC ou uma propriedade estática não seria.

você precisaria de algo que fosse estaticamente analisável. um HOC ou uma propriedade estática não seria

Cada exemplo de propriedade estática fornecido até agora seria estaticamente analisável (embora você certamente pudesse quebrar as coisas facilmente). Poderíamos apenas insistir para que você exporte sua função e defina a propriedade da rota de uma forma estaticamente analisável. O tempo de execução pode verificar as propriedades de rota que são definidas no tempo de execução, mas não foram detectadas por nosso analisador estático e emitir um aviso / lançar um erro.

O que acontece quando você navega para / blog no navegador? A 404?

@kingdaro - IMO, sim. Se você quiser usar os caminhos /blog e /blog/:blogId , use um diretório. Você está sobrecarregando esse caminho, então a estrutura do diretório é justificada.

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

Bem, suponha que você vá por esse caminho, provavelmente faria da maneira como o AMP está configurado agora:

// /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
};

No entanto, acho que coisas como essa podem ser adicionadas mais tarde, se parecer útil em algum momento. Acho que prefiro ver um suporte básico para começar, assim como está descrito no RFC. Obtenha algum uso real com isso e, em seguida, refine onde quebra. Também acho que os únicos caracteres que devem ser levados em consideração para evitar são os do sistema de arquivos. Esses são os verdadeiros bloqueadores para a construção desse recurso.

Acho que usar a configuração é uma má ideia porque você precisa passar por vários arquivos para ver o que é realmente dinâmico. Se você configurá-lo no sistema de arquivos, poderá vê-lo à primeira vista.

Eu me pergunto se mais de uma solução de roteamento padrão deve ser algo a se considerar.

O roteamento simples baseado em arquivo é um ótimo ponto de venda para aqueles que são novos no Next / React ou para qualquer pessoa que deseja colocar um aplicativo simples em funcionamento, mas pode ser bastante limitante. E me parece que tentar encaixar o roteamento dinâmico nesse padrão pode arruinar essa simplicidade e levar a uma complexidade desnecessária, tudo em nome de manter tudo baseado em arquivo.

Depois de ler esta discussão e pensar sobre meu próprio uso do Next.js, acho que o suporte de primeira classe para um sistema de roteamento alternativo (suplementar) poderia ser a melhor maneira de resolver isso.

Gosto de algumas ideias inovadoras neste tópico (como a proposta de usar decoradores), mas essas ideias definitivamente têm seus próprios problemas. Espero que possamos encontrar algo ótimo 👍

Exportar um objeto de configuração com uma chave de componente é algo que não odeio.

Você também pode usar apenas um HOC

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

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

Isso é muito legal, mas eu me pergunto se ter informações de rota divididas em muitos arquivos como
isso pode se tornar difícil de gerenciar.

Meu pensamento original ao propor uma configuração local (no arquivo) versus global ( route.js ), foi abordar os cenários específicos mencionados no meu primeiro comentário (arquivos profundamente aninhados que são o único arquivo em seu diretório, nomes de arquivos não semânticos e parâmetros genéricos).

Se usado estritamente nesses contextos, é muito menos confuso, porque a URL mapeia diretamente no sistema de arquivos e apenas parâmetros "extras" são endereçados pela configuração local.

Dito isso, não tenho certeza se tentaria impedir os usuários de fazer isso da maneira que desejassem. Podemos imprimir a tabela de roteamento calculada no console ou mesmo salvá-la em algum arquivo predeterminado. Isso deve ser o suficiente para ajudar na solução de problemas de rotas

@merelinguist Não acredito que = seja proibido no Windows, conforme você escreveu na tabela de resumo. Você está vinculando de volta a como : é proibido, mas de acordo com os documentos de nomenclatura de arquivos do Microsoft Windows, o caractere igual é permitido.

Já estou portando com rotas dinâmicas em um projeto que uso em produção (espero que consiga colocá-lo ao vivo esta semana).

No entanto, uma questão específica: o novo recurso da API do próximo @ canary _also_ oferecerá suporte ao roteamento dinâmico?

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

Acabei de tentar com [email protected] e recebo um 404 não encontrado, então suspeito que ainda não esteja lá. Parece que faz sentido que esses dois recursos (API + rotas dinâmicas) tenham paridade no roteamento de URL.

@remy ainda não foi implementado está na minha lista para fazê-lo em breve

Também devemos levar em consideração não apenas os sistemas Windows e Linux, mas também outros:
https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations

Eu gostaria de adicionar mais informações sobre minha proposta:

e se adicionarmos algum campo estático à página (como 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. O desenvolvedor não pode usar a variável de tempo de execução para essa propriedade de rota
const route = `/blog/${somethingElse}`;
BlogPost.route = route; // is not allowed
  1. Quando construímos o manifesto da página com este RFC atual (onde a pasta contém algum caractere para identificá-lo é dinâmico), não vejo diferença se construirmos este manifesto da página lendo o arquivo e encontrarmos a propriedade de rota estática na página. Da mesma forma que o idioma funciona: eles não permitem que o id para Trans seja dinâmico
<Trans id="msg.docs" /* id can only be static string */>
   Read the <a href="https://lingui.js.org">documentation</a>
   for more info.
 </Trans>

Indo pela lista de prefixos já listados - eu me pergunto se há alguma razão forte _não_ para usar um prefixo de símbolo @ ?

Duvido que tenha valor, mas você obtém paridade com Nuxt - o que significa que alguém que muda de um ou de outro saberá imediatamente como funciona.

Como alternativa, alguém já pensou em tornar o prefixo uma opção do usuário? Torna mais difícil para as pessoas entenderem um projeto de outro, mas significa que, se eu quiser, posso criar o prefixo query__{...} ou algo assim.

Apenas um pensamento.

Seguindo a sugestão de

@ scf4 Eu tinha uma biblioteca que é um PoC, que usa now.json routes config para fazer roteamento universal com nextjs também aqui

Espero que a equipe do Zeit também abra o analisador de rotas na biblioteca do lado do cliente.

Olhando para Nuxt, acho que _id.js não é tão ruim. Sim, já usamos _app e _document.js como você mencionou e não é roteável publicamente. Mas uma rota dinâmica também pode ser vista como não roteável, pois é um modelo para muitas páginas

Como isso seria tratado para exportações de sites estáticos?

(Esqueça este aqui)

Também acho que seria útil se Next.js imprimisse as rotas geradas em um único arquivo (talvez oculto por padrão). No mínimo, serviria como uma referência útil para as pessoas que trabalham em um projeto, mas também poderia abrir a porta para algum roteamento dinâmico poderoso mais tarde.

Ou seja, se ele usa esse arquivo para manipulação de rota em tempo de execução, seria muito fácil para os usuários adicionar / alterar rotas (por exemplo, para correspondência de padrões complexos) sem perder os benefícios da API baseada no sistema de arquivos.

Isso criaria alguns desafios em relação a como controlar as rotas que foram alteradas manualmente, mas se resolvido, acho que seria a melhor solução de longe.

@ scf4 Next.js já tem a capacidade de fazer rotas complexas usando a opção de servidor personalizado. O que você está propondo é alcançado em quase a mesma quantidade de código com as ferramentas já disponíveis.

Ah sim, é justo.

Acho que ter um único arquivo de rotas que pode ser editado é uma opção muito melhor de qualquer maneira!

Eu escrevi algumas idéias sobre roteamento com o sistema de arquivos , mas posso resumir minhas descobertas aqui:

  • [param] parece mais seguro (e é usado pelo Sapper).
  • : é familiar para os usuários do Express, mas eu poderia _jurar_ que tive problemas no Windows FS.
  • $ e {param} são usados ​​para expansão de variáveis ​​e chaves em shells, então isso pode ser mais problemático quando na CLI.
  • _ _poderia_ funcionar, mas é muito comum como um indicador "privado".

Eu pessoalmente tive experiências melhores com arquivos de listas brancas para rotas ( /^index\. ) em comparação com uma lista negra ( /^_/ ), mas isso seria um problema de compatibilidade com versões anteriores de /pages .

Com discussões recentes para oferecer suporte a rotas de API (# 7297), esta poderia ser uma oportunidade para oferecer suporte a /api e /pages ambos sob a nova casa de /routes .

No entanto, _e é um forte "porém" _, o ecossistema Next.js é grande o suficiente para garantir adições de recursos _incrementais_, em vez de um "ei, se tivéssemos que fazer isso novamente, faríamos design _ desta__ maneira".

Colchetes ( [example] ) são usados ​​por zsh para correspondência de padrões, então isso também não seria viável.

Veja exemplos em Geração de nome de arquivo

Os colchetes [] são usados ​​pelo zsh para correspondência de padrões, então isso também não seria viável.

Parece que eles conseguiram em https://github.com/zeit/next.js/pull/7623

Obrigado pelo aviso. Eu postei um comentário lá também.

Eu tentei [id] e apenas usá-lo em caminhos é uma dor (por exemplo, cd \[id\]/view.js ). Parece-me que sublinhados duplos __id (por exemplo, cd __id/view.js ) funcionam tão bem e podem ser distinguidos (albite, talvez um pouco confuso ainda) de arquivos / pastas internos (por exemplo, _app.js ).

@AaronDDM você está usando zsh ? Você não precisa escapar de [ ou ] no bash.

Sim, isso também acontece comigo com zsh - super irritante interagir com esses diretórios.

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

E como zsh se tornará o shell padrão no macOS Catalina , talvez algo deva ser feito sobre isso, afinal ...

concordo com __id.js

Hm, realmente não amo o __ , apenas não parece ótimo para mim.

@merelinguist em, Jest use __tests__ para a pasta de teste padrão, eu acho que __ faz sentido em alguns casos.

@YUFENGWANG Talvez, mas prefiro um único caractere, se possível. Em última análise, acho que a melhor solução seria:

  1. Padrão sensato, multiplataforma, como =
  2. Opção em next.config.js para personalizar o caractere de rota especial que é usado
  3. Documentação sobre quais personagens são problemáticos em quais situações

Concordo com um único personagem, mas prefiro ter uma configuração zero. e meu palpite é que muitas pessoas passarão por todos os problemas, mesmo que você os descreva em uma documentação

Observe também que = é reservado por zsh. Dos documentos :

Se uma palavra começar com '=' sem aspas e a opção EQUALS for definida, o restante da palavra será considerado o nome de um comando. Se existir um comando com esse nome, a palavra será substituída pelo nome do caminho completo do comando.

Apenas uma ideia; que tal usar um sufixo? Por exemplo [email protected] ou algo semelhante pode ser suficiente. Isso pode resolver o problema de ter que escapar e trabalhar em shells e sistemas de arquivos, desde que o caractere seja válido.

Eles funcionam em zsh e bash sem a necessidade de escapar, até agora:

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

Ooh. Não é um sufixo, mas uma forma de denotar parâmetros de URL finais.

Portanto, [email protected] se torna blog/:id .

compare@[email protected] torna-se compare/:a/:b .

Isso poderia resolver os diretórios de arquivo único profundamente aninhados que eu estava objetando acima e manter todo o sistema de arquivos de definição de roteamento baseado.

Não parece tão chique, mas que tal algo como:

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

um prefixo _var_ Que tipo de tenta imitar as declarações de variáveis ​​JS. Ou tem que ser supercurto, de um personagem?

Que tal ~ caractere?

Como /blogs/~id .

Usar ~ como prefixo também não é viável, pois é usado para expandir para a pasta inicial em shells compatíveis com POSIX.

Qualquer caractere que não corresponda a [0-9a-zA-Z-._] (regex) não pode ser considerado seguro como um prefixo em sistemas operacionais, shells e sistemas de arquivos.

Alguns caracteres também não são seguros em linha. Veja a documentação de zsh

Além disso, acho que não devemos nos esforçar para parecer sofisticado, mas sim ser intuitivo, legível e fácil de comunicar.

  • usar colchetes para [params].js parece mais elegante e amplamente usado. (sapador, nuxt v3?).
  • o prefixo de sublinhado pages/_helper.js geralmente para uma função privada e talvez não deva ser renderizado. isso nos permite criar componentes auxiliares dentro da pasta de páginas

imho: parece uma solução temporária para o problema maior. Embora seja muito bom ter rotas com base na estrutura de arquivos para começar, ele não se ajusta bem quando você tem centenas de rotas, parâmetros, etc. Ter um arquivo de configuração de rotas (talvez tenha um arquivo routes.js em cada diretório) é a melhor solução de longo prazo. Sinto-me pessoalmente atraído pelo nextjs por causa dos recursos prontos para uso (SSR, velocidade etc.) que ele oferece, não pela facilidade de criar rotas a partir de arquivos.

@mmahalwy você acertou em

Next.js já gera uma configuração de rotas (baseada no sistema de arquivos). Eu acredito que tornar esta configuração mais explícita e / ou permitir que o usuário "ejete" se desejar seria a solução mais perfeita aqui

@mmahalwy @ scf4 FWIW, uma justificativa significativa para as rotas do sistema de arquivos é remover a necessidade de um arquivo centralizado. Na verdade, pode-se facilmente argumentar que toda a API do Next.js para links e roteamento é projetada em torno dessa restrição.

O problema com uma configuração de rota é que você acaba tendo que enviá-la para o cliente, o que pode significar um pacote de código bastante pesado se você tiver rotas numerando de centenas a milhares.

No entanto, existem alguns casos de uso comuns que (pelo que pude perceber, ao discutir esse problema com @timneutkens inúmeras vezes nos últimos meses) não podem ser resolvidos sem uma configuração centralizada. Listei alguns deles em meu comentário anterior, mas há mais.

O mais simples é ter um blog controlado por CMS, onde os autores podem criar links para páginas do site. Eles estarão apenas criando links com um URL simples e antigo, sem nenhum conhecimento do que é o módulo de página subjacente. Com uma configuração de rota centralizada, é muito fácil fazer a correspondência reversa de um URL e descobrir qual página carregar (minha própria biblioteca, o resolvedor de próxima rota foi projetado para oferecer suporte a esses casos de uso e todos os outros que criei) .

Não vejo como posso fazer o site em que estou trabalhando funcionar sem uma configuração de rota, então meu foco tem sido apenas encontrar maneiras de manter a configuração de rota dentro das tolerâncias de tamanho de arquivo. Para outras pessoas, o roteamento do sistema de arquivos pode ser mais do que suficiente. Não acho que o roteamento seja um problema em que haja uma única solução que resolve tudo, é tudo uma questão de balanceamento de compensações.

Como mencionei antes, no que diz respeito a esta proposta, parece bom, desde que seja vendida como uma solução completa para o problema de roteamento, porque isso seria um pouco enganador :)

@AndrewIngram Eu entendo de onde você está vindo, mas esta limitação está limitando o poder que nextjs tem. Nextjs oferece tanto fora da caixa que deveria ser um acéfalo para qualquer novo projeto ou empresa usá-lo. O desafio, porém, é que é difícil ter uma opinião sobre o roteamento que o torna impossível no futuro (e como uma grande empresa, você está sempre considerando a estratégia de saída caso os projetos percam interesse ou manutenção).

@mmahalwy Acho que você entendeu mal meu ponto. Estou de acordo com você, não acho que o roteamento do sistema de arquivos seja suficiente para resolver o problema de roteamento e ficaria desapontado se fosse apresentado como tal. Acho que oferece uma melhoria para um determinado conjunto de casos de uso, mas também acho que deve haver algum tipo de formato de manifesto de rota para aqueles que desejam aderir a um conjunto diferente de trocas (por exemplo, você e eu) .

Para aqueles que desejam uma configuração de roteamento centralizada ou avançada, ela não é bem tratada usando o servidor personalizado e / ou pacotes externos? O que você espera que seja adicionado aqui?

Tudo parece fora do tópico deste RFC. Não creio que ninguém, incluindo o OP, tenha sugerido que essa é a solução definitiva para o roteamento. Isso apenas melhora o roteamento baseado no sistema de arquivos.

Tenho usado as rotas dinâmicas para um miniprojeto nas últimas semanas (usando $ embora observe que ele foi movido para [param] 3 dias atrás no repo canário, mas de qualquer maneira).

Eu _já_ comecei a usar getRequestHandler e acho que não está pegando o roteamento dinâmico no lado do servidor.

Isso é um bug, ou intencional (ou seja, alguma mudança em getRequestHandler ), outra coisa, ou usar getRequestHandler desativa completamente o roteamento dinâmico (o que faria sentido agora que penso nisso ...) ?

Para aqueles que desejam uma configuração de roteamento centralizada ou avançada, ela não é bem tratada usando o servidor personalizado e / ou pacotes externos? O que você espera que seja adicionado aqui?

Um dos objetivos aqui é evitar a necessidade de criar um servidor personalizado, nem que seja para torná-lo mais fácil de usar com serviços como o Now (que atualmente requer que todas as rotas dinâmicas façam parte de sua configuração).

Tudo parece fora do tópico deste RFC. Não creio que ninguém, incluindo o OP, tenha sugerido que essa é a solução definitiva para o roteamento. Isso apenas melhora o roteamento baseado no sistema de arquivos.

Na verdade, há algum contexto adicional aqui. Esta proposta demorou muito para chegar e, com base em muitas das discussões que vi relacionadas a ela (incluindo aquelas nas quais estive diretamente envolvido), isso estava sendo elogiado até certo ponto por eliminar a necessidade de usar essas rotas bibliotecas de gerenciamento como next-routes e minhas próprias. Não acho que seja fora do assunto destacar os casos de uso que não são cumpridos por esta RFC. Algumas delas podem ser atendidas por algumas alterações na proposta, outras não. Mas de qualquer forma, certamente é valioso aumentar a consciência sobre os limites do que está sendo proposto.

FWIW usamos rotas baseadas em FS do estilo [param] no Pinterest (embora não no Next). Está muito bem dimensionado até agora. A maior crítica é que Jest interpreta [] como pares regexp, portanto, pode ser difícil direcionar testes para manipuladores param-ful.

@chrislloyd Quais são suas experiências com a criação e gerenciamento de arquivos usando este formato para caminhos / arquivos em ambientes diferentes, considerando que alguém está usando zsh, ou uma ferramenta que os interpreta de forma diferente?

Visto como o [] está usando para correspondência de padrões no zsh (e, como você diz com Jest), você precisará escapar desses caminhos. Isso não é um grande problema se você _sabe_ isso, mas dado que deve ser utilizável e entendido por iniciantes, tenho dúvidas de que este seja o formato certo a seguir.

Tenho uma ideia sobre como usar ! como parâmetro obrigatório, como /pages/id!.js e ? parâmetro opcional, como em /pages/posts/id?.js .

Ele não tem nenhum problema com prefixo como as discussões acima, e é familiar em como ! representa os parâmetros obrigatórios e ? representa os parâmetros opcionais.

O Windows não permite pontos de interrogação em nomes de arquivos e ambos? e! têm um significado especial no Bash.

API rotas agora suportam parâmetros dinâmicos # 7629 🚀

@remy getRequestHandler deve lidar com o roteamento dinâmico - acabei de confirmar localmente que sim. Você poderia registrar um bug / problema separado com as etapas de reprodução para que possamos investigar? :orar:

Olá a todos! Obrigado pela incrível resposta a este RFC.

Este RFC foi implementado e lançado como estável no Next.js 9.
Você pode ler mais sobre isso na postagem do blog .

Vamos publicar um novo RFC no futuro para abordar todos os comentários avançados fornecidos aqui. Publicaremos uma atualização aqui quando estiver disponível.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

olifante picture olifante  ·  3Comentários

flybayer picture flybayer  ·  3Comentários

wagerfield picture wagerfield  ·  3Comentários

knipferrc picture knipferrc  ·  3Comentários

rauchg picture rauchg  ·  3Comentários