Agregue blogs, documentación y otras páginas estáticas a las aplicaciones de Phoenix. Esta biblioteca se integra perfectamente en su enrutador y viene con soporte incorporado para renderizar rebajas con frontmatter, resaltado de sintaxis, almacenamiento en caché en tiempo de compilación y más.
def deps do
[
{ :phoenix_pages , "~> 0.1" }
]
end
La forma recomendada de instalar en su aplicación Phoenix es agregar esto a la función de su router
en lib/myapp_web.ex
, reemplazando myapp
con el nombre de su aplicación:
def router do
quote do
use Phoenix.Router , helpers: false
use PhoenixPages , otp_app: :myapp
# ...
end
end
Ahora puedes agregar una nueva ruta usando la macro pages/4
:
scope "/" , MyAppWeb do
pipe_through :browser
get "/" , PageController , :home
pages "/:page" , PageController , :show , from: "priv/pages/**/*.md"
end
Esto leerá todos los archivos de rebajas de priv/pages
y creará una nueva ruta GET para cada uno. El segmento :page
se reemplazará con la ruta y el nombre del archivo (sin la extensión) relativos al directorio base (consulte Definición de rutas).
También deberá agregar el controlador :show
a lib/myapp_web/controllers/page_controller.ex
:
defmodule MyAppWeb.PageController do
use MyAppWeb , :controller
# ...
def show ( conn , _params ) do
render ( conn , "show.html" )
end
end
Por último, agregue una plantilla en lib/myapp_web/controllers/page_html/show.html.heex
. La rebaja representada de la página estará disponible en la asignación de inner_content
:
< main >
<%= @inner_content % >
</ main >
¡Eso es todo! Ahora intente crear un archivo en priv/pages/hello.md
y visite /hello
.
Para evitar que mix format
agregue paréntesis a la macro pages
de manera similar a las otras macros de Phoenix Router, agregue :phoenix_pages
a .formatter.exs
:
[
import_deps: [ :ecto , :ecto_sql , :phoenix , :phoenix_pages ]
]
Frontmatter permite incluir variables específicas de la página en la parte superior de un archivo de rebajas utilizando el formato YAML. Si está configurando variables frontales (que son opcionales), deben ser lo primero en el archivo y deben configurarse entre líneas de triple guion:
---
title : Hello World
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
Para especificar qué valores de frontmatter se esperan en cada página, configure la opción attrs
:
pages "/:page" , PageController , :show ,
from: "priv/pages/**/*.md" ,
attrs: [ :title , author: nil ]
Los valores atómicos se considerarán obligatorios y se generará un error de compilación si faltan en alguna de las páginas. Los valores-clave deben aparecer al final de la lista y se considerarán opcionales al definir un valor predeterminado. Cualquier valor inicial que no esté definido en la lista de atributos se descartará silenciosamente.
Los valores de atributos válidos estarán disponibles en las asignaciones:
< main >
< h1 > <%= @title % > </ h1 >
< h2 :if = {@author} > <%= @author % > </ h2 >
<%= @inner_content % >
</ main >
Phoenix Pages utiliza el proyecto Makeup para resaltar la sintaxis. Para habilitarlo, agregue un lexer para su(s) idioma(s) específico(s) a las dependencias del proyecto. Phoenix Pages seleccionará la nueva dependencia y comenzará a resaltar sus bloques de código sin ninguna configuración adicional. No se incluyen lexers de forma predeterminada.
`{:makeup_c, "~> 0.0"}`
`{:makeup_diff, "~> 0.0"}`
`{:makeup_elixir, "~> 0.0"}`
`{:makeup_erlang, "~> 0.0"}`
`{:makeup_graphql, "~> 0.0"}`
`{:makeup_eex, "~> 0.0"}`
`{:makeup_html, "~> 0.0"}`
`{:makeup_js, "~> 0.0"}`
`{:makeup_json, "~> 0.0"}`
`{:makeup_rust, "~> 0.0"}`
`{:makeup_sql, "~> 0.0"}`
Si el idioma de su elección no es compatible, considere escribir un nuevo léxico de maquillaje para contribuir a la comunidad. De lo contrario, puede utilizar un resaltador de sintaxis basado en JS, como resaltado.js, configurando code_class_prefix: "language-"
y syntax_highlighting: false
en render_options
.
A continuación, importe uno de los temas que se enumeran a continuación en su paquete CSS. Los detalles de cómo hacer esto dependen en gran medida de su configuración de CSS, pero a continuación se incluyen algunos ejemplos. En la mayoría de los casos, deberá importar phoenix_pages/css/monokai.css
(o cualquier tema que elija) a su paquete y asegurarse de que deps
esté incluido como directorio de proveedores.
Usando el instalador de ESBuild, agregue la opción env
a config/config.exs
:
config :esbuild ,
version: "0.17.18" ,
default: [
cd: Path . expand ( "../assets" , __DIR__ ) ,
env: % { "NODE_PATH" => Path . expand ( "../deps" , __DIR__ ) } ,
args: ~w ( --bundle --outdir=../priv/static/assets js/app.js )
]
Luego en app.js
:
import "phoenix_pages/css/monokai.css" ;
Usando el instalador de Sass, agregue el indicador --load-path
a config/config.exs
:
config :dart_sass ,
version: "1.62.0" ,
default: [
cd: Path . expand ( "../assets" , __DIR__ ) ,
args: ~w ( --load-path=../deps css/app.scss ../priv/static/assets/app.css )
]
Luego en app.scss
:
@import " phoenix_pages/css/monokai " ;
Instale el complemento postcss-import
como se describe aquí y agregue lo siguiente a assets/postcss.config.js
:
module . exports = {
plugins : {
"postcss-import" : { }
}
}
Luego en app.css
:
@import "../../deps/phoenix_pages/css/monokai" ;
Para crear una página de índice con enlaces a todas las demás páginas, cree una ruta GET normal y use la opción id
junto con get_pages/1
y get_pages!/1
en su enrutador:
get "/blog" , BlogController , :index
pages "/blog/:page" , BlogController , :show ,
id: :blog ,
from: "priv/blog/**/*.md" ,
attrs: [ :title , :author , :date ]
defmodule MyAppWeb.BlogController do
use MyAppWeb , :controller
def index ( conn , _params ) do
pages = MyAppWeb.Router . get_pages! ( :blog )
conn
|> assign ( :pages , pages )
|> render ( "index.html" )
end
def show ( conn , _params ) do
render ( conn , "show.html" )
end
end
<.link :for={page < - @pages} navigate = {page.path} >
<%= page.assigns.title % >
</.link>
Todos los archivos de páginas se leen y se almacenan en caché durante la compilación, por lo que las funciones get_pages
en realidad no leerán nada del sistema de archivos, lo que las hace muy eficientes.
Las páginas devueltas por las funciones get_pages
se ordenarán por nombre de archivo. Si desea especificar un orden diferente durante la compilación en lugar de en el controlador en cada carga de página, use la opción sort
:
pages "/blog/:page" , BlogController , :show ,
id: :blog ,
from: "priv/blog/**/*.md" ,
attrs: [ :title , :author , :date ] ,
sort: { :date , :desc }
Cualquier valor de atributo del frontmatter se puede definir como valor de clasificación.
Al definir la ruta de las páginas, el segmento :page
se reemplazará para cada página generada durante la compilación con los valores derivados de **
y *
. Esto es diferente a los segmentos en rutas regulares, que se analizan durante el tiempo de ejecución en el atributo params
de la función del controlador.
Por ejemplo, digamos que tiene la siguiente estructura de archivos:
┌── priv/
│ ┌── pages/
│ │ ┌── foo.md
│ │ ├── bar/
│ │ │ ┌── baz.md
Definir pages "/:page", from: "priv/pages/**/*.md"
en su enrutador creará dos rutas: get "/foo"
y get "/bar/baz"
. Incluso puedes colocar el segmento :page
en algún otro lugar de la ruta, como /blog/:page
, y funcionará como se esperaba creando get "/blog/foo"
y get "/blog/bar/baz"
.
Para escenarios complejos, tiene la opción de utilizar variables de grupo de captura en lugar del segmento :page
.
Digamos que tiene la misma estructura de archivos que la anterior, pero no desea que la ruta baz
esté anidada en /bar
. Podrías definir pages "/$2", from: "priv/pages/**/*.md"
, usando $2
en lugar de :page
. Esto creará dos rutas: get "/foo"
y get "/bar"
.
Las variables del grupo de captura contendrán el valor de los fragmentos **
y *
en orden, comenzando en $1
. Tenga en cuenta que **
coincidirá con todos los archivos y cero o más directorios y subdirectorios, y *
coincidirá con cualquier número de caracteres hasta el final del nombre del archivo, el siguiente punto o la siguiente barra.
Para obtener más información sobre los patrones de comodines, consulte Path.wildcard/2.
Además de las opciones de rebajas personalizables, la representación de rebajas también admite atributos IAL de forma predeterminada. Lo que significa que puede agregar atributos HTML a cualquier elemento a nivel de bloque usando la sintaxis {:attr}
.
Por ejemplo, para crear una salida renderizada de <h1 class="foobar">Header</h1>
:
# Header{:.foobar}
Los atributos pueden ser uno de los siguientes:
{:#id}
para definir una identificación{:.className}
para definir un nombre de clase{:name=value}
, {:name="value"}
o {:name='value'}
para definir cualquier otro atributo Para definir varios atributos, sepárelos con espacios: {:#id name=value}
.
Si agrega, elimina o cambia páginas mientras ejecuta mix phx.server
, se reemplazarán automáticamente en el caché y no tendrá que reiniciar para que surtan efecto. Para recargar en vivo cuando una página cambia, agregue a la lista de patrones de la configuración del Endpoint en config/dev.exs
:
config :myapp , MyAppWeb.Endpoint ,
live_reload: [
patterns: [
# ...
~r " priv/pages/.*(md)$ " ,
# ...
]
]