Next.js: [RFC] Rutas dinámicas

Creado en 19 jun. 2019  ·  90Comentarios  ·  Fuente: vercel/next.js

Rutas dinámicas

Antecedentes

El enrutamiento dinámico (también conocido como URL Slugs o URL bonitas / limpias) ha sido una característica solicitada desde hace mucho tiempo de Next.js.

Las soluciones actuales implican colocar un proxy L7 , un servidor personalizado o un middleware de usuario frente a su aplicación. Ninguna de estas soluciones ofrece una experiencia de desarrollador suficientemente _ergonómica_.

Además, los usuarios que buscan un servidor personalizado, sin darse cuenta, optan por no participar en funciones avanzadas de nivel de marco, como las funciones sin servidor por página.

Metas

  1. Aproveche la convención para proporcionar compatibilidad con URL Slug sobre la que sea fácil razonar
  2. Cubre la mayoría de los casos de uso observados en la naturaleza
  3. Elimina la necesidad de un servidor personalizado para admitir /blog/:post
  4. Valide las transiciones de ruta <Link /> cuando sea posible
  5. Evite una implementación que requiera un manifiesto de ruta
  6. Las rutas deben poder expresarse a través del sistema de archivos

Propuesta

Next.js debe admitir parámetros de URL con nombre que coincidan con un segmento de URL completo . Estas rutas se expresarían a través del sistema de archivos:

  1. Un nombre de archivo o nombre de directorio que está envuelto con [] se consideraría un parámetro con nombre
  2. Los segmentos de ruta explícitos tendrían prioridad sobre los segmentos dinámicos, emparejados de izquierda a derecha
  3. Los parámetros de ruta serían obligatorios , nunca opcionales
  4. Los parámetros de ruta se fusionarán en el objeto query (accesible desde getInitialProps o router través de withRouter ); estos parámetros no pueden ser anulados por un parámetro de consulta

Para ayudar a comprender esta propuesta, examinemos el siguiente árbol de archivos:

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

Next.js produciría las siguientes rutas, registradas en el siguiente orden:

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

Ejemplos de uso

Todos estos ejemplos asumen una página con el nombre pages/blog/[id].js archivo

Navegando a la página con <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>

El ejemplo anterior pasará a la página /blog/[id].js y proporcionará el siguiente objeto query al _Router_:

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

Lectura de parámetros con nombre 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: también puede usar withRouter .

Leer parámetros con nombre en 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

Advertencias

Los parámetros de ruta opcionales no se pueden expresar a través del sistema de archivos.

Puede emular un parámetro de ruta opcional creando una página de código auxiliar que exporte la versión del parámetro (o viceversa). Esto aumenta la visibilidad de las rutas de su aplicación al inspeccionar el sistema de archivos.

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

Los parámetros con nombre no pueden aparecer en medio del nombre de una ruta.

Esto significa que una página llamada blog-[id].js se interpretaría _literalmente_ y no coincidiría con /blog-1 . Puede reestructurar su página para que sea /blog/[id].js o convertir todo el segmento de URL en un parámetro con nombre y manejar la eliminación de blog- en el código de su aplicación.

Alternativas

Denote URL slugs con _insertar símbolo aquí_ en lugar de []

Hay muy pocos símbolos disponibles para representar un parámetro con nombre en el sistema de archivos. Desafortunadamente, la forma más reconocida de definir un parámetro con nombre ( :name ) no es

Al examinar el estado de la técnica, los símbolos más comunes utilizados para indicar un parámetro eran _ , $ y [] .

Descartamos _ porque _ es típicamente indicativo de una ruta interna que no es públicamente enrutable (por ejemplo, _app , _document , /_src , /_logs ).
También descartamos $ porque es un sigilo en bash para la expansión de parámetros.

Aproveche path-to-regexp para un soporte integral

La mayoría de los símbolos necesarios para expresar expresiones regulares no son

En el futuro, podemos permitir rutas path-to-regexp definidas en next.config.js o similar. Actualmente, esto está fuera del alcance de esta propuesta.

Exploración futura

Parámetros Catch-All

En el futuro, es posible que consideremos agregar parámetros generales. Con lo que sabemos hasta ahora, estos parámetros deben estar al final de la URL y potencialmente usarían % para indicar una ruta general (por ejemplo, pages/website-builder/[customerName]/%.tsx ).

Comentario más útil

Encuesta : Para expresar interés en los parámetros opcionales , reaccione con un "+1" a este comentario.

Nota : Los parámetros opcionales ya son posibles con este RFC, simplemente no tienen una sintaxis explícita (consulte la sección Advertencias ).

Todos 90 comentarios

Encuesta : Para expresar interés en los parámetros opcionales , reaccione con un "+1" a este comentario.

Nota : Los parámetros opcionales ya son posibles con este RFC, simplemente no tienen una sintaxis explícita (consulte la sección Advertencias ).

