เพิ่มบล็อก เอกสารประกอบ และเพจแบบคงที่อื่นๆ ให้กับแอป Phoenix ไลบรารีนี้ผสานรวมเข้ากับเราเตอร์ของคุณได้อย่างราบรื่น และมาพร้อมกับการรองรับในตัวสำหรับการเรนเดอร์มาร์กดาวน์ด้วย frontmatter การเน้นไวยากรณ์ การแคชเวลาคอมไพล์ และอื่นๆ
def deps do
[
{ :phoenix_pages , "~> 0.1" }
]
end
วิธีที่แนะนำในการติดตั้งลงในแอปพลิเคชัน Phoenix ของคุณคือเพิ่มสิ่งนี้ลงในฟังก์ชัน router
ของคุณใน lib/myapp_web.ex
โดยแทนที่ 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
จะถูกแทนที่ด้วยพาธและชื่อไฟล์ (ไม่มีนามสกุล) ที่สัมพันธ์กับไดเร็กทอรีฐาน (ดู การกำหนดพาธ)
คุณจะต้องเพิ่ม :show
handler ไปที่ 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
มาร์กดาวน์ที่แสดงผลของเพจจะพร้อมใช้งานในการกำหนด 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 หากคุณกำลังตั้งค่าตัวแปร 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 และข้อผิดพลาดในการคอมไพล์จะหายไปหากไม่มีหน้าใดๆ คีย์-ค่าต้องอยู่ท้ายรายการ และจะถือว่าเป็นทางเลือกโดยการกำหนดค่าเริ่มต้น ค่าของส่วนหน้าใดๆ ที่ไม่ได้กำหนดไว้ในรายการแอตทริบิวต์จะถูกละทิ้งโดยไม่แจ้งให้ทราบ
ค่าแอตทริบิวต์ที่ถูกต้องจะมีอยู่ในการมอบหมาย:
< main >
< h1 > <%= @title % > </ h1 >
< h2 :if = {@author} > <%= @author % > </ h2 >
<%= @inner_content % >
</ main >
Phoenix Pages ใช้โปรเจ็กต์แต่งหน้าสำหรับการเน้นไวยากรณ์ หากต้องการเปิดใช้งาน ให้เพิ่ม lexer สำหรับภาษาเฉพาะของคุณในการขึ้นต่อกันของโปรเจ็กต์ Phoenix Pages จะรับการขึ้นต่อกันใหม่ และเริ่มเน้นบล็อกโค้ดของคุณโดยไม่ต้องกำหนดค่าเพิ่มเติมใดๆ ไม่มี lexers รวมอยู่ตามค่าเริ่มต้น
`{: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 lexer ใหม่เพื่อสนับสนุนชุมชน มิฉะนั้น คุณสามารถใช้ปากกาเน้นข้อความไวยากรณ์แบบ JS เช่น highlight.js ได้โดยการตั้งค่า code_class_prefix: "language-"
และ syntax_highlighting: false
ใน render_options
จากนั้น นำเข้าธีมที่แสดงด้านล่างลงในชุด 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
flag ไปที่ 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
คุณสามารถกำหนด pages "/$2", from: "priv/pages/**/*.md"
โดยใช้ $2
แทน :page
สิ่งนี้จะสร้างสองเส้นทาง: get "/foo"
และ get "/bar"
ตัวแปรกลุ่มการจับภาพจะมีค่าของส่วน **
และ *
ตามลำดับ เริ่มต้นที่ $1
โปรดทราบว่า **
จะจับคู่ไฟล์ทั้งหมด รวมถึงไดเรกทอรีและไดเรกทอรีย่อยตั้งแต่ศูนย์ขึ้นไป และ *
จะจับคู่อักขระจำนวนเท่าใดก็ได้จนถึงจุดสิ้นสุดของชื่อไฟล์ จุดถัดไป หรือเครื่องหมายทับถัดไป
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับรูปแบบไวด์การ์ด โปรดดูที่ Path.wildcard/2
นอกเหนือจากตัวเลือกมาร์กดาวน์ที่ปรับแต่งได้ การแสดงผลมาร์กดาวน์ยังรองรับแอตทริบิวต์ IAL ตามค่าเริ่มต้น หมายความว่าคุณสามารถเพิ่มแอตทริบิวต์ HTML ให้กับองค์ประกอบระดับบล็อกได้โดยใช้ไวยากรณ์ {:attr}
ตัวอย่างเช่น หากต้องการสร้างเอาต์พุตที่แสดงผลของ <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)$ " ,
# ...
]
]