อาจมีสองความคิดหลักในหัวของคุณในขณะที่คุณอ่านบทนำนี้:
แต่เดี๋ยวก่อน! นั่นคือสิ่งที่คุณคิดผิดและฉันจะพิสูจน์ให้คุณเห็น หากคุณยอมสละเวลาสัก 10 นาทีฉันจะแสดงให้คุณเห็นว่า ClojureScript ช่วยให้การเขียนแอปพลิเคชัน front-end และ React-y เป็นเรื่องสนุกรวดเร็วและที่สำคัญที่สุดได้อย่างไร การทำงาน .
ดังนั้นคุณจึงมีเวลาไม่มากในการเรียนรู้ ClojureScript และคุณแค่อยากรู้ว่าสิ่งทั้งหมดนี้กำลังไปที่ใด ก่อนอื่น ClojureScript คืออะไร?
จากเว็บไซต์ ClojureScript: ClojureScript เป็นคอมไพเลอร์สำหรับ Clojure ที่กำหนดเป้าหมายเป็น JavaScript มันปล่อยรหัส JavaScript ซึ่งเข้ากันได้กับโหมดการรวบรวมขั้นสูงของคอมไพเลอร์การเพิ่มประสิทธิภาพการปิด Google
เหนือสิ่งอื่นใด ClojureScript มีข้อเสนอมากมาย:
เมื่อไม่เป็นเช่นนั้นเรามาเปิดตัวอย่างเวิร์มกระป๋องนี้ด้วย:
(defn component [] [:div 'Hello, world!'])
หมายเหตุสำหรับผู้ที่ไม่คุ้นเคยกับภาษา Lisp หรือ ClojureScript: ส่วนที่สำคัญที่สุดของตัวอย่างนี้ ได้แก่ :div
, []
และ ()
:div
เป็นคำหลักที่แสดงถึงองค์ประกอบ []
เป็นเวกเตอร์เหมือนกับ ArrayList
ใน Java และ ()
เป็นลำดับเหมือนกับ LinkedList
ฉันจะอธิบายรายละเอียดเพิ่มเติมในภายหลังในโพสต์นี้!
นี่คือรูปแบบพื้นฐานที่สุดขององค์ประกอบการตอบสนองใน ClojureScript เพียงแค่คำหลักสตริงและรายการทั้งหมด
ฮี้ คุณพูดว่าไม่ได้แตกต่างจาก“ hello world” ใน JSX หรือ TSX อย่างมีนัยสำคัญ:
function component() { return ( 'Hello, world!' ); }
อย่างไรก็ตามมีความแตกต่างที่สำคัญบางอย่างที่เราสามารถมองเห็นได้จากตัวอย่างพื้นฐานนี้:
ความแตกต่างเล็ก ๆ น้อย ๆ ทั้งสองนี้มีผลกระทบอย่างมากไม่ใช่เฉพาะกับคุณ เขียน รหัส แต่ในการแสดงออกของคุณเช่นกัน!
คุณถามว่าเป็นอย่างไร? มาเข้าร่วมการต่อสู้และดูว่า ClojureScript มีอะไรอีกบ้างสำหรับเรา ...
ตลอดบทแนะนำ ClojureScript นี้ฉันจะพยายามอย่างยิ่งที่จะไม่ขุดคุ้ยว่ามันคืออะไรที่ทำให้ Clojure [Script] ยอดเยี่ยม (ซึ่งมีหลายอย่าง แต่ฉันพูดนอกเรื่อง) อย่างไรก็ตามการมีแนวคิดพื้นฐานบางอย่างครอบคลุมจะเป็นประโยชน์ดังนั้นจึงเป็นไปได้ที่จะเข้าใจสิ่งที่เราทำได้ที่นี่อย่างกว้างขวาง
สำหรับ Clojuristas และ Lispians ที่มีประสบการณ์อย่าลังเลที่จะข้ามไปยังส่วนถัดไป!
มีแนวคิดหลักสามประการที่ฉันต้องพูดถึงก่อน:
Clojure [Script] มีแนวคิดที่เรียกว่า Keyword มันอยู่ที่ไหนสักแห่งระหว่างสตริงคงที่ (พูดใน Java) และคีย์ พวกเขาคือ ตัวบ่งชี้เชิงสัญลักษณ์ที่ประเมินตนเอง .
ดังตัวอย่างคำหลัก :cat
จะอ้างถึง :cat
เสมอ และไม่เคยทำอย่างอื่น เช่นเดียวกับใน Java คุณอาจพูดว่า:
private static const String MY_KEY = 'my_key'; // ... myMap.put(MY_KEY, thing); // ... myMap.get(MY_KEY);
…ใน Clojure คุณจะมี:
การออกแบบมัลติมีเดีย vs การออกแบบกราฟิก
(assoc my-map :my-key thing) (my-map :my-key) ; equivalent to (:my-key my-map) ...nice and flexible!
โปรดทราบ: ใน Clojure แผนที่เป็นทั้งคอลเลกชัน (ของคีย์สู่ค่าเช่นเดียวกับ Java HashMap
) และฟังก์ชันสำหรับเข้าถึงเนื้อหา เรียบร้อย!
Clojure [Script] การเป็นภาษา Lisp หมายความว่ามันให้ความสำคัญกับรายการมาก ดังที่ฉันได้กล่าวไว้ก่อนหน้านี้มีสองสิ่งหลักที่ต้องระวัง:
[]
เป็นเวกเตอร์เหมือนกับ ArrayList
()
เป็นลำดับเหมือนกับ LinkedList
ในการสร้างรายการสิ่งต่างๆใน Clojure [Script] ให้ทำดังต่อไปนี้:
[1 2 3 4] ['hello' 'world'] ['my' 'list' 'contains' 10 'things'] ; you can mix and match types ; in Clojure lists!
สำหรับลำดับจะแตกต่างกันเล็กน้อย:
'(1 2 3 4) '('hello' 'world')
ก่อนหน้า '
จะอธิบายในหัวข้อถัดไป
สุดท้ายเรามีฟังก์ชัน ฟังก์ชันใน Clojure [Script] คือ a ลำดับ ที่พิมพ์ออกมา โดยไม่ต้องจ่ายล่วงหน้า '
. องค์ประกอบแรกของรายการนั้นคือฟังก์ชันเองและองค์ประกอบต่อไปนี้ทั้งหมดจะเป็นไฟล์ ข้อโต้แย้ง . ตัวอย่างเช่น:
(+ 1 2 3 4) ; -> 10 (str 'hello' ' ' 'world') ; -> 'hello world' (println 'hi!') ; prints 'hi!' to the console (run-my-function) ; runs the function named `run-my-function`
ข้อพิสูจน์อย่างหนึ่งของพฤติกรรมนี้คือคุณสามารถสร้างคำจำกัดความของฟังก์ชันได้โดยไม่ต้องดำเนินการจริง! ระบบจะดำเนินการตามลำดับ 'เปล่า' เมื่อโปรแกรมได้รับการประเมิน
(+ 1 1) ; -> 2 '(+ 1 1); -> a list of a function and two numbers
สิ่งนี้จะเกี่ยวข้องในภายหลัง!
ฟังก์ชันสามารถกำหนดได้สองสามวิธี:
; A normal function definition, assigning the function ; to the symbol `my-function` (defn my-function [arg1 arg2] (+ arg1 arg2)) ; An anonymous function that does the same thing as the above (fn [arg1 arg2] (+ arg1 arg2)) ; Another, more concise variation of the above #(+ %1 %2)
ตอนนี้เราได้ครอบคลุมพื้นฐานแล้วเรามาเจาะลึกรายละเอียดเพิ่มเติมเพื่อดูว่าเกิดอะไรขึ้นที่นี่
โดยทั่วไปการตอบสนองใน ClojureScript ทำได้โดยใช้ไลบรารีที่เรียกว่า รีเอเจนต์ . Reagent ใช้ Hiccup และไวยากรณ์เพื่อแสดง HTML จาก Hiccup repo วิกิของ:
“ อาการสะอึกทำให้โครงสร้างข้อมูล Clojure เป็นแบบนี้”
[:a {:href 'http://github.com'} 'GitHub']
“ เป็นสตริงของ HTML เช่นนี้”
GitHub
ใส่เพียงแค่ องค์ประกอบแรก ของรายการจะกลายเป็นประเภทองค์ประกอบ HTML และส่วนที่เหลือจะกลายเป็นเนื้อหาขององค์ประกอบนั้น คุณสามารถระบุแผนที่แอตทริบิวต์ซึ่งจะแนบไปกับองค์ประกอบนั้นได้
องค์ประกอบต่างๆสามารถซ้อนกันได้ง่ายๆเพียงแค่นำองค์ประกอบเหล่านี้มาซ้อนกันในรายชื่อผู้ปกครอง! สิ่งนี้เห็นได้ง่ายที่สุดด้วยตัวอย่าง:
[:div [:h1 'This is a header'] [:p 'And in the next element we have 1 + 1'] [:p (+ 1 1)]]
สังเกตว่าเราสามารถใส่ฟังก์ชันเก่าหรือไวยากรณ์ Clojure ทั่วไปภายในโครงสร้างของเราได้อย่างไรโดยไม่ต้องประกาศวิธีการฝังอย่างชัดเจน มันเป็นเพียงรายการเท่านั้น!
และยิ่งไปกว่านั้นสิ่งนี้ประเมินผลกับรันไทม์อย่างไร
[:div [:h1 'This is a header'] [:p 'And in the next element we have 1 + 1'] [:p 2]]
รายการคีย์เวิร์ดและเนื้อหาแน่นอน! ไม่มีประเภทตลกไม่มีเวทมนตร์ซ่อนเร้น มันเป็นเพียงรายการเก่า ๆ ธรรมดา ๆ คุณสามารถต่อและเล่นกับรายการนี้ได้มากเท่าที่คุณต้องการสิ่งที่คุณเห็นคือสิ่งที่คุณจะได้รับ
เมื่อสะอึกทำเค้าโครงและรีเอเจนต์ทำตรรกะและการประมวลผลเหตุการณ์เราจะได้สภาพแวดล้อมการตอบสนองที่ทำงานได้อย่างสมบูรณ์
เอาล่ะมารวมกันอีกเล็กน้อยกับส่วนประกอบบางอย่าง สิ่งมหัศจรรย์อย่างหนึ่งเกี่ยวกับ React (และ Reagent) คือคุณแบ่งมุมมองและเลย์เอาต์ตรรกะของคุณเป็นโมดูลซึ่งคุณสามารถนำมาใช้ซ้ำได้ตลอดทั้งแอปพลิเคชันของคุณ
สมมติว่าเราสร้างส่วนประกอบง่ายๆที่แสดงปุ่มและตรรกะง่ายๆ:
; widget.cljs (defn component [polite?] [:div [:p (str 'Do not press the button' (when polite? ', please.'))] [:input {:type 'button' :value 'PUSH ME' :on-click #(js/alert 'What did I tell you?')}]])
หมายเหตุสั้น ๆ เกี่ยวกับการตั้งชื่อ: โมดูลใน Clojure มักจะใช้เนมสเปซดังนั้น widget.cljs
อาจถูกนำเข้าภายใต้เนมสเปซ widget
ซึ่งหมายความว่าระดับบนสุด component
จะเข้าถึงฟังก์ชันเป็น widget/component
ฉันต้องการให้มีส่วนประกอบระดับบนสุดเพียงรายการเดียวต่อโมดูล แต่นี่เป็นลักษณะที่ต้องการคุณอาจต้องการตั้งชื่อฟังก์ชันส่วนประกอบของคุณเช่น polite-component
หรือ widget-component
.
ส่วนประกอบง่ายๆนี้นำเสนอวิดเจ็ตที่สุภาพซึ่งเป็นทางเลือกให้กับเรา (when polite? ', please.')
ประเมินเป็น ', please.'
เมื่อ polite? == true
และถึง nil
เมื่อเป็น false
.
ตอนนี้มาฝังไว้ใน app.cljs
ของเรา:
(defn app [] [:div [:h1 'Welcome to my app'] [widget/component true]])
ที่นี่เราฝังวิดเจ็ตของเราไว้ในส่วนประกอบแอพของเราโดยเรียกมันว่าเป็นรายการแรกของรายการเช่นเดียวกับคำหลัก HTML! จากนั้นเราสามารถส่งต่อลูกหรือพารามิเตอร์ไปยังองค์ประกอบโดยจัดให้เป็นองค์ประกอบอื่น ๆ ในรายการเดียวกัน ที่นี่เราเพียงแค่ส่ง true
ดังนั้นในวิดเจ็ตของเรา polite? == true
ดังนั้นเราจึงได้รับเวอร์ชันสุภาพ
หากเราจะประเมินฟังก์ชันแอปของเราตอนนี้เราจะได้รับสิ่งต่อไปนี้:
[:div [:h1 'Welcome to my app'] [widget/component true]] ; <- widget/component would look more like a ; function reference, but I have kept it ; clean for legibility.
สังเกตวิธี widget/component
ยังไม่ได้รับการประเมิน! (ดู ส่วนฟังก์ชัน ถ้าคุณสับสน)
ส่วนประกอบภายในแผนผัง DOM ของคุณจะได้รับการประเมินเท่านั้น (และจะแปลงเป็นวัตถุ React จริงในเบื้องหลัง) หากได้รับการอัปเดตซึ่งช่วยให้สิ่งต่างๆดีและรวดเร็วและลดความซับซ้อนที่คุณต้องจัดการเมื่อใดก็ได้
สามารถดูรายละเอียดเพิ่มเติมเกี่ยวกับเรื่องนี้สำหรับผู้ที่สนใจได้ ในเอกสาร Reagent .
นอกจากนี้โปรดทราบว่า DOM เป็นเพียงรายการของรายการและส่วนประกอบเป็นเพียงฟังก์ชันที่ส่งกลับรายการของรายการ เหตุใดสิ่งนี้จึงสำคัญมากเมื่อคุณเรียนรู้ ClojureScript
เพราะ สิ่งที่คุณสามารถทำได้กับฟังก์ชันหรือรายการคุณสามารถทำได้กับส่วนประกอบ
ภาษาประกาศมักใช้สำหรับแอปพลิเคชันการผลิต
นี่คือจุดเริ่มต้นที่คุณจะได้รับผลตอบแทนแบบทบต้นโดยใช้ภาษา Lisp เช่น ClojureScript: ส่วนประกอบและองค์ประกอบ HTML ของคุณกลายเป็นวัตถุชั้นหนึ่งที่คุณสามารถจัดการได้เหมือนกับข้อมูลทั่วไปอื่น ๆ ! ให้ฉันพูดอีกครั้ง:
กวดวิชาขั้นสูง c++
ส่วนประกอบและองค์ประกอบ HTML เป็นออบเจ็กต์ที่รองรับชั้นหนึ่งภายในภาษา Clojure!
ถูกต้องคุณได้ยินฉัน เกือบจะเหมือนกับว่า Lisps ได้รับการออกแบบมาเพื่อประมวลผลรายการ (คำใบ้: พวกเขา)
ซึ่งรวมถึงสิ่งต่างๆเช่น:
(def words ['green' 'eggs' 'and' 'ham']) (defn li-shout [x] [:li (string/uppercase x)) (concat [:ol] (map li-shout words) ; becomes [:ol [:li 'GREEN'] [:li 'EGGS'] [:li 'AND'] [:li 'HAM']]
; in widget.cljs (defn greeting-component [name] [:div [:p (str 'Hiya ' name '!')]]) ; ... (def shouty-greeting-component #(widget/greeting-component (string/uppercase %))) (defn app [] [:div [:h1 'My App'] [shouty-greeting-component 'Luke']]) ; <- will show Hiya LUKE!
(def default-btn-attrs {:type 'button' :value 'I am a button' :class 'my-button-class'}) (defn two-button-component [] [:div [:input (assoc default-btn-attrs :on-click #(println 'I do one thing'))] [:input (assoc default-btn-attrs :on-click #(println 'I do a different thing'))]])
การจัดการกับประเภทข้อมูลเก่า ๆ ธรรมดา ๆ เช่นรายการและแผนที่นั้นง่ายกว่าอะไรที่มีลักษณะคล้ายคลาสมากและท้ายที่สุดก็จะมีประสิทธิภาพมากขึ้นในระยะยาว!
เอาล่ะมาสรุปกัน ตอนนี้บทช่วยสอน ClojureScript ของเราแสดงอะไรบ้าง
สองจุดนี้เข้ากันได้ดีกับ Clojure และ ethos การเขียนโปรแกรมเชิงฟังก์ชัน - โค้ดคือ ข้อมูล ต้องจัดการและสร้างความซับซ้อนผ่านการเชื่อมต่อส่วนที่ซับซ้อนน้อยกว่า เรานำเสนอโปรแกรมของเรา (หน้าเว็บของเราในตัวอย่างนี้) เป็นข้อมูล (รายการฟังก์ชันแผนที่) และเก็บไว้อย่างนั้นจนถึงช่วงเวลาสุดท้ายเมื่อ Reagent เข้ามาแทนที่และเปลี่ยนเป็น React code สิ่งนี้ทำให้โค้ดของเราสามารถใช้งานได้อีกครั้งและที่สำคัญที่สุดคืออ่านและเข้าใจได้ง่ายโดยใช้เวทมนตร์เพียงเล็กน้อย
ตอนนี้เรารู้วิธีสร้างแอปพลิเคชันที่มีฟังก์ชันพื้นฐานแล้วมาดูวิธีทำให้แอปพลิเคชันออกมาดูดีกัน มีสองวิธีในการเข้าถึงสิ่งนี้วิธีที่ง่ายที่สุดคือการใช้สไตล์ชีตและการอ้างอิงคลาสในส่วนประกอบของคุณ:
.my-class { color: red; }
[:div {:class 'my-class'} 'Hello, world!']
สิ่งนี้จะเป็นไปตามที่คุณคาดหวังนำเสนอเราด้วยสีแดงที่สวยงาม“ สวัสดีชาวโลก!” ข้อความ
อย่างไรก็ตามทำไมต้องไปที่ปัญหาทั้งหมดนี้ในการกำหนดมุมมองและรหัสตรรกะลงในส่วนประกอบของคุณ แต่จากนั้นแยกสไตล์ของคุณออกเป็นสไตล์ชีต - ไม่เพียง แต่ตอนนี้คุณต้องดูในสองที่ที่แตกต่างกันเท่านั้น แต่คุณต้องจัดการกับสองที่แตกต่างกัน ภาษาด้วย!
ทำไมไม่เขียน CSS ของเราเป็น รหัสในส่วนประกอบของเรา (ดูธีมที่นี่?) สิ่งนี้จะทำให้เรามีข้อดีมากมาย:
รสชาติ CSS-in-code ที่ฉันชอบคือ ชีทสไตล์ Clojure (cljss) . CSS ในตัวมีลักษณะดังนี้:
;; -- STYLES ------------------------------------------------------------ (defstyles component-style [] {:color 'red' :width '100%'}) ;; -- VIEW -------------------------------------------------------------- (defn component [] [:div {:class (component-style)} 'Hello, world!'])
defstyles
สร้างฟังก์ชันที่จะสร้างชื่อคลาสเฉพาะสำหรับเรา (ซึ่งเหมาะสำหรับทุกคนที่นำเข้าส่วนประกอบของเรา)
ยังมีสิ่งอื่น ๆ อีกมากมายที่ cljss สามารถทำให้คุณได้ (การแต่งสไตล์ภาพเคลื่อนไหวการลบล้างองค์ประกอบ ฯลฯ ) ซึ่งฉันจะไม่ลงรายละเอียดในที่นี้ ฉันขอแนะนำให้คุณตรวจสอบด้วยตัวคุณเอง!
สุดท้ายนี้ต้องใช้กาวในการติดทั้งหมดนี้เข้าด้วยกัน โชคดีที่นอกจากไฟล์โปรเจ็กต์และ index.html
แล้วยังมีเอกสารสำเร็จรูปเป็นอย่างน้อยที่นี่
คุณต้องการ:
project.clj
. วัตถุดิบหลักของโปรเจ็กต์ Clojure สิ่งนี้กำหนดการอ้างอิงของคุณ - โดยตรงจาก GitHub - และคุณสมบัติการสร้างอื่น ๆ (คล้ายกับ build.gradle
หรือ package.json
)index.html
ที่ทำหน้าที่เป็นจุดเชื่อมสำหรับแอปพลิเคชันรีเอเจนต์คุณจะพบตัวอย่างโค้ดแบบเต็มสำหรับบทแนะนำ ClojureScript นี้ พร้อมใช้งานบน GitHub .
เท่านี้ก็เรียบร้อย (สำหรับตอนนี้) หวังว่าอย่างน้อยฉันก็กระตุ้นความอยากรู้อยากเห็นของคุณได้เล็กน้อยไม่ว่าจะเป็นการตรวจสอบภาษา Lisp (Clojure [Script] หรืออื่น ๆ ) หรือแม้แต่ลองใช้แอปพลิเคชัน Reagent ของคุณเอง! ฉันสัญญาว่าคุณจะไม่เสียใจ
เข้าร่วมกับฉันในการติดตามบทความนี้ การเข้าสู่สถานะ ที่ฉันพูดถึงการจัดการของรัฐโดยใช้ กรอบใหม่ - ทักทายกับ Redux ใน ClojureScript!
React เป็นกรอบการพัฒนาส่วนหน้าสำหรับ JavaScript
ClojureScript เป็นคอมไพเลอร์สำหรับ Clojure ที่กำหนดเป้าหมายเป็น JavaScript มันปล่อยรหัส JavaScript ซึ่งเข้ากันได้กับโหมดการรวบรวมขั้นสูงของคอมไพเลอร์การเพิ่มประสิทธิภาพการปิด Google มันสืบทอดคุณสมบัติส่วนใหญ่ของ Clojure ซึ่งเป็นภาษาโปรแกรมแบบไดนามิกที่สนับสนุนการพัฒนาแบบโต้ตอบ
Lisp เป็นชื่อที่ใช้สำหรับกลุ่มภาษาที่มีลักษณะร่วมกัน (เช่น Clojure, Common Lisp, Scheme เป็นต้น) ดังนั้น 'Lisp' จึงไม่ได้ถูกเขียนขึ้นจริงๆ (นี่เหมือนกับการถามว่า 'เค้กมีส่วนผสมอะไรบ้าง' เค้กส่วนใหญ่จะมีธีมคล้าย ๆ กัน แต่จะมีส่วนผสมที่แตกต่างกัน)
ใช่ Lisp จัดลำดับความสำคัญของหลักการของ 'รหัสเป็นข้อมูล' สิ่งนี้สามารถเห็นได้จากที่มาของชื่อตัวเอง - LISt Processor ทุกอย่างในภาษา Lisp เป็นข้อมูล - แม้แต่การเรียกใช้ฟังก์ชันก็ยังเป็นรายการอาร์กิวเมนต์! Lisp เป็นรูปแบบของภาษาโปรแกรมที่ยอดเยี่ยมมีหลายรสชาติที่เหมาะกับทุกความต้องการ
ภาษา Lisp มักเรียกส่วนต่างๆของภาษาโปรแกรมที่ใช้งานได้และจำเป็น Clojure และ ClojureScript เป็นหลายตัวแปร - พวกเขาพึ่งพาการเขียนโปรแกรมเชิงฟังก์ชันเป็นอย่างมากและช่วยให้คุณสามารถแยกสาขาออกไปได้ตามความจำเป็นเมื่อจำเป็น สิ่งนี้ช่วยให้คุณได้สัมผัสกับสิ่งที่ดีที่สุดของทั้งสองโลก!
Google Closure Library เป็นไลบรารีข้ามเบราว์เซอร์ที่กว้างขวางและเป็นที่ยอมรับสำหรับ JavaScript ซึ่งมีเครื่องมือที่หลากหลายสำหรับ UI การจัดการ DOM การสื่อสารกับเซิร์ฟเวอร์การทดสอบและอื่น ๆ ซึ่งมีไว้สำหรับใช้กับ Google Closure Compiler
Google Closure Compiler รวบรวม JavaScript เป็น 'JavaScript ที่ดีกว่า' โดยการจัดการปรับแต่งและย่อขนาดเพื่อให้แน่ใจว่าดาวน์โหลดและทำงานได้เร็วขึ้น นอกจากนี้ยังเรียกใช้ชุดการตรวจสอบโค้ดของคุณเพื่อลดปัญหาขณะรันไทม์