Encuesta : Para expresar interés en los parámetros generales , reaccione con un "+1" en este comentario.

Nota : ¡Comparta su caso de uso para los parámetros generales en este hilo! Nos encantaría entender más el problema del espacio.

reservado 3

En ricardo.ch, usamos un prefijo local para cada ruta, lo que hace que el enrutamiento sea un poco más complejo.

Ejemplo de rutas válidas:

  • / - página de inicio con configuración regional detectada automáticamente
  • /:locale - página de inicio con configuración regional forzada
  • /:locale/search - página de búsqueda
  • /:locale/article/:id - página del artículo

¿Cree que estos parámetros de prefijo podrían ser compatibles?

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

Otra cosa: para la página del artículo, también admitimos un slug antes de la identificación como /de/article/example-article-123 donde la identificación sería 123. Esto se hace a través de una expresión regular bastante compleja usando next-routes y yo no vea cómo se puede expresar esto con una API de sistema de archivos.

@ValentinH, todas las rutas proporcionadas son posibles utilizando la API del sistema de archivos, dadas las rutas proporcionadas:

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

también admitimos un slug antes de la identificación como / de / article / example-article-123 donde la identificación sería 123

Este caso de uso se aborda anteriormente:

Los parámetros con nombre no pueden aparecer en medio del nombre de una ruta.

Esto significa que una página llamada blog-$id.js se interpretaría literalmente y no coincidiría con /blog-1 . Puede reestructurar sus páginas para que sean /blog/$id.js o convertir todo el segmento de URL en un parámetro con nombre y manejar la eliminación de blog- en el código de su aplicación.

¿Esta solución no satisface sus necesidades? Nos encantaría saber más sobre sus requisitos específicos.

Muchas gracias por la respuesta.

No pensé en usar $locale/index.js tanto como carpeta como como archivo, ¡esto es realmente genial!

Con respecto al "parámetro con nombre en el medio", lo pasé por alto porque pensé que tener el slug dinámico era diferente. Sin embargo, tiene toda la razón y esto se aborda en el párrafo que mencionó. Anotar la babosa en el código de la aplicación será el camino a seguir 🙂

¿Sería posible algo como esto (analizar parámetros de .archivos. Ocultos / .carpetas)?

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

o deje el $ para que uno pueda encontrar sus archivos: D pero siempre use la carpeta $ para indicar un 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

Solía ​​tener este caso de uso para parámetros opcionales en una aplicación que funcionaba con paquetes npm. Opcionalmente, estos podrían tener un alcance. Hay rutas como:

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

Básicamente, el parámetro de alcance es opcional, pero también es solo un alcance cuando comienza con @ .
Entonces /packages/express/dependencies y /packages/@babel/core tienen la misma cantidad de segmentos, pero en un caso es /dependencies de express y en el otro es /index de @babel/core .

Al final se resolvió en react-router con las siguientes rutas:

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

No estoy seguro de ver una solución para este caso de uso en este RFC.

En cuanto a los casos de uso general, estoy pensando en cualquier enlace profundo en datos anidados recursivamente, como estructuras de carpetas, vistas de árbol, mapas de árbol.

Mis 2 centavos: los signos de dólar en los nombres de archivo son una mala idea porque los proyectiles los utilizan como un sigilo. Vas a confundir a las personas que intentan ejecutar rm $root.js . Los guiones bajos parecen una alternativa decente.

En términos más generales: como muchas personas, he intentado aprovechar el sistema de archivos como solución a esto en el pasado. En última instancia, creo que el sistema de archivos nunca ofrecerá la expresividad completa que está buscando. Por ejemplo, los enrutadores declarativos generalmente le permiten especificar un patrón de validación para un parámetro dinámico. En ese caso, parte del esquema reside en el sistema de archivos y otra parte en el código. La separación de preocupaciones es algo bueno, pero en este caso, es una limitación técnica más que cualquier otra cosa.

Como @ValentinH usamos $ locale var, pero es opcional.

¿Deberíamos usar /page.ts y /page/$locale/page.ts?

Debido a que podemos usar una configuración regional "predeterminada" o una configuración regional predefinida (configuración de usuario), en esos casos no usamos el parámetro $ locale.

Pero tenemos más casos de uso: / coche / búsqueda / $ filtro-opcional-1 / $ filtro-opcional-2 / $ filtro-opcional-3

Donde filtro-opcional-1: color-rojo, filtro-opcional-2: marca-ford, etc ...

Y para parámetros opcionales, algo como / $ required-param / y / $$ optional-param /?

¡Es genial que esto se presente en la hoja de ruta!

Sin embargo, intervenir para apoyar a touch $file esto generará mucha confusión. Debes recordar escapar en cada interacción. touch \$file; vim $file abrirá vim sin un archivo (porque $ file no es una variable definida).
Del mismo modo, la finalización de la pestaña en un shell enumerará todas las variables, lo que una vez más genera confusión.

