配置 project.clj
添加相关依赖
文件:project.clj
;; ClojureScript 库
[org.clojure/clojurescript "1.10.439"]
配置前端编译器和 Figwheel
文件:project.clj
(defproject soul-talk "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [
;; Clojure 主运行时库
[org.clojure/clojure "1.9.0"]
;; Ring 库
[ring "1.7.1"]
;; 基于 Ring 的 Response 简化工具库
[metosin/ring-http-response "0.9.1"]
;; 常用中间件集合
[ring/ring-defaults "0.3.2"]
;; 路由库
[compojure "1.6.1"]
;; Selmer 模板库
[selmer "1.12.0"]
;; ring 前端库中间件
[ring-webjars "0.2.0"]
;; JQuery 依赖,Bootstrap 需要
[org.webjars/jquery "3.3.1-1"]
;; Bootstrap
[org.webjars/bootstrap "4.0.0-2"]
;; Popper
[org.webjars/popper.js "1.14.1"]
;; 字体库
[org.webjars/font-awesome "5.5.0"]
;; ClojureScript 库
[org.clojure/clojurescript "1.10.439"]]
:plugins [
;; 基于 Lein 的 Ring 插件
[lein-ring "0.12.4"]
;; Cljsbuild 编译器插件
[lein-cljsbuild "1.1.7" :excludes [[org.clojure/clojure]]]
;; figwheel 环境插件
[lein-figwheel "0.5.17-SNAPSHOT"]]
;; Ring 插件不通过 main 函数启动,只需要指定一个入口 Handler
:ring {:handler soul-talk.core/app}
;; 不使用插件的时候,程序仍然从 main 函数启动
;; 启用 ClojureScript 之后,要关闭预编译 AOT
:main ^:skip-aot soul-talk.core
;; 指定源文件和资源文件路径
:source-paths ["src"]
:resource-paths ["resources"]
;; 为 figwheel 指定 CSS 路径
:figwheel {:css-dirs ["resources/public/css"]}
;; 设置自动清理路径
:clean-targets ^{:protect false} [
:target-path
;; 下面的路径根据 cljsbuild 配置查找
[:cljsbuild :builds :dev :compiler :output-dir]
[:cljsbuild :builds :dev :compiler :output-to]]
;; 设置 cljsbuild 编译器参数
:cljsbuild {
:builds {
;; 开发环境
:dev {
;; 源代码目录
:source-paths ["src-cljs"]
;; 开启 figwheel
:figwheel true
:compiler {
;; 主命名空间
:main soul-talk.core
;; 依赖文件路径
:asset-path "js/out"
;; 最终输出的文件
:output-to "resources/public/js/main.js"
;; 临时文件输出路径
:output-dir "resources/public/js/out"
;; 不优化
:optimizations :none
;; 源代码
:source-map-timestamp true
;; 打印格式
:pretty-print true}}}}
:profiles {
:user {
:dependencies []
:plugins [[lein-ancient "0.6.15"]]}}
)
配置中间件
默认的 default
中间件会启用“防止跨域攻击”中间件,先关闭他
文件:src/soul_talk/core.clj
;; 组合中间件
(def app
(-> app-routes
(wrap-nocache)
(wrap-reload)
(wrap-webjars)
;; 常用中间件,关闭跨域攻击功能
(wrap-defaults (assoc-in site-defaults [:security :anti-forgery] false))))
静态资源
创建 base.html
静态页面父模板
注意:要引入 js/main.js
自定义脚本
文件:resources/base.html
使用 Clojure 建立个人网站
{% style "/assets/bootstrap/css/bootstrap.min.css" %}
{% style "/assets/font-awesome/css/all.css" %}
{% block page-css %}
{% endblock %}
{% block content %}
{% endblock %}
{% script "/assets/jquery/jquery.min.js" %}
{% script "/assets/font-awesome/js/all.js" %}
{% script "/assets/bootstrap/js/bootstrap.min.js" %}
{% block page-script %}
{% endblock %}
修改 index.html
注意:页面接收 Handler 传来的一个变量,用于显示登录状态,但不是真正的 Session
文件:resources/index.html
{% extends "base.html" %}
{% block content %}
Title of a longer featured blog post
From the Firehose
Sample blog post
- Vestibulum id ligula porta felis euismod semper.
- Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.
- Maecenas sed diam eget risus varius blandit sit amet non magna.
Cras mattis consectetur purus sit amet fermentum. Sed posuere consectetur est at lobortis.
Another blog post
Cum sociis natoque penatibus et magnis dis parturient montes,
New feature
Donec ullamcorper nulla non metus auctor fringilla. Nulla vitae elit libero, a pharetra augue.
{% endblock %}
创建 login.html
文件:resources/login.html
{% extends "base.html" %}
{% block page-title %}Soul Talk Login {% endblock %}
{% block page-css %}
{% endblock %}
{% block content %}
{% endblock %}
ClojureScript
新建 src-cljs
目录,在这个目录下新建 soul_talk/core.cljs
文件
(ns soul-talk.core)
(defn main []
(enable-console-print!)
(prn "Hello, Clojurescript"))
(main)
Clojure
添加登录相关处理器
修改 src/soul_talk/core.clj
,添加处理登录请求的处理器
;; 渲染 index.html 页面
(defn home-handle [request]
(parser/render-file "index.html" request))
;; Get 登录页面
(defn login-page [request]
(parser/render-file "login.html" {}))
;; Post 登录数据
(defn handle-login [email password request]
(if (and (= email "[email protected]") (= password "12345678"))
;; 如果登录成功,则在 Session 中添加信息
(home-handle (assoc-in request [:session :identity] email))
;; 如果失败,则返回登陆页面,并向页面中传送错误信息
(login-page (assoc request :error "用户名密码不对"))))
注意一:这里的 session 不是系统 session,而只是一个传给模板的变量,仅仅对渲染页面起作用,其他页面就看不到这个 session 了。如果要使用系统 session,必须在返回的键值对中加入
:session
键:
(-> (redirect "/") (assoc :session {:identity email}))
注意二:这里 Post 完毕后直接返回了
Index.html
的 HTML ,因此 URL 还是http://localhost:3000/login
,这是不正确的做法,应该重定向到/index.html
添加退出登录处理器
退出流程:清空 Session 信息,跳转到首页即可
(ns soul-talk.core
(:require ......
;; 引入重定向函数
[ring.util.response :refer [redirect]]))
;; 退出登录,清空 Session ,调整到首页
(defn handle-logout [request]
(do
(assoc request :session {})
(redirect "/")))
注意:这里清除的同样也只是一个模板变量,而不是系统 session 。如果要清楚系统 session ,需要将返回键值对中的
:session
键设置为空:
(-> (redirect "/") (assoc :session {}))
配置路由
将登录和退出处理器添加到路由规则中
文件:src/soul_talk/core.clj
(ns soul-talk.core
(:require ......
;; 引入相关函数
[compojure.core :refer [routes GET defroutes POST]]))
(def app-routes
(routes
(GET "/" request (home-handle request))
(GET "/about" [] (str "这是关于我的页面"))
;; 登录路由,Get 和 POST
(GET "/login" request (login-page request))
(POST "/login" [email password :as req] (handle-login email password req))
;; 退出登录路由==========
(GET "/logout" request (handle-logout request))
(route/not-found error-page)))
启动程序
启动服务
lein ring server-headless
编译 ClojureScript
lein figwheel
设置 Git 忽略文件
因为 ClojureScript 会下载很多依赖文件,同时产生很多编译输出文件,包括最终的输出文件 main.js ,都设置在了 /resources/public/js 中。这些都是动态的,不需要 Git 跟踪。
另外 FigWheel 的日志文件 figwheel_server.log 也不需要发布,因此都可以放到忽略文件中
figwheel_server.log
/resources/public/js