Fügen Sie Blogs, Dokumentationen und andere statische Seiten zu Phoenix-Apps hinzu. Diese Bibliothek lässt sich nahtlos in Ihren Router integrieren und bietet integrierte Unterstützung für das Rendern von Markdown mit Frontmatter, Syntaxhervorhebung, Caching zur Kompilierungszeit und mehr.
def deps do
[
{ :phoenix_pages , "~> 0.1" }
]
end Die empfohlene Methode zur Installation in Ihrer Phoenix-Anwendung besteht darin, dies zu Ihrer router Funktion in lib/myapp_web.ex hinzuzufügen und dabei myapp durch den Namen Ihrer Anwendung zu ersetzen:
def router do
quote do
use Phoenix.Router , helpers: false
use PhoenixPages , otp_app: :myapp
# ...
end
end Jetzt können Sie mit dem Makro pages/4 eine neue Route hinzufügen:
scope "/" , MyAppWeb do
pipe_through :browser
get "/" , PageController , :home
pages "/:page" , PageController , :show , from: "priv/pages/**/*.md"
end Dadurch werden alle Markdown-Dateien von priv/pages gelesen und für jede einzelne eine neue GET-Route erstellt. Das :page -Segment wird durch den Pfad und Dateinamen (ohne Erweiterung) relativ zum Basisverzeichnis ersetzt (siehe Definieren von Pfaden).
Sie müssen außerdem den :show Handler zu lib/myapp_web/controllers/page_controller.ex hinzufügen:
defmodule MyAppWeb.PageController do
use MyAppWeb , :controller
# ...
def show ( conn , _params ) do
render ( conn , "show.html" )
end
end Fügen Sie abschließend eine Vorlage unter lib/myapp_web/controllers/page_html/show.html.heex hinzu. Der gerenderte Markdown der Seite ist in der inner_content -Zuweisung verfügbar:
< main >
<%= @inner_content % >
</ main > Das ist es! Versuchen Sie nun, eine Datei unter priv/pages/hello.md zu erstellen und /hello aufzurufen.
Um zu verhindern, dass mix format Klammern zum pages hinzufügt, ähnlich wie bei den anderen Phoenix Router-Makros, fügen Sie :phoenix_pages zu .formatter.exs hinzu:
[
import_deps: [ :ecto , :ecto_sql , :phoenix , :phoenix_pages ]
] Mit Frontmatter können seitenspezifische Variablen mithilfe des YAML-Formats oben in eine Markdown-Datei eingefügt werden. Wenn Sie Frontmatter-Variablen festlegen (was optional ist), müssen diese an erster Stelle in der Datei stehen und zwischen dreifach gestrichelten Linien festgelegt werden:
---
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. Um anzugeben, welche Frontmatter-Werte auf jeder Seite erwartet werden, legen Sie die Option attrs fest:
pages "/:page" , PageController , :show ,
from: "priv/pages/**/*.md" ,
attrs: [ :title , author: nil ]Atomwerte gelten als erforderlich und es wird ein Kompilierungsfehler ausgegeben, wenn sie auf einer der Seiten fehlen. Schlüsselwerte müssen in der Liste an letzter Stelle stehen und werden durch die Definition eines Standardwerts als optional betrachtet. Alle Frontmatter-Werte, die nicht in der Attributliste definiert sind, werden stillschweigend verworfen.
Gültige Attributwerte sind in den Zuweisungen verfügbar:
< main >
< h1 > <%= @title % > </ h1 >
< h2 :if = {@author} > <%= @author % > </ h2 >
<%= @inner_content % >
</ main >Phoenix Pages verwendet das Makeup-Projekt zur Syntaxhervorhebung. Fügen Sie zum Aktivieren einen Lexer für Ihre spezifische(n) Sprache(n) zu den Projektabhängigkeiten hinzu. Phoenix Pages übernimmt die neue Abhängigkeit und beginnt ohne weitere Konfiguration mit der Hervorhebung Ihrer Codeblöcke. Standardmäßig sind keine Lexer enthalten.
`{: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"}` Wenn die Sprache Ihrer Wahl nicht unterstützt wird, sollten Sie erwägen, einen neuen Makeup-Lexer zu schreiben, um einen Beitrag zur Community zu leisten. Andernfalls können Sie einen JS-basierten Syntax-Highlighter wie highlights.js verwenden, indem Sie in render_options code_class_prefix: "language-" und syntax_highlighting: false festlegen.
Importieren Sie als Nächstes ein unten aufgeführtes Theme in Ihr CSS-Bundle. Die Einzelheiten dazu hängen stark von Ihrer CSS-Konfiguration ab, aber einige Beispiele sind unten aufgeführt. In den meisten Fällen müssen Sie phoenix_pages/css/monokai.css (oder ein beliebiges Theme Ihrer Wahl) in Ihr Bundle importieren und sicherstellen, dass deps als Anbieterverzeichnis enthalten ist.
Fügen Sie mit dem ESBuild-Installationsprogramm die Option env zu config/config.exs hinzu:
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 )
] Dann in app.js :
import "phoenix_pages/css/monokai.css" ; Fügen Sie mit dem Sass-Installationsprogramm das Flag --load-path zu config/config.exs hinzu:
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 )
] Dann in app.scss :
@import " phoenix_pages/css/monokai " ; Installieren Sie das Plugin postcss-import wie hier beschrieben und fügen Sie Folgendes zu assets/postcss.config.js hinzu:
module . exports = {
plugins : {
"postcss-import" : { }
}
} Dann in app.css :
@import "../../deps/phoenix_pages/css/monokai" ; Um eine Indexseite mit Links zu allen anderen Seiten zu erstellen, erstellen Sie eine normale GET-Route und verwenden Sie die Option id neben get_pages/1 und get_pages!/1 in Ihrem Router:
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> Alle Seitendateien werden während der Kompilierung gelesen und zwischengespeichert, sodass die get_pages -Funktionen eigentlich nichts aus dem Dateisystem lesen – was sie sehr leistungsfähig macht.
Die von den get_pages -Funktionen zurückgegebenen Seiten werden nach Dateinamen sortiert. Wenn Sie bei der Kompilierung und nicht im Controller bei jedem Laden einer Seite eine andere Reihenfolge angeben möchten, verwenden Sie die sort :
pages "/blog/:page" , BlogController , :show ,
id: :blog ,
from: "priv/blog/**/*.md" ,
attrs: [ :title , :author , :date ] ,
sort: { :date , :desc }Als Sortierwert kann jeder Attributwert aus der Frontmatter definiert werden.
Beim Definieren des Seitenpfads wird das :page Segment für jede generierte Seite während der Kompilierung durch die von ** und * abgeleiteten Werte ersetzt. Dies unterscheidet sich von Segmenten in regulären Routen, die zur Laufzeit in das params -Attribut der Controller-Funktion geparst werden.
Angenommen, Sie haben die folgende Dateistruktur:
┌── priv/
│ ┌── pages/
│ │ ┌── foo.md
│ │ ├── bar/
│ │ │ ┌── baz.md
Durch das Definieren pages "/:page", from: "priv/pages/**/*.md" in Ihrem Router werden zwei Routen erstellt: get "/foo" und get "/bar/baz" . Sie können das :page Segment sogar an einer anderen Stelle im Pfad platzieren, z. B. /blog/:page , und es funktioniert wie erwartet, indem Sie get "/blog/foo" und get "/blog/bar/baz" erstellen.
Für komplexe Szenarien haben Sie die Möglichkeit, Capture-Gruppenvariablen anstelle des :page -Segments zu verwenden.
Nehmen wir an, Sie haben die gleiche Dateistruktur wie oben, möchten aber nicht, dass der baz -Pfad unter /bar verschachtelt wird. Sie könnten pages "/$2", from: "priv/pages/**/*.md" definieren und dabei $2 anstelle von :page verwenden. Dadurch werden zwei Routen erstellt: get "/foo" und get "/bar" .
Capture-Gruppenvariablen enthalten den Wert der ** und * -Blöcke der Reihe nach, beginnend bei $1 . Beachten Sie, dass ** mit allen Dateien und null oder mehr Verzeichnissen und Unterverzeichnissen übereinstimmt und * mit einer beliebigen Anzahl von Zeichen bis zum Ende des Dateinamens, dem nächsten Punkt oder dem nächsten Schrägstrich übereinstimmt.
Weitere Informationen zu den Platzhaltermustern finden Sie unter Path.wildcard/2.
Zusätzlich zu den anpassbaren Markdown-Optionen unterstützt das Markdown-Rendering standardmäßig auch IAL-Attribute. Das heißt, Sie können HTML-Attribute zu jedem Element auf Blockebene hinzufügen, indem Sie die Syntax {:attr} verwenden.
So erstellen Sie beispielsweise eine gerenderte Ausgabe von <h1 class="foobar">Header</h1> :
# Header{:.foobar}Attribute können eines der folgenden sein:
{:#id} um eine ID zu definieren{:.className} um einen Klassennamen zu definieren{:name=value} , {:name="value"} oder {:name='value'} um ein anderes Attribut zu definieren Um mehrere Attribute zu definieren, trennen Sie sie durch Leerzeichen: {:#id name=value} .
Wenn Sie Seiten hinzufügen, entfernen oder ändern, während mix phx.server ausgeführt wird, werden diese automatisch im Cache ersetzt und Sie müssen nicht neu starten, damit sie wirksam werden. Um ein Live-Neuladen zu ermöglichen, wenn sich eine Seite ändert, fügen Sie Folgendes zur Musterliste der Endpoint-Konfiguration in config/dev.exs hinzu:
config :myapp , MyAppWeb.Endpoint ,
live_reload: [
patterns: [
# ...
~r " priv/pages/.*(md)$ " ,
# ...
]
]