Estoy proponiendo dos alternativas que creo que brindan las asociaciones adecuadas y deberían funcionar en shells:

  • = Puede leerse como page is a customer por =customer . Incluso puede contorsionarlo mentalmente para que sean dos puntos simplemente estirados, lo que se asemeja a la forma más común para los parámetros con nombre.
  • @ ya que también se lee algo bien. a customer por @customer

Otra opción sería utilizar llaves (a menos que sean caracteres reservados en algunos sistemas de archivos). Esta sintaxis de parámetro también es "técnica anterior" y es utilizada por muchos otros enrutadores:

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

Esto permitiría tener parámetros en el medio del segmento de ruta y múltiples parámetros por segmento, ya que está claro dónde comienza el parámetro y dónde termina, por ejemplo, /product-{productId}-{productColor} .

¡Estoy tan emocionado de que las rutas dinámicas lleguen a Next.js!

Con respecto a la sintaxis de los parámetros con nombre, esto es algo que se ha discutido en Spectrum: https://spectrum.chat/next-js/general/rfc-move-parameterized-routing-to-the-file-system~ce289c5e-ff66 -4a5b-8e49-08548adfa9c7. Podría valer la pena usar eso como entrada para la discusión aquí. Personalmente, me gusta cómo lo hace Sapper usando [brackets] . Esto también es algo que Nuxt va a implementar en la versión 3. Tener diferentes marcos que usen el mismo formato para rutas dinámicas basadas en sistemas de archivos suena como algo bueno.

Con respecto al uso de <Link /> , creo que los desarrolladores se olvidarán fácilmente de configurar los atributos href y as . Entiendo que no es posible "fusionar" estos en el atributo href porque introduciría un cambio importante, pero siento que podría resolverse de una manera más elegante.

Desafortunadamente, Bash usa llaves para agrupar comandos.

Estoy de acuerdo con @ stephan281094 con respecto al uso de <Link /> , será fuente de errores.

El enrutamiento dinámico es una característica extremadamente útil, por lo que es realmente increíble que lo hayan investigado y hayan encontrado una solución, ¡enormes accesorios!

