將部落格、文件和其他靜態頁面新增至 Phoenix 應用程式。該程式庫無縫整合到您的路由器中,並內建支援使用 frontmatter 渲染 markdown、語法突出顯示、編譯時快取等。
def deps do
[
{ :phoenix_pages , "~> 0.1" }
]
end
安裝到 Phoenix 應用程式的建議方法是將其新增至lib/myapp_web.ex
中的router
函數中,並將myapp
替換為應用程式的名稱:
def router do
quote do
use Phoenix.Router , helpers: false
use PhoenixPages , otp_app: :myapp
# ...
end
end
現在您可以使用pages/4
宏新增路由:
scope "/" , MyAppWeb do
pipe_through :browser
get "/" , PageController , :home
pages "/:page" , PageController , :show , from: "priv/pages/**/*.md"
end
這將從priv/pages
讀取所有 Markdown 文件,並為每個文件建立新的 GET 路由。 :page
段將替換為相對於基底目錄的路徑和檔案名稱(不含副檔名)(請參閱定義路徑)。
您還需要將:show
處理程序新增至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
最後,在lib/myapp_web/controllers/page_html/show.html.heex
新增一個模板。頁面渲染的 markdown 將在inner_content
分配中可用:
< main >
<%= @inner_content % >
</ main >
就是這樣!現在嘗試在priv/pages/hello.md
建立檔案並存取/hello
。
為了防止mix format
在pages
巨集新增括號(類似其他 Phoenix Router 巨集),請將:phoenix_pages
新增至.formatter.exs
:
[
import_deps: [ :ecto , :ecto_sql , :phoenix , :phoenix_pages ]
]
Frontmatter 允許使用 YAML 格式將特定於頁面的變數包含在 Markdown 檔案的頂部。如果您要設定 frontmatter 變數(可選),它們必須是檔案中的第一個變量,並且必須設定在三虛線之間:
---
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.
若要指定每個頁面中期望哪些 frontmatter 值,請設定attrs
選項:
pages "/:page" , PageController , :show ,
from: "priv/pages/**/*.md" ,
attrs: [ :title , author: nil ]
Atom 值將被視為必需,如果任何頁面中缺少原子值,則會引發編譯錯誤。鍵值必須位於清單的最後,並且透過定義預設值將被視為可選。屬性清單中未定義的任何 frontmatter 值都會默默丟棄。
有效的屬性值將在分配中可用:
< main >
< h1 > <%= @title % > </ h1 >
< h2 :if = {@author} > <%= @author % > </ h2 >
<%= @inner_content % >
</ main >
Phoenix Pages 使用 Makeup 項目進行語法突出顯示。若要啟用,請將特定語言的詞法分析器新增至專案依賴項。 Phoenix Pages 將取得新的依賴項並開始突出顯示您的程式碼區塊,無需任何進一步的配置。預設不包含詞法分析器。
`{: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"}`
如果您選擇的語言不受支持,請考慮編寫新的 Makeup 詞法分析器來為社群做出貢獻。否則,您可以透過在render_options
中設定code_class_prefix: "language-"
和syntax_highlighting: false
來使用基於 JS 的語法高亮工具,例如highlight.js。
接下來,將下面列出的主題匯入到您的 CSS 包中。執行此操作的具體細節很大程度上取決於您的 CSS 配置,但下麵包含一些範例。在大多數情況下,您需要將phoenix_pages/css/monokai.css
(或您選擇的任何主題)匯入到您的套件中,並確保deps
作為供應商目錄包含在內。
使用 ESBuild 安裝程序,將env
選項新增至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 )
]
然後在app.js
:
import "phoenix_pages/css/monokai.css" ;
使用 Sass 安裝程序,將--load-path
標誌新增至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 )
]
然後在app.scss
中:
@import " phoenix_pages/css/monokai " ;
請按照此處所述安裝postcss-import
插件,並將以下內容新增至assets/postcss.config.js
:
module . exports = {
plugins : {
"postcss-import" : { }
}
}
然後在app.css
中:
@import "../../deps/phoenix_pages/css/monokai" ;
要建立一個包含所有其他頁面連結的索引頁面,請建立一個普通的 GET 路由並在路由器中使用id
選項以及get_pages/1
和get_pages!/1
:
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>
所有頁面檔案在編譯期間都會被讀取和快取,因此get_pages
函數實際上不會從檔案系統中讀取任何內容,這使得它們的效能非常好。
從get_pages
函數傳回的頁面將按檔案名稱排序。如果您想在編譯期間而不是在每次頁面載入時在控制器中指定不同的順序,請使用sort
選項:
pages "/blog/:page" , BlogController , :show ,
id: :blog ,
from: "priv/blog/**/*.md" ,
attrs: [ :title , :author , :date ] ,
sort: { :date , :desc }
來自 frontmatter 的任何屬性值都可以定義為排序值。
定義頁面路徑時,編譯期間每個產生的頁面的:page
段將被替換為從**
和*
派生的值。這與常規路由中的段不同,常規路由中的段在運行時解析為控制器函數的params
屬性。
例如,假設您有以下文件結構:
┌── priv/
│ ┌── pages/
│ │ ┌── foo.md
│ │ ├── bar/
│ │ │ ┌── baz.md
在路由器中定義pages "/:page", from: "priv/pages/**/*.md"
將建立兩個路由: get "/foo"
和get "/bar/baz"
。您甚至可以將:page
段放在路徑中的其他位置,例如/blog/:page
,它將按預期建立get "/blog/foo"
和get "/blog/bar/baz"
。
對於複雜的場景,您可以選擇使用擷取群組變數而不是:page
段。
假設您具有與上面相同的檔案結構,但不希望baz
路徑嵌套在/bar
下。您可以使用$2
而不是:page
來定義pages "/$2", from: "priv/pages/**/*.md"
。這將建立兩個路由: get "/foo"
和get "/bar"
。
擷取組變數將依序包含**
和*
區塊的值,從$1
開始。請記住, **
將匹配所有文件以及零個或多個目錄和子目錄,而*
將匹配文件名末尾、下一個點或下一個斜杠之前的任意數量的字符。
有關通配符模式的更多信息,請查看 Path.wildcard/2。
除了可自訂的 Markdown 選項外,Markdown 渲染預設還支援 IAL 屬性。這意味著您可以使用語法{:attr}
將 HTML 屬性新增到任何區塊級元素。
例如,要建立<h1 class="foobar">Header</h1>
的渲染輸出:
# Header{:.foobar}
屬性可以是以下其中之一:
{:#id}
定義 ID{:.className}
定義類別名{:name=value}
、 {:name="value"}
或{:name='value'}
定義任何其他屬性若要定義多個屬性,請用空格分隔它們: {:#id name=value}
。
如果您在執行mix phx.server
時新增、刪除或變更頁面,它們將自動在快取中替換,並且無需重新啟動即可生效。若要在頁面變更時即時重新加載,請新增至config/dev.exs
中端點配置的模式清單:
config :myapp , MyAppWeb.Endpoint ,
live_reload: [
patterns: [
# ...
~r " priv/pages/.*(md)$ " ,
# ...
]
]