Phoenix 앱에 블로그, 문서 및 기타 정적 페이지를 추가하세요. 이 라이브러리는 라우터에 완벽하게 통합되며 머리말, 구문 강조, 컴파일 타임 캐싱 등을 포함한 마크다운 렌더링 지원 기능이 내장되어 있습니다.
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
에서 모든 마크다운 파일을 읽고 각 파일에 대해 새로운 GET 경로가 생성됩니다. :page
세그먼트는 기본 디렉토리에 상대적인 경로 및 파일 이름(확장자 없음)으로 대체됩니다(경로 정의 참조).
또한 lib/myapp_web/controllers/page_controller.ex
에 :show
핸들러를 추가해야 합니다.
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
에 템플릿을 추가하세요. 페이지의 렌더링된 마크다운은 inner_content
할당에서 사용할 수 있습니다.
< main >
<%= @inner_content % >
</ main >
그게 다야! 이제 priv/pages/hello.md
에서 파일을 생성하고 /hello
방문해보세요.
mix format
다른 Phoenix Router 매크로와 유사한 pages
매크로에 괄호를 추가하는 것을 방지하려면 .formatter.exs
에 :phoenix_pages
추가하세요.
[
import_deps: [ :ecto , :ecto_sql , :phoenix , :phoenix_pages ]
]
Frontmatter를 사용하면 YAML 형식을 사용하여 마크다운 파일 상단에 페이지별 변수를 포함할 수 있습니다. 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.
각 페이지에서 예상되는 머리말 값을 지정하려면 attrs
옵션을 설정하십시오.
pages "/:page" , PageController , :show ,
from: "priv/pages/**/*.md" ,
attrs: [ :title , author: nil ]
Atom 값은 필수로 간주되며 페이지에 누락된 경우 컴파일 오류가 발생합니다. 키-값은 목록의 마지막에 와야 하며 기본값을 정의하면 선택사항으로 간주됩니다. 속성 목록에 정의되지 않은 머리말 값은 자동으로 삭제됩니다.
할당에서 유효한 속성 값을 사용할 수 있습니다.
< main >
< h1 > <%= @title % > </ h1 >
< h2 :if = {@author} > <%= @author % > </ h2 >
<%= @inner_content % >
</ main >
Phoenix Pages는 구문 강조를 위해 메이크업 프로젝트를 사용합니다. 활성화하려면 특정 언어에 대한 어휘 분석기를 프로젝트 종속성에 추가하세요. 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"}`
선택한 언어가 지원되지 않으면 새로운 메이크업 어휘 분석기를 작성하여 커뮤니티에 기여하는 것을 고려해 보세요. 그렇지 않은 경우 render_options
에서 code_class_prefix: "language-"
및 syntax_highlighting: false
설정하여 하이라이트.js와 같은 JS 기반 구문 강조 표시기를 사용할 수 있습니다.
다음으로 아래 나열된 테마를 CSS 번들로 가져옵니다. 이 작업을 수행하는 구체적인 방법은 CSS 구성에 따라 크게 다르지만 아래에는 몇 가지 예가 포함되어 있습니다. 대부분의 경우 phoenix_pages/css/monokai.css
(또는 선택한 테마)를 번들로 가져와서 deps
공급업체 디렉터리에 포함되어 있는지 확인해야 합니다.
ESBuild 설치 프로그램을 사용하여 config/config.exs
에 env
옵션을 추가합니다.
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 경로를 만들고 라우터에서 get_pages/1
및 get_pages!/1
과 함께 id
옵션을 사용하세요.
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 }
머리말의 모든 속성 값을 정렬 값으로 정의할 수 있습니다.
페이지 경로를 정의할 때 :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
아래에 중첩되는 것을 원하지 않는다고 가정해 보겠습니다. :page
대신 $2
를 사용하여 pages "/$2", from: "priv/pages/**/*.md"
정의할 수 있습니다. 이렇게 하면 get "/foo"
및 get "/bar"
라는 두 가지 경로가 생성됩니다.
캡처 그룹 변수에는 $1
부터 시작하여 **
및 *
청크의 값이 순서대로 포함됩니다. **
모든 파일과 0개 이상의 디렉터리 및 하위 디렉터리와 일치하며, *
는 파일 이름 끝까지, 다음 점 또는 다음 슬래시까지의 문자 수와 일치합니다.
와일드카드 패턴에 대한 자세한 내용은 Path.wildcard/2를 확인하세요.
사용자 정의 가능한 마크다운 옵션 외에도 마크다운 렌더링은 기본적으로 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
의 Endpoint 구성 패턴 목록에 추가하세요.
config :myapp , MyAppWeb.Endpoint ,
live_reload: [
patterns: [
# ...
~r " priv/pages/.*(md)$ " ,
# ...
]
]