Si bien se trata de este tema, las rutas comodín también serían una valiosa adición a la propuesta. Mencionó los parámetros generales como algo para investigar en el futuro, pero no cubre los casos en los que podría querer hacer algo como /category/* , que podría tener N niveles, y desea todos los ellos para representar la página category .

¿Es posible usar : forma segura? Si es así, ese sería mi voto, porque todos ya están familiarizados con esa convención de Express.

Debido a que $ conflicto con las variables de shell, personalmente me opongo firmemente a ello.

¿Es posible usar : forma segura? Si es así, ese sería mi voto, porque todos ya están familiarizados con esa convención de Express.

Aparentemente, : es un carácter prohibido en Windows, por lo que probablemente no sea seguro. Ir con _ tampoco es ideal, ya que se pueden usar guiones bajos en las URL. La razón por la que creo que [brackets] son una buena solución es porque está más preparado para el futuro. Si Next.js quiere admitir rutas como post-12345 en el futuro, con esta sintaxis se puede hacer sin introducir un cambio importante.

Entonces, una lista de personajes a evitar sería:

  • Conflictos con los sistemas de archivos: : , * , " , < , > , |
  • Conflictos con variables de shell: $
  • Conflictos con la expansión de llaves bash { , }

¿Algo más?

Esto no eliminaría nuestra necesidad de tener un archivo de ruta centralizado por un par de razones:

  • Tenemos un mapa del sitio generado automáticamente y el sistema de archivos por sí solo no es suficiente para definirlo.
  • Usamos rutas con nombre y las "páginas" de destino están determinadas por datos, en lugar de algo que se pueda conocer en el momento de la construcción. La lógica para averiguar qué página cargar en función del nombre y los parámetros está impulsada por la configuración de la ruta.

También generamos nuestra carpeta de páginas por estas razones:

  • Usamos Relay, y esto significa que los módulos que involucran GraphQL deben tener un nombre exclusivo. Por esa razón, a menudo no podemos hacer que los nombres de los segmentos de ruta sean los mismos que los nombres de los módulos. index.js definitivamente no es único, y veo lugares en los que tendríamos múltiples segmentos comunes como edit .
  • Preferimos co-ubicar componentes únicos específicos de la página como hermanos de los propios módulos de página, lo que Next.js no permite dentro de la carpeta de páginas.

Básicamente, nuestro patrón es utilizar nuestra configuración de ruta centralizada para generar nuestra carpeta de páginas, que contiene archivos que no hacen nada más que importar / exportar módulos de cualquier otra parte del código base.

Con ese fin, mi atención se centra más en si esta propuesta puede funcionar simplemente como un formato de salida mejorado para nuestro proceso de generación de páginas existente, de modo que al menos podamos obtener el beneficio de no necesitar un servidor personalizado.

He repasado algunos de mis casos de uso en otros lugares: https://gist.github.com/AndrewIngram/8d4c4ccd9bd10415a375caacade9f5ca

Lo principal que no veo es el soporte de parámetros implícitos que no se expresan en el sistema de archivos, por ejemplo, anulaciones de URL.

Digamos que tenemos una URL como esta:

/some-vanity-url/

Donde en los términos actuales de Next.js, queremos que se asigne a una página de producto con una serie de parámetros de consulta, por ejemplo, Product.js?id=foo&language=en .

De manera similar, en nuestro sitio web, la mayoría de los "sitios" de los países están sujetos a un segmento de nivel superior, por ejemplo, es o ie , pero el sitio gb se monta sin ese segmento. Esto significa que todas las páginas gb tienen un parámetro country implícito, mientras que para todos los demás países es explícito.

La otra desventaja es que debido a que en nuestro caso, la misma 'página' puede existir en múltiples puntos de montaje en la arquitectura de URL, vamos a terminar con una mayor cantidad de paquetes (es decir, varios puntos de entrada duplicados) de los que realmente tenemos. necesidad en la práctica.

En general, esta propuesta parece funcionar bien para los casos de uso más comunes, pero no evita la necesidad de una configuración de ruta o un servidor personalizado en _todos_ los casos. Pero suponiendo que esto no reemplace mi capacidad para usar el marco de la forma en que lo hago hoy, no tengo ninguna objeción real a que esta sea la API de ruta feliz preferida.

Apoyo la sugerencia {id} . Permite múltiples parámetros y creo que se ve mucho mejor. También encaja mejor con React.

Estoy a favor del carácter file/&param.js . Tomado directamente de las URL y no parece que entre en conflicto con los sistemas de archivos o bash.

Usaría _ y tal vez permitiría una anulación en next.config.js para aquellos que realmente necesitan algo diferente.

Aprecie el trabajo en esto. ¡Lo he estado queriendo por un tiempo! ❤️

¡Asombroso! 🎉🎉🎉

Mi único problema aquí es que Link necesita tanto href como as params.

Creo que podríamos escribir <Link to="blog/123" /> : dado que Nextjs ya conoce todas las rutas basadas en archivos en la carpeta de páginas, podría traducirlo fácilmente a "/blog/$id" .

Entonces, una lista de personajes a evitar sería:

& es un operador de control en bash que ejecuta el lado izquierdo del argumento en una subcapa asincrónica. Texto simple: open pages/&customer ejecutaría open pages/ en segundo plano y el comando customer en el shell de primer plano.

Esto se ve realmente genial.

Parece que esto creará una cantidad significativa de directorios de archivos únicos (como /blog/$id en el ejemplo original). Esto se vuelve aún más complicado si desea dos parámetros de ruta final (es decir, /git/compare/$hash1/$hash2 ).

Tampoco me encanta que el nombre de archivo para reproducir una publicación de blog sea $id.js . Tenerlo llamado blog.js sería mucho más descriptivo.

¿Quizás combinar con un 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

Esto también parece proporcionar una solución más limpia para los parámetros generales propuestos.

Los decoradores no se pueden aplicar a funciones (¿tal vez esto cambió desde la última vez que lo leí?)

Bueno, supongamos que sigue ese camino, probablemente lo haría de la forma en que AMP está configurado ahora:

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

Sin embargo, creo que cosas como esta se pueden agregar más adelante si parece útil en algún momento. Creo que prefiero ver un soporte básico para empezar, como se describe en el RFC. Obtenga un uso real con eso, luego refine donde se rompe. También creo que los únicos caracteres que se deben tener en cuenta para evitar son los del sistema de archivos. Esos son los verdaderos bloqueadores para construir esta función.

Por favor, asegúrese de utilizar un personaje que sea compatible con las soluciones sin servidor. (En Aws, hay algunos personajes que podrían causar problemas)

Exportar un objeto de configuración con una clave de componente es algo que no odio.

También puedes usar un HOC

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

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

¿Qué pasa si agregamos algún campo estático a la 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 ¿Qué sucede cuando navega a /blog en el navegador? ¿Un 404?

Debido a que esto debe determinarse en el momento de la compilación, supongo que necesitaría algo que se pueda analizar estáticamente. un HOC o una propiedad estática no lo sería.

necesitaría algo que sea analizable estáticamente. un HOC o una propiedad estática no sería

Cada ejemplo de propiedad estática dado hasta ahora sería analizable estáticamente (aunque ciertamente podría romper cosas fácilmente). Podríamos simplemente insistir en que exporte su función y establezca la propiedad de ruta en ella de una manera analizable estáticamente. El tiempo de ejecución podría verificar las propiedades de la ruta que están configuradas en el tiempo de ejecución, pero no fueron detectadas por nuestro analizador estático y emitir una advertencia / lanzar un error.

¿Qué sucede cuando navega a / blog en el navegador? ¿Un 404?

@kingdaro - En mi opinión, sí. Si desea utilizar las rutas /blog y /blog/:blogId , utilice un directorio. Está sobrecargando esa ruta, por lo que la estructura del directorio está justificada.

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

Bueno, supongamos que sigue ese camino, probablemente lo haría de la forma en que AMP está configurado ahora:

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

Sin embargo, creo que cosas como esta se pueden agregar más adelante si parece útil en algún momento. Creo que prefiero ver un soporte básico para empezar, como se describe en el RFC. Obtenga un uso real con eso, luego refine donde se rompe. También creo que los únicos caracteres que se deben tener en cuenta para evitar son los del sistema de archivos. Esos son los verdaderos bloqueadores para construir esta función.

Creo que usar config es una mala idea porque necesitas revisar varios archivos para ver qué es realmente dinámico. Si lo configura en el sistema de archivos, puede verlo a primera vista.

Me pregunto si más de una solución de enrutamiento estándar debería ser algo a considerar.

El enrutamiento simple basado en archivos es un gran punto de venta para aquellos que son nuevos en Next / React, o para cualquiera que desee tener rápidamente una aplicación simple en funcionamiento, pero puede ser bastante limitante. Y me parece que tratar de encajar el enrutamiento dinámico con calzador en este patrón podría arruinar esa simplicidad y llevar a una complejidad innecesaria, todo en nombre de mantener todo basado en archivos.

Después de leer esta discusión y pensar en mi propio uso de Next.js, creo que el soporte de primera clase para un sistema de enrutamiento alternativo (complementario) podría ser la mejor manera de resolver esto.

Me gustan algunas de las ideas originales de este hilo (como la propuesta de usar decoradores) pero esas ideas definitivamente tienen sus propios problemas. Espero que se nos ocurra algo genial 👍

Exportar un objeto de configuración con una clave de componente es algo que no odio.

También puedes usar un HOC

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

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

Eso es bastante bueno, pero me pregunto si tener información de ruta dividida en muchos archivos como
esto podría resultar difícil de manejar.

Mi pensamiento original al proponer una configuración local (en el archivo) frente a una global ( route.js ), fue abordar los escenarios específicos mencionados en mi primer comentario (archivos profundamente anidados que son el único archivo en su directorio, nombres de archivos no semánticos y catch-all-params).

Si se usa estrictamente en esos contextos, es mucho menos confuso, porque la URL se asigna directamente al sistema de archivos, y la configuración local solo dirige los parámetros "adicionales".

Dicho esto, no estoy seguro de si intentaría restringir a los usuarios para que no lo hagan como quieran. Podemos imprimir bastante la tabla de enrutamiento calculada en la consola, o incluso guardarla en algún archivo predeterminado. Eso debería ser suficiente para ayudar a solucionar problemas en las rutas.

@merelinguist No creo que = esté prohibido en Windows como ha escrito en la tabla de resumen. Está vinculando de nuevo a cómo : está prohibido, pero de acuerdo con los documentos de nombres de archivos de Microsoft Windows, se permite el carácter igual.

Ya estoy portando con rutas dinámicas en un proyecto que uso en producción (ojalá pueda hacerlo en vivo esta semana).

Sin embargo, una pregunta específica, ¿la nueva función de la API next @ canary _ también_ admitirá el enrutamiento dinámico?

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

Lo acabo de probar con [email protected] y obtengo un 404 no encontrado, así que sospecho que aún no está allí. Parece que tiene sentido que estas dos características (API + rutas dinámicas) tengan paridad en el enrutamiento de URL.

@remy aún no está implementado está en mi lista para hacerlo pronto

También debemos tener en cuenta no solo los sistemas Windows y Linux, sino también otros:
https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations

Me gustaría agregar más información sobre mi propuesta:

¿Qué pasa si agregamos algún campo estático a la 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. El desarrollador no puede usar la variable de tiempo de ejecución para esa propiedad de ruta
const route = `/blog/${somethingElse}`;
BlogPost.route = route; // is not allowed
  1. Cuando construimos el manifiesto de la página con este RFC actual (donde la carpeta contiene algún carácter para identificar que es dinámico), no veo la diferencia si construimos este manifiesto de página leyendo el archivo y encontramos la propiedad de ruta estática en la página. De la misma manera que funciona el lingui : no permiten que id para Trans sea ​​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>

Siguiendo la lista de prefijos ya enumerados , me pregunto si hay alguna razón sólida _no_ para usar un prefijo de símbolo @ .

Dudo que sea de valor, pero obtienes la paridad con Nuxt, lo que significa que alguien que cambie de uno u otro sabrá inmediatamente cómo funciona.

Alternativamente, ¿alguien ha pensado en hacer del prefijo una opción de usuario? Hace que sea más difícil para la gente entender un proyecto de otro, pero significa que si quisiera, podría hacer el prefijo query__{...} o algo así.

Solo un pensamiento.

Siguiendo la sugerencia de @remy , ¿por qué no abrir completamente la API para

@ scf4 Tenía una biblioteca que es un PoC, que usa la configuración de rutas now.json para hacer enrutamiento universal con nextjs también aquí

Espero que el equipo de Zeit también abra el analizador de rutas en la biblioteca del lado del cliente.

Mirando a Nuxt, creo que _id.js no es tan malo. Sí, ya usamos _app y _document.js como mencionaste y no se puede enrutar públicamente. Pero una ruta dinámica también se puede ver como no enrutable, ya que es una plantilla para muchas páginas.

¿Cómo se manejaría esto para las exportaciones de sitios estáticos?

(No importa este)

También creo que sería útil si Next.js imprimiera las rutas generadas en un solo archivo (quizás oculto de forma predeterminada). Como mínimo, serviría como una referencia útil para las personas que trabajan en un proyecto, pero también podría abrir la puerta a un enrutamiento dinámico poderoso más adelante.

Es decir, si usa ese archivo para el manejo de rutas en tiempo de ejecución, sería muy fácil para los usuarios agregar / cambiar rutas (por ejemplo, para la coincidencia de patrones complejos) sin perder los beneficios de la API basada en el sistema de archivos.

Esto crearía algunos desafíos con respecto a cómo realizar un seguimiento de las rutas que se han cambiado manualmente, pero si se resuelven, creo que sería la mejor solución con diferencia.

@ scf4 Next.js ya tiene la capacidad de hacer rutas complejas usando la opción de servidor personalizado. Lo que propone se logra en casi la misma cantidad de código con las herramientas ya disponibles.

Ah, sí, bastante justo.

Creo que tener un único archivo de rutas que se pueda editar es una opción mucho mejor de todos modos.

Escribí algunas ideas sobre el enrutamiento con el sistema de archivos , pero puedo resumir mis hallazgos aquí:

  • [param] parece el más seguro (y lo usa Sapper).
  • : es familiar para los usuarios de Express, pero podría haber jurado que tenía problemas con Windows FS.
  • $ y {param} se utilizan para la expansión de variables y llaves en shells, por lo que esto puede ser más problemático en la CLI.
  • _ _podría_ funcionar, pero es demasiado común como indicador "privado".

Personalmente, he tenido mejores experiencias con la lista blanca de archivos para rutas ( /^index\. ) frente a una lista negra ( /^_/ ), pero eso sería un problema de compatibilidad con versiones anteriores con /pages .

Con discusiones recientes para admitir rutas API (# 7297), esta podría ser una oportunidad para admitir /api y /pages ambos bajo una nueva casa de /routes .

Sin embargo, _y es un "sin embargo" fuerte _, el ecosistema de Next.js es lo suficientemente grande como para justificar adiciones de características _incrementales_, frente a un diseño de "oye, si tuviéramos que hacer esto de nuevo, lo haríamos de esta manera".

Los corchetes ( [example] ) son usados ​​por zsh para hacer coincidir patrones, por lo que tampoco sería viable.

Ver ejemplos en Generación de nombre de

Los corchetes [] son utilizados por zsh para la coincidencia de patrones, por lo que tampoco sería viable.

Parece que lo acaban de hacer en https://github.com/zeit/next.js/pull/7623

Gracias por el aviso. También publiqué un comentario allí.

Probé [id] y solo usarlo en rutas es un dolor (por ejemplo, cd \[id\]/view.js ). Me parece que los guiones bajos dobles __id (por ejemplo, cd __id/view.js ) funcionan igual de bien y se pueden distinguir (aunque quizás un poco confuso) de los archivos / carpetas internos (por ejemplo, _app.js ).

@AaronDDM, ¿estás usando zsh ? No necesita escapar [ o ] en bash.

Sí, esto también me pasa con zsh - super molesto interactuar con estos directorios.

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

Y dado que zsh se convertirá en el shell predeterminado en macOS Catalina , tal vez se deba hacer algo al respecto después de todo ...

de acuerdo con __id.js

Hm, realmente no me encanta el __ , simplemente no me parece genial.

@merelinguist em, Jest usa __tests__ para la carpeta de prueba predeterminada, creo que __ tiene sentido en algún caso.

@YUFENGWANG Quizás, pero preferiría un solo personaje si es posible. En definitiva, creo que la mejor solución sería:

  1. Valor predeterminado sensible y multiplataforma, como =
  2. Opción en next.config.js para personalizar el carácter de ruta especial que se utiliza
  3. Documentación sobre qué personajes son problemáticos en qué situaciones.

De acuerdo con un solo carácter, pero prefiero tener una configuración cero. y supongo que muchas personas pasarán por todos los problemas incluso si los describe en una documentación

Tenga en cuenta también que = está reservado por zsh. De los documentos :

Si una palabra comienza con un '=' sin comillas y se establece la opción EQUALS, el resto de la palabra se toma como el nombre de un comando. Si existe un comando con ese nombre, la palabra se reemplaza por la ruta completa del comando.

Solo una idea; ¿qué pasa con el uso de un sufijo? Por ejemplo, [email protected] , o algo similar podría ser suficiente. Esto puede resolver el problema de tener que escapar y trabajar en shells y sistemas de archivos siempre que el carácter sea válido.

Estos funcionan en zsh y bash sin la necesidad de escapar, hasta ahora:

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

Oh. No es un sufijo, sino una forma de denotar los parámetros de URL finales.

Entonces [email protected] convierte en blog/:id .

compare@[email protected] convierte en compare/:a/:b .

Esto podría resolver los directorios de archivos únicos profundamente anidados a los que me oponía anteriormente y mantener basado todo el sistema de archivos de definición de enrutamiento.

No parece tan elegante, pero ¿qué tal algo como:

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

un prefijo _var_ Qué tipo de intenta imitar las declaraciones de variables JS. ¿O tiene que ser algo muy corto, de un solo personaje?

¿Qué tal el carácter ~ ?

Como /blogs/~id .

Usar ~ como prefijo tampoco es viable, ya que se usa para expandirse a la carpeta de inicio en shells compatibles con POSIX.

Cualquier carácter que no coincida con [0-9a-zA-Z-._] (regex) no puede considerarse seguro como prefijo en los sistemas operativos, shells y sistemas de archivos.

Algunos personajes tampoco son seguros en línea. Ver la documentación de zsh

También creo que no deberíamos esforzarnos por que parezca elegante, sino más bien intuitivo, legible y fácil de comunicar.

  • usar corchetes para [params].js parece más elegante y ampliamente utilizado. (zapador, nuxt v3?).
  • prefijo de subrayado pages/_helper.js generalmente para una función privada y tal vez esto no debería representarse. esto nos permite crear componentes auxiliares dentro de la carpeta de páginas

En mi humilde opinión: esto se siente como una solución temporal al problema mayor. Aunque tener rutas basadas en la estructura de archivos es muy bueno para empezar, no se escala bien cuando tienes cientos de rutas, parámetros, etc. Tener un archivo de configuración de rutas (tal vez tener un archivo route.js en cada directorio) es una mejor solución a largo plazo. Personalmente, me atrae nextjs debido a las características listas para usar (SSR, velocidad, etc.) que proporciona, no a la facilidad de crear rutas a partir de archivos.

@mmahalwy has dado en el clavo.

Next.js ya genera una configuración de rutas (basada en el sistema de archivos). Creo que hacer esta configuración más explícita y / o permitir que el usuario la "expulse" si lo desea sería la solución más perfecta aquí.

@mmahalwy @ scf4 FWIW, una justificación significativa para las rutas del sistema de archivos es eliminar la necesidad de tener un archivo centralizado. De hecho, se podría argumentar fácilmente que la totalidad de la API de Next.js para enlaces y enrutamiento está diseñada en torno a esta restricción.

El problema con una configuración de ruta es que termina teniendo que enviársela al cliente, lo que puede significar un paquete de código bastante considerable si tiene rutas que van desde cientos hasta miles.

Sin embargo, hay bastantes casos de uso comunes que (por lo que he podido decir, al discutir este problema con @timneutkens en numerosas ocasiones durante los últimos meses) no se pueden resolver realmente sin una configuración centralizada. Enumeré algunos de ellos en mi comentario anterior, pero hay más.

El más simple es tener un blog impulsado por CMS donde los autores pueden crear enlaces a las páginas del sitio. Simplemente crearán enlaces con una URL antigua simple, sin conocimiento de cuál es el módulo de página subyacente. Con una configuración de ruta centralizada, es bastante fácil invertir la coincidencia de una URL y averiguar qué página cargar (mi propia biblioteca, next-route-resolver está diseñada para admitir estos casos de uso y todos los demás que se me han ocurrido) .

No veo cómo puedo hacer que el sitio en el que estoy trabajando funcione sin una configuración de ruta, por lo que mi enfoque ha sido encontrar formas de mantener la configuración de ruta dentro de las tolerancias. Para otras personas, el enrutamiento del sistema de archivos puede ser más que suficiente. No creo que el enrutamiento sea un problema en el que hay una única solución que resuelve todo, se trata de equilibrar las compensaciones.

Entonces, como mencioné antes, en lo que respecta a esta propuesta, parece estar bien siempre que se venda como una solución del problema de enrutamiento por completo, porque eso sería un poco engañoso :)

@AndrewIngram Entiendo de dónde vienes, pero esta limitación está limitando el poder que tiene nextjs. Nextjs ofrece tantas cosas listas para usar que debería ser una obviedad para cualquier nuevo proyecto o empresa utilizarlo. Sin embargo, el desafío es la opinión difícil sobre el enrutamiento que lo hace imposible de rechazar en el futuro (y como una gran empresa, siempre está considerando la estrategia de salida en caso de que los proyectos pierdan interés o mantenimiento).

@mmahalwy Creo que

Para aquellos que desean una configuración de enrutamiento centralizada o avanzada, ¿no se maneja bien mediante el uso del servidor personalizado y / o paquetes externos? ¿Qué esperas que se agregue aquí?

Todo parece fuera del tema de este RFC. No creo que nadie, incluido el OP, haya sugerido que esta sea la solución definitiva para el enrutamiento. Esto solo mejora el enrutamiento basado en el sistema de archivos.

He estado usando las rutas dinámicas para un mini proyecto durante las últimas semanas (usando $ aunque noté que se movió a [param] 3 días atrás en el repositorio de canary, pero de todos modos).

Acabo de empezar a usar getRequestHandler y creo que no está recogiendo el enrutamiento dinámico en el lado del servidor.

¿Es un error o intencional (es decir, algún cambio en getRequestHandler ), algo más, o el uso de getRequestHandler apaga completamente el enrutamiento dinámico (lo que tendría sentido ahora que lo pienso ...) ?

Para aquellos que desean una configuración de enrutamiento centralizada o avanzada, ¿no se maneja bien mediante el uso del servidor personalizado y / o paquetes externos? ¿Qué esperas que se agregue aquí?

Uno de los objetivos aquí es evitar la necesidad de crear un servidor personalizado, aunque solo sea para facilitar su uso con servicios como Now (que actualmente requiere que todas las rutas dinámicas sean parte de su configuración).

Todo parece fuera del tema de este RFC. No creo que nadie, incluido el OP, haya sugerido que esta sea la solución definitiva para el enrutamiento. Esto solo mejora el enrutamiento basado en el sistema de archivos.

De hecho, hay un contexto adicional aquí. Esta propuesta ha tardado en llegar y, según muchas de las discusiones que he visto relacionadas con ella (incluidas aquellas en las que he estado involucrado directamente), se promocionó hasta cierto punto como eliminar la necesidad de usar estas rutas. bibliotecas de gestión como next-route y la mía. No creo que esté fuera de tema resaltar los casos de uso que no se cumplen en este RFC. Es posible que algunos de ellos se cumplan con algunos cambios en la propuesta, mientras que otros no. Pero de cualquier manera, seguramente es valioso crear conciencia sobre los límites de lo que se propone.

FWIW usamos rutas basadas en FS estilo [param] en Pinterest (aunque no Next). Está muy bien escalado hasta ahora. La mayor crítica es que Jest interpreta [] como pares de expresiones regulares, por lo que puede ser difícil apuntar a las pruebas para los controladores param-ful.

@chrislloyd ¿Cuáles son sus experiencias al crear y administrar archivos usando este formato para rutas / archivos en diferentes entornos, considerando que alguien está usando zsh o una herramienta que los interpreta de manera diferente?

Visto como [] se usa para la coincidencia de patrones en zsh (y, como dice con Jest), deberá escapar de estas rutas. Esto no es un gran problema si lo sabes, pero dado que los principiantes deberían poder usarlo y entenderlo, tengo dudas de que este sea el formato adecuado.

Tengo una idea sobre el uso de ! como parámetro obligatorio, como /pages/id!.js y ? como parámetro opcional, como en /pages/posts/id?.js .

No tiene ningún problema con el prefijo como en las discusiones anteriores, y está familiarizado con cómo ! representa los parámetros obligatorios y ? representa los parámetros opcionales.

Windows no permite signos de interrogación en los nombres de archivo, ¿y ambos? y! tienen un significado especial en Bash.

API rutas

Se espera que @remy getRequestHandler maneje el enrutamiento dinámico; acabo de confirmar localmente que lo hace. ¿Podría presentar un error / problema por separado con los pasos de reproducción para que podamos investigar? :orar:

¡Hola a todos! Gracias por la increíble respuesta a este RFC.

Este RFC se ha implementado y publicado como estable en Next.js 9.
Puede leer más sobre esto en la publicación del blog .

Publicaremos un nuevo RFC en el futuro para abordar todos los comentarios avanzados que se brindan aquí. Publicaremos una actualización aquí cuando esté disponible.

¿Fue útil esta página
0 / 5 - 0 calificaciones