用 ClojureScript 语法运行 React
得益于最近 ClojureScript(简称 cljs) 社区的发展,运行和编译 cljs 已经越来越方便. 执行 ClojureScript 代码如果你只是想执行一下 cljs 代码熟悉语法,可以直接安装 Lumo. $ npm install -g lumo-cljs $ brew install lumo 安装完成之后可以从命令行直接启动: $ lumo Lumo 1.5.0 ClojureScript 1.9.542 Node.js v7.10.0 Docs: (doc function-name-here) (find-doc "part-of-name-here") Source: (source function-name-here) Exit: Control+D or :cljs/quit or exit cljs.user=> (println "Hello world!") Hello world! nil cljs.user=> 或者也可以用把代码贴到一个文件里,然后通过 lumo -i main.cljs 你可以把这个文件保存下来试着用 Lumo 运行,注意依赖的几个 React 模块: (ns demo.server-render) (def React (js/require "react")) (def ReactDOM (js/require "react-dom/server")) (def create-class (js/require "create-react-class")) (def comp-demo (create-class #js {:displayName "demo" :render (fn [] (.createElement React "div" nil))})) (println "This is only a demo.") (println (.renderToString ReactDOM (.createElement React comp-demo nil))) 运行 React用 cljs 来写网页会复杂一些,因为涉及到编译,也涉及到引用 npm 模块. npm install shadow-cljs cljs 编译器有 JVM 依赖,需要看下系统是否安装了 Java,如果没有也可以用 shadow-cljs 支持将 cljs 编译到 CommonJS 格式的代码,所以原理上很简单. 项目结构首先来看一下文件结构: =>> tree -I "node_modules|target" . ├── README.md ├── dist │ └── index.html ├── entry │ ├── main.css │ └── page.js ├── package.json ├── shadow-cljs.edn ├── src │ └── app │ └── main.cljs ├── webpack.config.js └── yarn.lock 4 directories,9 files 配置 shadow-cljs 编译器除了我们熟悉的 Webpack 开发常用的文件,还有这样一些文件:
{:source-paths ["src"] :dependencies [[mvc-works/hsl "0.1.2"]] :builds {:app {:target :npm-module :output-dir "target/"}}} 这是常用的编译到 npm 模块的配置
然后我们通过 npm 本地安装 shadow-cljs 就可以通过命令行工具启动编译器了: "scripts": { "watch": "webpack-dev-server --hot-only","compile-cljs": "shadow-cljs -b app --once","watch-cljs": "shadow-cljs -b app --dev" },"devDependencies": { "css-loader": "^0.28.4","shadow-cljs": "^0.9.5","style-loader": "^0.18.2","webpack": "^2.6.1","webpack-dev-server": "^2.4.5" }, 注意 shadow-cljs 的常用参数:
经过这样的配置,就可以通过命令启动了: yarn watch-cljs 编译结果会输出在 编写 React 代码我们用 cljs 写 React 用到的是 JavaScript Interop. 首先需要在命名空间当中定义 React 的依赖,主要是三个模块. (ns app.main (:require ["react" :as React] ["react-dom" :as ReactDOM] ["create-react-class" :as create-class])) 然后可以用 (def container (create-class #js {:displayName "container" :render (fn [] (.createElement React "div" nil (.createElement React "span" nil "Hello world!")))})) 然后是挂载组件. 通过 (def mount-point (.querySelector js/document "#app")) (defn render! [] (.render ReactDOM (.createElement React container nil) mount-point)) 刚才的代码用到了很多 React without JSX 的写法,可以参考官方文档: 然后我们定义一下初始化的代码, (defn main [] (render!) (println "App loaded.")) (defn reload [] (render!) (println "App reloaded.")) 这个文件编译之后是一个很难看到 js 文件,可以看到是支持 CommonJS 规范的. var $CLJS = require("./cljs_env"); require("./cljs.core.js"); require("./shadow.npm.react.js"); require("./shadow.npm.react_dom.js"); require("./shadow.npm.create_react_class.js"); var cljs=$CLJS.cljs; var shadow=$CLJS.shadow; var goog=$CLJS.goog; var app=$CLJS.app || ($CLJS.app = {}); goog.dependencies_.written["app.main.js"] = true; goog.provide('app.main'); goog.require('cljs.core'); goog.require('cljs.core'); goog.require('shadow.npm.react'); goog.require('shadow.npm.react_dom'); goog.require('shadow.npm.create_react_class'); app.main.mount_point = document.querySelector("#app"); app.main.container = (function (){var G__10849 = ({"displayName": "container","render": (function (){ return shadow.npm.react.createElement("div",null,shadow.npm.react.createElement("span","Hello world!")); })}); return shadow.npm.create_react_class(G__10849); })(); app.main.render_BANG_ = (function app$main$render_BANG_(){ return shadow.npm.react_dom.render(shadow.npm.react.createElement(app.main.container,({})),app.main.mount_point); }); app.main.main = (function app$main$main(){ app.main.render_BANG_(); return cljs.core.println.cljs$core$IFn$_invoke$arity$variadic(cljs.core.array_seq(["App loaded."],0)); }); app.main.reload = (function app$main$reload(){ app.main.render_BANG_(); return cljs.core.println.cljs$core$IFn$_invoke$arity$variadic(cljs.core.array_seq(["App reloaded."],0)); }); module.exports = app.main; //# sourceMappingURL=app.main.js.map Webpack 配置然后还需要一个入口文件来处理一下启动的功能,比如 CSS 和热替换. require('./main.css'); window.onload = require('../target/app.main').main; if (module.hot) { module.hot.accept('../target/app.main',function() { require('../target/app.main').reload(); }); } Webpack 的完整配置我就不重复了,整个链接我贴在这里. 启动最后加一个给 Webpack 启动的入口文件,也就注意一下加载顺序: <div id="app"></div> <script type="text/javascript" src="main.js"></script> 最后就可以启动整个应用了,前面写在了 npm scripts 里边: yarn # 安装依赖 yarn watch-cljs # 启动 cljs 的编译器 # 再开个终端 yarn watch # 启动 Webpack 的开发环境 再打开 http://localhost:8080/ 就可以看到 React 渲染的 "Hello world!" 了. 更多这篇文章介绍的只是很基础的在 cljs 里调用 React 的写法. 学习 ClojureScript 是个比较麻烦的过程,这个语言大家平时都不习惯, 如果你想了解 shadow-cljs 编译器更多的用法,可以查看 Wiki: 另外我最近(06-13)在 SegmentFault 有个简短分享,感兴趣的话可以来评论. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |