昨天介紹完前端專案為什麼選擇使用React,
以及透過Reagent,
我們可以使用clojureScript語法當作interface去更有效率地寫出React components
怎麼拿~~麼厲害!
那麼,
既然我們學了Clojure / ClojureScript也快一個月了,
就來試著用Reagent建立前端專案吧:)
這個步驟很簡單,
如同我們在 Day21: 建立ClojureScript專案所述,
從Leiningen文件上知道,Leiningen是個自動化產生clojure檔案的套件,
因此查看指令,建立新專案的語法如下:
lein new [TEMPLATE] NAME # generate a new project skeleton
當時透過figwheel template建立一個cljs專案的語法是
lein new figwheel hello-world
因此舉一反三,建立reagent專案就也是依樣畫葫蘆啦!lein new xoxo xoxoxo
我們來將今天的專案名稱naming為ironproject
lein new reagent ironproject
登登!一個嶄新的美麗新世界 專案來到我們眼前!
用tree -a
指令看一下新專案內的內容
cd ~/Projects/ironproject
~/Projects/ironproject
~/Projects/ironproject
❯ tree -a
.
├── .gitignore
├── Procfile
├── README.md
├── env
│ ├── dev
│ │ ├── clj
│ │ │ ├── ironproject
│ │ │ │ ├── middleware.clj
│ │ │ │ └── repl.clj
│ │ │ └── user.clj
│ │ └── cljs
│ │ └── ironproject
│ │ └── dev.cljs
│ └── prod
│ ├── clj
│ │ └── ironproject
│ │ └── middleware.clj
│ └── cljs
│ └── ironproject
│ └── prod.cljs
├── package.json
├── project.clj
├── resources
│ └── public
│ └── css
│ └── site.css
├── shadow-cljs.edn
├── src
│ ├── clj
│ │ └── ironproject
│ │ ├── handler.clj
│ │ └── server.clj
│ ├── cljc
│ │ └── ironproject
│ │ └── util.cljc
│ └── cljs
│ └── ironproject
│ └── core.cljs
└── system.properties
21 directories, 18 files
從前幾篇文章知道一個簡單型的cljs專案裡面該有的樣子,
在此我們又看到了熟悉的src
file,
分成
-cljs
,裡有熟悉的core.cljs,是我們修改前端code的地方
-clj
,是放屬於後端的clojure file
-cljc
,放utiliy檔案之處,當Clojure或ClojureScript需要load namespace,首先會Clojure會去找.clj
檔、ClojureScript
會去找.cljs
檔,如果找不到,才會去找.cljc
檔。
cljc檔案裡面的namespace如下:
(ns ironproject.util)
由於這種一鍵安裝的活動最怕不知道別人幫我們打包了什麼東東進來電腦
因此我的習慣是會每個檔案瀏覽一下
尤其是觀察之前介紹過的project.clj,
可以讓我們了解一下dependency裡已經幫我們帶來哪些套件
:dependencies [[org.clojure/clojure "1.11.1"]
[ring-server "0.5.0"]
[reagent "1.1.1"]
[reagent-utils "0.3.4"]
[cljsjs/react "17.0.2-0"]
[cljsjs/react-dom "17.0.2-0"]
[ring "1.9.5"]
[ring/ring-defaults "0.3.3"]
[hiccup "1.0.5"]
[yogthos/config "1.2.0"]
[org.clojure/clojurescript "1.11.54"
:scope "provided"]
[metosin/reitit "0.5.18"]
[pez/clerk "1.0.0"]
[venantius/accountant "0.2.5"
:exclusions [org.clojure/tools.reader]]]
從套件相依性來看,
除了越來越熟悉的 Clojure / ClojureScript,和我們昨天介紹過的h
ic
up
(可以產生H
TML Markup
標記語言 by C
lojure語法)
[org.clojure/clojure "1.11.1"]
[org.clojure/clojurescript "1.11.54" :scope "provided"]
還有幾個react/reagent相關套件
[cljsjs/react "17.0.2-0"]
[cljsjs/react-dom "17.0.2-0"]
[reagent "1.1.1"]
[reagent-utils "0.3.4"]
以及reitit,Clojure(Script)的data-driven router
[metosin/reitit "0.5.18"]
還有幫我們溝通client端到handler的ring server
[ring "1.9.5"]
[ring/ring-defaults "0.3.3"]
Ring is a Clojure web applications library inspired by Python's WSGI and Ruby's Rack. By abstracting the details of HTTP into a simple, unified API, Ring allows web applications to be constructed of modular components that can be shared among a variety of applications, web servers, and web frameworks.
各種前後端功能的套件都有
看來clojure/clojurescript的生態系真的很蓬勃呢!
Clojure handler就類似Ruby on Rails的Controller,目的是用來controller/handle流程。
├── src
│ ├── clj
│ │ └── ironproject
│ │ ├── handler.clj
(ns ironproject.handler
(:require
[reitit.ring :as reitit-ring]
[ironproject.middleware :refer [middleware]]
[hiccup.page :refer [include-js include-css html5]]
[config.core :refer [env]]))
(def mount-target
[:div#app
[:h2 "Welcome to ironproject"]
[:p "please wait while Figwheel/shadow-cljs is waking up ..."]
[:p "(Check the js console for hints if nothing exciting happens.)"]])
(defn head []
[:head
[:meta {:charset "utf-8"}]
[:meta {:name "viewport"
:content "width=device-width, initial-scale=1"}]
(include-css (if (env :dev) "/css/site.css" "/css/site.min.css"))])
(defn loading-page []
(html5
(head)
[:body {:class "body-container"}
mount-target
(include-js "/js/app.js")]))
(defn index-handler
[_request]
{:status 200
:headers {"Content-Type" "text/html"}
:body (loading-page)})
(def app
(reitit-ring/ring-handler
(reitit-ring/router
[["/" {:get {:handler index-handler}}]
["/items"
["" {:get {:handler index-handler}}]
["/:item-id" {:get {:handler index-handler
:parameters {:path {:item-id int?}}}}]]
["/about" {:get {:handler index-handler}}]])
(reitit-ring/routes
(reitit-ring/create-resource-handler {:path "/" :root "/public"})
(reitit-ring/create-default-handler))
{:middleware middleware}))
稍微瀏覽handler code的用途,handler會幫我們拿資料、或者跟前端要HTML template來render資料到前端頁面,或者可能需要存取外部務。
打開 shadow-cljs.edn
稍微瀏覽一下,可以知道shadow-cljs這個套件整合了npm
可以方便地幫助我們編譯ClojureScript,將專案內的靜態資源做打包壓縮(ClojureScript -> JavaScript)
{:lein {:profile "+shadow-cljs"}
:builds {:app {:target :browser
:output-dir "resources/public/js"
:asset-path "/js"
:modules {:app {:init-fn ironproject.core/init!}}}}
:dev-http {3000 {:root "resources/public"
:handler ironproject.handler/app}}}
shadow-cljs除了有cljs REPL
,能幫我們快速Live Reload cljs + css、還有一些方便使用的targets像是 :browser
, :node-script
,:modules
,:npm-module
, :react-native
, :chrome-extension
這麼好用的東西,是不是要來學習安裝呀!
如果不是像本文一開頭直接建立Reagent App(一鍵安裝,什麼都幫忙下載好了~)
可以參考shaw-cljs手冊來進行安裝:
https://shadow-cljs.github.io/docs/UsersGuide.html#_installation
1.node.js (v6.0.0+, most recent version preferred)
2.Java SDK (至少要Version 8+)
1.安裝node
用node -v
來確認電腦裡是否已經裝好:
❯ node -v
v10.12.0
2.安裝JDK (Java SE Development Kit)
https://adoptopenjdk.net/ 安裝open SDK
❯ java -version
openjdk version "1.8.0_312"
OpenJDK Runtime Environment (Zulu 8.58.0.13-CA-macosx) (build 1.8.0_312-b07)
OpenJDK 64-Bit Server VM (Zulu 8.58.0.13-CA-macosx) (build 25.312-b07, mixed mode)
我的MacBook JDK版本大於8所以沒問題~
瀏覽的檔案結構,對有整體Reagent App有清晰的概念之後
打開專案架構裡面的README.md看提示
Figwheel會幫我們把cljs code的改動直接在瀏覽器做更新,達到超讚的Live code reloading的效果
Figwheel builds your ClojureScript code and hot loads it into the browser as you are coding
cd ~/Projects/ironproject
lein figwheel
Figwheel: Starting server at http://0.0.0.0:3449
Figwheel: Watching build - app
Figwheel: Cleaning build - app
Compiling build :app to "target/cljsbuild/public/js/app.js" from ["src/cljs" "src/cljc" "env/dev/cljs"]...
Successfully compiled build :app to "target/cljsbuild/public/js/app.js" in 16.944 seconds.
Figwheel: Starting CSS Watcher for paths ["resources/public/css"]
Figwheel: Starting nREPL server on port: 7002
Launching ClojureScript REPL for build: app
Figwheel Controls:
(stop-autobuild) ;; stops Figwheel autobuilder
(start-autobuild id ...) ;; starts autobuilder focused on optional ids
(switch-to-build id ...) ;; switches autobuilder to different build
(reset-autobuild) ;; stops, cleans, and starts autobuilder
(reload-config) ;; reloads build config and resets autobuild
(build-once id ...) ;; builds source one time
(clean-builds id ..) ;; deletes compiled cljs target files
(print-config id ...) ;; prints out build configurations
(fig-status) ;; displays current state of system
(figwheel.client/set-autoload false) ;; will turn autoloading off
(figwheel.client/set-repl-pprint false) ;; will turn pretty printing off
Switch REPL build focus:
:cljs/quit ;; allows you to switch REPL to another build
Docs: (doc function-name-here)
Exit: :cljs/quit
Results: Stored in vars *1, *2, *3, *e holds last exception object
Prompt will show when Figwheel connects to your application
[Rebel readline] Type :repl/help for online help info
有的時候figwheel server會有非預期的行為,所以我們還可以使用另一招:
lein do clean, run
改一下裡的檔案
│ └── cljs
│ └── ironproject
│ └── core.cljs
編輯一下home-page
component讓專案更加個人化,
(defn home-page []
(fn []
[:span.main
[:h1 "Welcome to Ting's"]
[:h1 "2022 IT Ironman project "]
[:div
[:h4 "I am Ironman"]
[:p
"I have a " [:strong "bold"]
[:span {:style {:color "red"}} " heart!"]]]
[:ul
[:li [:a {:href (path-for :items)} "My IronProject"]]
[:li [:a {:href "/broken/link"} "Articles link "]]]]))
再跑lein do clean, run
指令,這時候畫面就會出現了!
I am Ironman
I have a bold heart!
YA!
歡迎來到我們第一個reagent app :)
後記
Reagent /React App一啓動,畫面質感就是不一樣
是不是比我之前做的plain cljs app更好看呢?⬇️ XDDDD
拿~~麼厲害!
一個新網站跟專案就這樣誕生了,有一種新家的感覺,讓人充滿期待。這一篇文章真是內容滿滿,讓我想起過去我參加鐵人賽也曾經一口起研究好幾種不同框架比較差異,在這之中去了解設計思維與優劣,感覺可以學到很多!
cljs專案的左下角預設會有一個圓圓的logo嗎?感覺很可愛!希望剩下的一週時間能看見更多精彩的cljs介紹與分析,可以成為像是之前Ruby面試教科書一樣的聖典級作品!