เฟรมเวิร์ก Phoenix ได้รับความนิยมเพิ่มขึ้นอย่างรวดเร็วโดยนำเสนอประสิทธิภาพของเฟรมเวิร์กเช่น Ruby on Rails ในขณะเดียวกันก็เป็นหนึ่งใน เฟรมเวิร์กที่เร็วที่สุด ใช้ได้ มันทำลายตำนานที่คุณต้องเสียสละประสิทธิภาพเพื่อเพิ่มผลผลิต
ฟีนิกซ์คืออะไร?
Phoenix เป็นเว็บเฟรมเวิร์กที่สร้างขึ้นด้วยภาษาโปรแกรม Elixir Elixir ซึ่งสร้างขึ้นบน Erlang VM ใช้สำหรับการสร้างระบบแบบกระจายที่มีเวลาแฝงต่ำทนต่อความผิดพลาดซึ่งเป็นคุณสมบัติที่จำเป็นมากขึ้นของเว็บแอปพลิเคชันสมัยใหม่ คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับ Elixir ได้จาก โพสต์บล็อกนี้ หรือของพวกเขา คู่มืออย่างเป็นทางการ .
หากคุณเป็น ผู้พัฒนา Ruby on Rails คุณควรสนใจฟีนิกซ์อย่างแน่นอนเนื่องจากประสิทธิภาพที่ได้รับตามสัญญา นักพัฒนาของเฟรมเวิร์กอื่น ๆ สามารถติดตามเพื่อดูว่าฟีนิกซ์มีแนวทางในการพัฒนาเว็บอย่างไร
ในบทความนี้เราจะเรียนรู้บางสิ่งในฟีนิกซ์ที่คุณควรจำไว้หากคุณมาจากโลกของ Ruby on Rails
ซึ่งแตกต่างจาก Ruby Elixir เป็นภาษาการเขียนโปรแกรมที่ใช้งานได้ซึ่งอาจเป็นความแตกต่างที่ยิ่งใหญ่ที่สุดที่คุณอาจต้องจัดการ อย่างไรก็ตามมีความแตกต่างที่สำคัญบางประการระหว่างสองแพลตฟอร์มนี้ที่ทุกคนที่เรียนรู้ฟีนิกซ์ควรทราบเพื่อเพิ่มประสิทธิผลสูงสุด
นี่เป็นขนาดเล็ก แต่ง่ายต่อการเลอะหากคุณมาจาก Ruby on Rails
ในฟีนิกซ์การประชุมคือการเขียนทุกอย่างในรูปเอกพจน์ ดังนั้นคุณจะมี“ UserController” แทนที่จะเป็น“ UsersController” เหมือนที่คุณทำใน Ruby on Rails สิ่งนี้ใช้ได้ทุกที่ยกเว้นเมื่อตั้งชื่อตารางฐานข้อมูลโดยที่หลักการคือการตั้งชื่อตารางในรูปพหูพจน์
นี่อาจไม่ใช่เรื่องใหญ่หลังจากเรียนรู้ว่าจะใช้รูปพหูพจน์ใน Ruby on Rails เมื่อใดและที่ไหน แต่ในตอนแรกมันค่อนข้างสับสนเมื่อฉันเรียนรู้ที่จะใช้ Ruby on Rails และฉันแน่ใจว่ามันทำให้หลาย ๆ คนสับสน อื่น ๆ เช่นกัน ฟีนิกซ์ช่วยให้คุณไม่ต้องกังวลอีกเรื่องหนึ่ง
Phoenix และ Ruby on Rails มีความคล้ายคลึงกันมากในการกำหนดเส้นทาง ความแตกต่างที่สำคัญคือวิธีที่คุณสามารถควบคุมวิธีดำเนินการตามคำขอได้
ใน Ruby on Rails (และแอปพลิเคชัน Rack อื่น ๆ ) จะทำผ่านมิดเดิลแวร์ในขณะที่ Phoenix จะทำโดยใช้สิ่งที่เรียกว่า 'ปลั๊ก'
ปลั๊กคือสิ่งที่คุณใช้ในการประมวลผลการเชื่อมต่อ
ตัวอย่างเช่นมิดเดิลแวร์ Rails::Rack::Logger
บันทึกคำขอใน Rails ซึ่งใน Phoenix จะดำเนินการด้วย Plug.Logger
ปลั๊ก โดยพื้นฐานแล้วสิ่งที่คุณสามารถทำได้ในมิดเดิลแวร์ของ Rack สามารถทำได้โดยใช้ปลั๊ก
นี่คือตัวอย่างของเราเตอร์ใน Phoenix:
defmodule HelloWorld.Router do use HelloWorld.Web, :router pipeline :browser do plug :accepts, ['html'] plug :fetch_session plug :fetch_flash plug :protect_from_forgery plug :put_secure_browser_headers end pipeline :api do plug :accepts, ['json'] end scope '/', HelloWorld do pipe_through :browser get '/', HelloController, :index end scope '/api', HelloWorld do pipe_through :api end end
ขั้นแรกมาดูที่ท่อ
นี่คือกลุ่มปลั๊กที่คำขอจะเดินทางไป คิดว่าเป็นสแต็คมิดเดิลแวร์ สิ่งเหล่านี้สามารถใช้เพื่อตรวจสอบว่าคำขอต้องการ HTML ดึงข้อมูลเซสชันและตรวจสอบว่าคำขอนั้นปลอดภัย ทั้งหมดนี้เกิดขึ้นก่อนถึงคอนโทรลเลอร์
เนื่องจากคุณสามารถระบุไปป์ไลน์ได้หลายรายการคุณจึงเลือกปลั๊กที่จำเป็นสำหรับเส้นทางเฉพาะได้ ตัวอย่างเช่นในเราเตอร์ของเราเรามีไปป์ไลน์ที่แตกต่างกันสำหรับการร้องขอเพจ (HTML) และ API (JSON)
มันไม่สมเหตุสมผลเสมอไปที่จะใช้ไปป์ไลน์เดียวกันสำหรับคำขอประเภทต่างๆ ฟีนิกซ์ทำให้เรามีความยืดหยุ่น
มุมมองใน Phoenix ไม่เหมือนกับมุมมองใน Ruby on Rails
มุมมองใน Phoenix รับผิดชอบเทมเพลตการแสดงผลและจัดเตรียมฟังก์ชันที่ทำให้ข้อมูลดิบง่ายขึ้นสำหรับเทมเพลตที่จะใช้ มุมมองใน Phoenix ใกล้เคียงที่สุดกับตัวช่วยใน Ruby on Rails ด้วยการเพิ่มการแสดงผลเทมเพลต
ลองเขียนตัวอย่างที่แสดงว่า“ สวัสดีชาวโลก!” ในเบราว์เซอร์
แอป / ตัวควบคุม / hello_controller.rb:
class HelloController app / views / hello / index.html.erb:
') end end
คุณสามารถลบเทมเพลตและใช้ฟังก์ชันการเรนเดอร์ใหม่และคุณจะได้ผลลัพธ์เดียวกัน สิ่งนี้มีประโยชน์เมื่อคุณต้องการส่งคืนข้อความหรือแม้แต่ JSON
โมเดลเป็นเพียงแบบจำลองข้อมูล
ในฟีนิกซ์โมเดลจะจัดการกับการตรวจสอบข้อมูลสคีมาความสัมพันธ์กับโมเดลอื่น ๆ และวิธีการนำเสนอเป็นหลัก
การระบุสคีมาในโมเดลอาจฟังดูแปลก ๆ ในตอนแรก แต่จะช่วยให้คุณสร้างฟิลด์ 'เสมือน' ได้อย่างง่ายดายซึ่งเป็นฟิลด์ที่ไม่อยู่ในฐานข้อมูล ลองดูตัวอย่าง:
defmodule HelloPhoenix.User do use HelloPhoenix.Web, :model schema 'users' do field :name, :string field :email, :string field :password, :string, virtual: true field :password_hash, :string end end
ที่นี่เรากำหนดฟิลด์สี่ฟิลด์ในตาราง 'ผู้ใช้': ชื่ออีเมลรหัสผ่านและ password_hash
ไม่ค่อยมีอะไรน่าสนใจที่นี่ยกเว้นช่อง 'รหัสผ่าน' ซึ่งตั้งค่าเป็น 'เสมือน'
ซึ่งทำให้เราสามารถตั้งค่าและรับฟิลด์นี้ได้โดยไม่ต้องบันทึกการเปลี่ยนแปลงลงในฐานข้อมูล มันมีประโยชน์เพราะเราจะมีตรรกะในการแปลงรหัสผ่านนั้นเป็นแฮชซึ่งเราจะบันทึกลงในช่อง“ password_hash” แล้วบันทึกลงในฐานข้อมูล
คุณยังต้องสร้างการย้ายข้อมูล สคีมาในโมเดลเป็นสิ่งจำเป็นเนื่องจากฟิลด์ไม่ได้ถูกโหลดลงในโมเดลโดยอัตโนมัติเหมือนใน Ruby on Rails
ความแตกต่างระหว่าง Phoenix และ Ruby on Rails คือโมเดลไม่รองรับการคงอยู่ของฐานข้อมูล สิ่งนี้ได้รับการจัดการโดยโมดูลที่เรียกว่า“ Repo” ซึ่งกำหนดค่าด้วยข้อมูลฐานข้อมูลดังที่แสดงในตัวอย่างด้านล่าง:
config :my_app, Repo, adapter: Ecto.Adapters.Postgres, database: 'ecto_simple', username: 'postgres', password: 'postgres', hostname: 'localhost'
รหัสนี้รวมอยู่ในไฟล์คอนฟิกูเรชันเฉพาะสภาพแวดล้อมเช่น config/dev.exs
หรือ config/test.exs
. สิ่งนี้ทำให้เราสามารถใช้ Repo เพื่อดำเนินการกับฐานข้อมูลเช่นสร้างและอัปเดต
Repo.insert(%User{name: 'John Smith', example: ' [email protected] '}) do {:ok, user} -> # Insertion was successful {:error, changeset} -> # Insertion failed end
นี่เป็นตัวอย่างทั่วไปในคอนโทรลเลอร์ใน Phoenix
เราให้ชื่อผู้ใช้และอีเมลและ Repo พยายามสร้างบันทึกใหม่ลงในฐานข้อมูล จากนั้นเราสามารถเลือกที่จะจัดการกับความพยายามที่สำเร็จหรือล้มเหลวดังที่แสดงในตัวอย่าง
เพื่อให้เข้าใจรหัสนี้ดีขึ้นคุณต้องเข้าใจการจับคู่รูปแบบใน Elixir ค่าที่ฟังก์ชันส่งคืนคือทูเปิล ฟังก์ชันจะส่งคืนทูเพิลของสองค่าสถานะจากนั้นโมเดลหรือชุดการเปลี่ยนแปลง ชุดการเปลี่ยนแปลงเป็นวิธีติดตามการเปลี่ยนแปลงและตรวจสอบความถูกต้องของโมเดล (ชุดการเปลี่ยนแปลงจะกล่าวถึงในหัวข้อถัดไป)
ทูเปิลตัวแรกจากบนลงล่างที่ตรงกับรูปแบบของทูเปิลที่ส่งคืนโดยฟังก์ชันที่พยายามแทรกผู้ใช้ลงในฐานข้อมูลจะเรียกใช้ฟังก์ชันที่กำหนดไว้
เราสามารถตั้งค่าตัวแปรสำหรับสถานะแทนอะตอม (ซึ่งโดยพื้นฐานแล้วสัญลักษณ์คืออะไรในรูบี้) แต่จากนั้นเราจะจับคู่ทั้งความพยายามที่สำเร็จและล้มเหลวและจะใช้ฟังก์ชันเดียวกันสำหรับทั้งสองสถานการณ์ การระบุอะตอมที่เราต้องการจับคู่เรากำหนดฟังก์ชันเฉพาะสำหรับสถานะนั้น
Ruby on Rails มีฟังก์ชันการคงอยู่ในตัวแบบจำลองผ่าน ActiveRecord สิ่งนี้จะเพิ่มความรับผิดชอบให้กับโมเดลมากขึ้นและบางครั้งอาจทำให้การทดสอบโมเดลมีความซับซ้อนมากขึ้น ในฟีนิกซ์สิ่งนี้ถูกแยกออกจากกันด้วยวิธีที่เหมาะสมและป้องกันการท้องอืดทุกรุ่นด้วยตรรกะการคงอยู่
การเปลี่ยนแปลงช่วยให้สามารถตรวจสอบความถูกต้องและกฎการเปลี่ยนแปลงได้อย่างชัดเจน
ใน Ruby on Rails การตรวจสอบความถูกต้องและการแปลงข้อมูลอาจเป็นที่มาของข้อบกพร่องที่ยากต่อการค้นหา เนื่องจากไม่ชัดเจนในทันทีเมื่อข้อมูลถูกเปลี่ยนโดยการเรียกกลับเช่น“ before_create” และการตรวจสอบความถูกต้อง
ใน Phoenix คุณทำการตรวจสอบความถูกต้องและการเปลี่ยนแปลงเหล่านี้อย่างชัดเจนโดยใช้ชุดการเปลี่ยนแปลง นี่เป็นหนึ่งในฟีเจอร์โปรดของฉันในฟีนิกซ์
มาดูชุดการเปลี่ยนแปลงโดยการเพิ่มชุดการเปลี่ยนแปลงในรุ่นก่อนหน้านี้:
defmodule HelloPhoenix.User do use HelloPhoenix.Web, :model schema 'users' do field :name, :string field :email, :string field :password, :string, virtual: true field :password_hash, :string end def changeset(struct, params \ %{}) do struct |> cast(params, [:name, :email, :password]) |> validate_required([:email, :password]) end end
ที่นี่ชุดการเปลี่ยนแปลงทำสองสิ่ง
ขั้นแรกเรียกฟังก์ชัน 'cast' ซึ่งเป็นรายการที่อนุญาตพิเศษของช่องที่อนุญาตคล้ายกับ 'strong_parameters' ใน Ruby on Rails จากนั้นจะตรวจสอบความถูกต้องว่ารวมช่อง 'อีเมล' และ 'รหัสผ่าน' ไว้ด้วยทำให้ฟิลด์ 'ชื่อ' ไม่จำเป็น. ด้วยวิธีนี้ผู้ใช้จะแก้ไขเฉพาะฟิลด์ที่คุณอนุญาตเท่านั้น
สิ่งที่ดีเกี่ยวกับแนวทางนี้คือเราไม่ได้ จำกัด เฉพาะชุดการเปลี่ยนแปลงเดียว เราสามารถสร้างชุดการเปลี่ยนแปลงได้หลายชุด ชุดการเปลี่ยนแปลงสำหรับการลงทะเบียนและชุดหนึ่งสำหรับการอัปเดตผู้ใช้เป็นเรื่องปกติ บางทีเราต้องการเฉพาะฟิลด์รหัสผ่านเมื่อลงทะเบียน แต่ไม่ใช่เมื่ออัปเดตผู้ใช้
เปรียบเทียบวิธีนี้กับสิ่งที่ทำกันทั่วไปใน Ruby on Rails คุณจะต้องระบุว่าการตรวจสอบความถูกต้องควรจะรันบน 'สร้าง' เท่านั้น บางครั้งสิ่งนี้ทำให้ยากใน Rails เพื่อดูว่าโค้ดของคุณกำลังทำอะไรเมื่อคุณมีโมเดลที่ซับซ้อน
ฟังก์ชันการนำเข้านั้นตรงไปตรงมา แต่มีความยืดหยุ่น
ฟังก์ชันส่วนใหญ่ของไลบรารีที่เรียกว่า“ Ecto” ถูกนำเข้ามาในโมเดล โมเดลมักจะมีบรรทัดนี้ใกล้ด้านบน:
use HelloPhoenix.Web, :model
โมดูล“ HelloPhoenix.Web” อยู่ใน“ web / web.ex” ในโมดูลควรมีฟังก์ชันที่เรียกว่า“ model” ดังนี้:
def model do quote do use Ecto.Schema import Ecto import Ecto.Changeset import Ecto.Query end end
คุณสามารถดูได้ว่าเรากำลังนำเข้าโมดูลใดจาก Ecto คุณสามารถลบหรือเพิ่มโมดูลอื่น ๆ ที่คุณต้องการได้ที่นี่และจะนำเข้าสู่ทุกรุ่น
นอกจากนี้ยังมีฟังก์ชันที่คล้ายกันเช่น 'มุมมอง' และ 'ตัวควบคุม' ซึ่งตอบสนองจุดประสงค์เดียวกันสำหรับมุมมองและตัวควบคุมตามลำดับ
quote
และ use
คำหลักอาจดูสับสน สำหรับตัวอย่างนี้คุณสามารถคิดว่าอัญประกาศเรียกใช้โค้ดนั้นโดยตรงในบริบทของโมดูลที่เรียกใช้ฟังก์ชันนั้น ดังนั้นมันจะเทียบเท่ากับการเขียนโค้ดภายใน quote ในโมดูล
คำหลักใช้ยังช่วยให้คุณสามารถเรียกใช้โค้ดในบริบทของตำแหน่งที่เรียก โดยพื้นฐานแล้วต้องใช้โมดูลที่ระบุจากนั้นเรียก __using__
แมโครบนโมดูลที่เรียกใช้ในบริบทของตำแหน่งที่เรียก คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ อ้าง และ ใช้ ในคู่มืออย่างเป็นทางการ
สิ่งนี้ช่วยให้คุณเข้าใจว่าฟังก์ชันบางอย่างอยู่ที่ใดในเฟรมเวิร์กและช่วยลดความรู้สึกว่าเฟรมเวิร์กกำลังสร้าง 'เวทมนตร์' มากมาย
ภาวะพร้อมกันเป็นหัวใจหลัก
การทำงานพร้อมกันมาฟรีในฟีนิกซ์เนื่องจากเป็นคุณสมบัติหลักของ Elixir คุณจะได้รับแอปพลิเคชันที่สามารถสร้างกระบวนการต่างๆและทำงานบนหลายคอร์โดยไม่ต้องกังวลเรื่องความปลอดภัยและความน่าเชื่อถือของเธรด
คุณสามารถสร้างกระบวนการใหม่ใน Elixir ได้ง่ายๆดังนี้:
spawn fn -> 1 + 2 end
ทุกอย่างหลังจาก spawn
และก่อนหน้านี้ end
จะทำงานในกระบวนการใหม่
ในความเป็นจริงทุกคำขอใน Phoenix ได้รับการจัดการในกระบวนการของตัวเอง Elixir ใช้พลังของ Erlang VM เพื่อนำเสนอการทำงานพร้อมกันที่เชื่อถือได้และมีประสิทธิภาพ“ ฟรี”
นอกจากนี้ยังทำให้ Phoenix เป็นตัวเลือกที่ยอดเยี่ยมสำหรับการเรียกใช้บริการที่ใช้ WebSockets เนื่องจาก WebSockets จำเป็นต้องรักษาการเชื่อมต่อแบบเปิดระหว่างไคลเอนต์และเซิร์ฟเวอร์ (ซึ่งหมายความว่าคุณต้องสร้างแอปพลิเคชันของคุณเพื่อให้สามารถรองรับการเชื่อมต่อพร้อมกันได้หลายพันรายการ)
ข้อกำหนดเหล่านี้จะเพิ่มความซับซ้อนให้กับโปรเจ็กต์ที่สร้างบน Ruby on Rails แต่ Phoenix สามารถตอบสนองความต้องการเหล่านี้ได้ฟรีผ่าน Elixir
หากคุณต้องการใช้ WebSockets ในแอปพลิเคชัน Phoenix ของคุณคุณจะต้องใช้ ช่อง . มันเทียบเท่ากับ ActionCable
ใน Ruby on Rails แต่การตั้งค่ามีความซับซ้อนน้อยกว่าเนื่องจากคุณไม่จำเป็นต้องเรียกใช้เซิร์ฟเวอร์แยกต่างหาก
Phoenix ทำให้การสร้างเว็บแอปสมัยใหม่ไม่ยุ่งยาก
แม้ว่าเราจะมุ่งเน้นไปที่ความแตกต่างเป็นส่วนใหญ่ แต่ Phoenix ก็มีบางสิ่งที่เหมือนกันกับ Ruby on Rails
Phoenix เป็นไปตามรูปแบบ MVC แบบเดียวกับ Ruby on Rails ดังนั้นการหาว่าโค้ดไปที่ใดไม่ควรเป็นเรื่องยากในตอนนี้ที่คุณรู้เกี่ยวกับความแตกต่างหลัก ๆ Phoenix ยังมีเครื่องกำเนิดไฟฟ้าที่คล้ายกับ Ruby on Rails สำหรับการสร้างโมเดลตัวควบคุมการโยกย้ายและอื่น ๆ
หลังจากเรียนรู้ Elixir แล้วคุณจะเข้าใกล้ระดับการผลิตของ Ruby on Rails อย่างช้าๆเมื่อคุณคุ้นเคยกับ Phoenix มากขึ้น
ไม่กี่ครั้งที่ฉันรู้สึกว่าไม่มีประสิทธิผลก็คือเมื่อฉันพบปัญหาที่ได้รับการแก้ไขโดยอัญมณีใน Ruby และฉันไม่พบห้องสมุดที่คล้ายกันสำหรับ Elixir โชคดีที่ช่องว่างเหล่านี้ถูกเติมเต็มอย่างช้าๆโดยชุมชน Elixir ที่กำลังเติบโต
ความแตกต่างของ Phoenix ช่วยแก้ปัญหาความเจ็บปวดมากมายที่มาพร้อมกับการจัดการโครงการ Ruby on Rails ที่ซับซ้อน แม้ว่าพวกเขาจะไม่สามารถแก้ปัญหาได้ทั้งหมด แต่ก็ช่วยผลักดันคุณไปในทิศทางที่ถูกต้อง การเลือกกรอบงานที่ช้าเพราะคุณต้องการผลผลิตไม่ใช่ข้ออ้างที่ถูกต้องอีกต่อไป Phoenix ให้คุณมีทั้งสองอย่าง