摘要:方法里返回的同樣是虛擬節點,不同的是節點必須符合要求的節點類型。內采用了模式匹配的形式,定義了對于所有可能的需要如何更新。值得注意的是,組件的是不可變的,而目前中只有一個字段,所以我們沒有這樣去展開它。
如果你是一個 React 愛好者,開始在各種站點聽到有人談論 Reason 這個新語言,也看見 Jordan(React 作者)說 ReasonReact 將是未來,但你卻是不知道從哪下手,那么這篇小教程就是為你準備的。
ps. 有條件的話還是盡量看 Reason 和 ReasonReact 的官方文檔吧
pps. Jared 寫的 A ReasonReact Tutorial 是 ReasonReact 最棒的入門指南。本文也是經由他允許,參考了很多其中的內容。能看的懂英語的都直接去他那里吧~
Reason 是什么?Reason 是一門基于 OCaml 的語言,它為 Ocaml 帶來了新的語法和工具鏈。它既可以通過 BuckleScript 被同編譯為 JavaScript,也支持直接編譯為原生的二進制匯編。Reason 提供了和 JavaScript 相似的語法,也可以使用 npm 來安裝依賴。長江后浪推前浪,Reason 丟掉了歷史包袱,比 JavaScript 多了可靠的靜態類型,也更快更簡潔!
為什么要學 Reason ?“為啥我要花時間學一門全新的語言呢?是 JavaScript 哪里不好還是你們要求太高?”
錯!Reason 不是一門全新的語言,事實上 80% 的語義都可以直接對應到現代的 JavaScript 上,反之也差不多。你只需要丟棄掉一丟丟的 JavaScript 邊角語法,再學一點點好東西,就可以獲得也許 ES2030 才有的特性。對于大部分人來說,學習 Reason 也不會比學習 JavaScript 和一個其他的類型系統(比如 Flow)來的慢。
不相信的話,先自己去看看 JS -> Reason 速查表,然后去 playground 體驗一下吧。
從哪開始?如果你體驗了一下,還是提不起興趣,你可以再出門右轉逛逛隔壁家 elm 和 ClojureScript 試試。但如果你覺得 ok,卻不知道從哪下手,那不妨和我一樣,從咱們熟悉的 React 開始。Jordan 重新發起了 ReasonReact 這個新項目,讓我們可以換一種更簡單優雅的方式寫 React。
ReasonReactReasonReact 提供了一些和 React 腳手架類似的工具,比如 reason-scripts。不過為了理解的深入一點,不妨從零開始搭起我們的第一個 ReasonReact 項目。新建一個項目目錄,名字隨意,讓我們開始吧~ 當然,你也可以直接 clone 已經準備好了的 simple-reason-react-demo 項目來參考。
首先,初始化 package.json
{ "name": "simple-reason-react-demo", "version": "0.1.0", "scripts": { "start": "bsb -make-world -w", "build": "webpack -w" }, "dependencies": { "react": "^16.2.0", "react-dom": "^16.2.0", "reason-react": "^0.3.0" }, "devDependencies": { "bs-platform": "^2.1.0", "webpack": "^3.10.0" } }
然后安裝一下依賴:
npm install --registry=https://registry.npm.taobao.org
項目里安裝了最新的 React 和 ReactDOM,以及額外的 ReasonReact。而編譯工具使用了前端業界標準 Webpack 和 張宏波 開發的 bs-platform。你可能暫時還弄不清 BuckleScript 在這里將要扮演怎樣的角色,不過沒關系,暫時你只要把他理解成 Reason -> JavaScript 的編譯器就好了,就像 Babel 把 ES2016 編譯成了 ES5 一樣。
然后,我們添加一個 BuckleScript 的配置文件 bsconfig.json
{ "name" : "simple-reason-react-demo", "reason" : {"react-jsx" : 2}, "refmt": 3, "bs-dependencies": ["reason-react"], "sources": "src" }
可以大概猜出來,項目用到了 reason 的 react-jsx 語法,依賴了 reason-react,源代碼存放在 src 目錄。時間有限,就先不展開研究了,詳細配置可以查看 bsconfig.json 結構。再創建下 src 目錄,我們的項目應該長成這樣了
. ├── bsconfig.json ├── src ├── node_modules └── package.json你好,ReasonReact
是不是很容易的就到這里了,讓我們正式開始寫 Reason 吧!在 src 里新建 Main.re 文件,寫下 Hello World
ReactDOMRe.renderToElementWithId((ReasonReact.stringToElement("Hello ReasonReact")), "root" );
幾乎和 React 代碼一樣不是么?然后我們運行編譯命令
# 相當于之前寫好的 "bsb -make-world -w" npm start
一切正常的話,可以看到編譯成功的提示,否則就要辛苦你按錯誤提示排查一下了,注意 bsb 的輸出對我們的很重要,一些錯誤提示和類型檢查的信息都要通過它來看。因為我們開啟了 -w 的 watch 模式,接下來還要用到,就先不用退出了。bsb 將代碼編譯到了 lib 目錄下
lib ├── bs └── js └── src └── Main.js
目前我們要關注一下的是 lib/js/src/Main.js,打開它我們可以看到編譯好的 JavaScript 代碼,非常漂亮是吧?這都是 BuckleScript 的功勞。為了讓代碼能在瀏覽器里運行,我們還需要用 Webpack 打包一下模塊化,這些你都應該非常熟悉了。
創建 public/index.html
你好
以及 webpack.config.js
const path = require("path"); module.exports = { entry: "./lib/js/src/Main.js", output: { path: path.join(__dirname, "public"), filename: "bundle.js", }, };
Webpacck 配置里入口是 bsb 編譯生成的 "./lib/js/src/Main.js"。再打開一個終端運行 npm run build,我們的準備工作就全部就緒了。我們只利用 webpack 做很簡單的打包,所以你基本可以忽略這個終端的輸出,還是把精力放在剛剛的 start 命令上。接下來直接在瀏覽器里打開 index.html 文件,就可以看到 “Hello ReasonReact” 了~
第一個組件讓我們開始第一個組件的開發,一個只能加加減減的步進器。新建一個組件文件:src/Stepper.re
let component = ReasonReact.statelessComponent("Stepper"); let make = (children) => ({ ...component, render: (self) =>});(ReasonReact.stringToElement("I"m a Stepper! "))
ReasonReact.statelessComponent 會返回一個默認的組件定義,里面包含了你熟悉的那些生命周期函數以及其他一些方法和屬性。這里我們定義了 make 方法,目前它只接受一個 children 參數,返回了一個組件。我們利用了類似 es6 的 ... 對象展開操作符 重寫了 component 中的 render 方法。神奇的是這段代碼居然完全符合 JavaScript 的語法...接下來,讓我們再修改一下 Main.re,讓他渲染這個 Stepper 組件
ReactDOMRe.renderToElementWithId(, "root");
刷新下瀏覽器,你應該可以看到剛寫好的組件就這么成功的 render 出來了。
你可能很好奇為什么這里沒有寫 require() 或 import。這是因為 Reason 的跨文件依賴是自動從你的代碼中推導出來的,當編譯器看到 Stepper 這個在 Main.re 中并沒有定義的量,它就會自動去找 Stepper.re 這個文件并引入該模塊。
熟悉 ReactJS 的同學都應該知道,jsx 并不是什么特殊的語法,只是會被編譯成普通的函數調用,比如
Hello React// to React.createElement( "div", null, "Hello React" );
而在 ReasonReact 中,jsx 會被翻譯成
/* to */ Stepper.make([||]) /* [|1,2,3|] 是 Reason 中數組的語法 */
意思是調用 Stepper 模塊的 make 函數,參數是一個空的數組。這就和我們之前寫好的 Stepper.re 中的 make 函數對應上了,這個空數組就對應于 make 的參數 children。再讓我們看眼我們的第一個組件
let component = ReasonReact.statelessComponent("Stepper"); let make = (children) => ({ ...component, render: (self) =>});(ReasonReact.stringToElement("I"m a Stepper! "))
不同于 ReactJS 中組件的 render,這里的 render 方法需要一個參數:self,暫且你可以把它比作 this,因為我們的 Stepper 是一個 stateless 組件,所以我們還用不到它。render 方法里返回的同樣是虛擬 DOM 節點,不同的是節點必須符合 ReasonReact 要求的節點類型。我們不能再直接寫
思來想去,我們的步進器還需要一個狀態,就是要顯示的數字。在 Reason 中,我們需要先定義 state 的類型(type)
type state = { value: int };
如果你寫過 flow 或者 typescript,一定不會覺得奇怪,這標識我們的 state 中包含 int 類型的 value 字段。然后,我們需要開始把原先的 statelessComponent 替換成 reducerComponent,原先的組件代碼也需要略微改動一下
type state = { value: int }; let component = ReasonReact.reducerComponent("Stepper"); let make = (children) => ({ ...component, initialState: () => { value: 0 }, reducer: ((), state) => ReasonReact.NoUpdate, render: (self) =>});(ReasonReact.stringToElement(string_of_int(self.state.value)))
聰明的你肯定一下就看懂了 initialState 和 ReactJS 的 getInitialState 簡直一模一樣。而在 render 這里也很類似,組件當前的狀態可以通過 self.state 獲取,還是為了類型匹配我們套了一層 string_of_int 將 int 類型的 value 轉換成 string。而新增的 reducer 函數可能就有點看不懂了。有意思的地方來啦~
在 ReactJS 中,我們依靠 setState 去手動的更新 state。ReasonReact 里則引入了 “reducer” 的概念,看上去很像 Redux 對吧?也許是 Jordan 自己也不是很喜歡 setState 這個非函數式的操作吧 …… ReasonReact 里更新一個組件狀態分為兩個步驟,首先發起一個 action,然后在 reducer 中處理它并更新狀態。此時此刻,我們還沒有添加 action,所以 reducer 還是無操作的,我們直接返回了一個 ReasonReact.NoUpdate 來標識我們并沒有觸發更新。讓我們繼續加上 action
type state = { value: int }; /* here */ type action = | Increase | Decrease; let component = ReasonReact.reducerComponent("Stepper"); let make = (children) => ({ ...component, initialState: () => { value: 0 }, reducer: (action, state) => { /* here */ switch action { | Decrease => ReasonReact.Update({value: state.value - 1}) | Increase => ReasonReact.Update({value: state.value + 1}) }; }, render: (self) =>/* and here */});(ReasonReact.stringToElement(string_of_int(self.state.value)))
首先,我們定義了 action 類型,它是一個 Variant(變體)。在 JavaScript 的世界里我們沒見過這種值,它用來表示這個變體(或者先叫它 "枚舉"?)可能的值。就像在 Redux 中推薦先聲明一堆 actionType 一樣,這個例子里我們定義了 +(Increase) 和 -(Decrease) 兩種 action。
然后我們就可以給 button 增加點擊的回調函數。我們使用了 self.reduce 這個函數(還記得 dispatch 么),它接收一個函數 (evt) => Increase 做轉換,可以把它看作將點擊的 event(在這里我們忽略掉了它因為用不到它...)換成一個 action,而這個 action 會被 self.reduce 用于做一個副作用操作來更新 state,更新 state 的操作就在 reducer 中。
reducer 內采用了模式匹配的形式,定義了對于所有可能的 action 需要如何更新 state。例如,對于 Increase 這個類型的 action,返回了 ReasonReact.Update({value: self.state.value + 1}) 去觸發更新。值得注意的是,組件的 state 是不可變的,而目前 state 中只有 value 一個字段,所以我們沒有 {...state, value: state.value + 1} 這樣去展開它。
如果你熟悉 Redux 的話,應該非常熟悉這一套范式了(雖然這其實來源于 Elm)。不同的是,我們直接擁有不可變的數據,不再需要過度的使用 JavaScript 的 String 來做 actionType,reducer 也寫的更加優雅簡單了,看著真是舒服~
繼續?這篇文章到這里也就暫時結束了,距離能做出一般的組件功能我們還差了很多東西。目前我也只是在一些個人的小項目中使用 Reason,文章內容很淺,主要是希望能啟發下厲害的你去嘗試 Reason 這個還算新鮮的語言,相信它會讓你眼前一亮的。
對了,既然都看到這里了,不如再去看看今年兩次 React Conf 上 chenglou 關于 Reason 的精彩演講吧~
Taming the Meta Language - React Conf 2017
What"s in a language? - Cheng Lou
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/90574.html
摘要:年的報告,總共有來自個不同的國家共多名開發者參與調查中國占總數的,共個開發者。今年的報告和前兩年不同,取消了部分。此比率較高的國家地區顯示為紅色,較低的國家地區顯示為藍色調查受訪者總數少于的國家地區將被省略。 前言 作為前端開發者,及時了解行業動態對我們的工作、學習方向起到至關重要的作用,畢竟知識太多,選擇對的學習方向才能既省力又能緊跟技術發展潮流。近期,StateOfJS發布了剛剛過...
摘要:年的報告,總共有來自個不同的國家共多名開發者參與調查中國占總數的,共個開發者。今年的報告和前兩年不同,取消了部分。此比率較高的國家地區顯示為紅色,較低的國家地區顯示為藍色調查受訪者總數少于的國家地區將被省略。 前言 作為前端開發者,及時了解行業動態對我們的工作、學習方向起到至關重要的作用,畢竟知識太多,選擇對的學習方向才能既省力又能緊跟技術發展潮流。近期,StateOfJS發布了剛剛過...
摘要:出處設計模式和最佳實踐作者米凱萊貝爾托利出版時間年月第版還算新使用來簡化獲取數據的代碼上面的代碼,我們將獲取數據的邏輯用高階組件抽離出來,下面我們再用來簡化上面的異步代碼瞬間清爽多了,順便利用提供的屬性,順便把邏輯也添加了分離列表和 出處:《react設計模式和最佳實踐》 作者:米凱萊·貝爾托利 出版時間:2018年8月第1版(還算新) 使用react-refetch來簡化api獲取數...
摘要:值得一瞥的相關技術趨勢從屬于筆者的前端入門與工程實踐,推薦閱讀我的前端之路工具化與工程化獲得更多關于年前端總結。的不少開發者都是的粉絲,他們的以及都是基于構建的。 2017值得一瞥的JavaScript相關技術趨勢從屬于筆者的Web 前端入門與工程實踐,推薦閱讀2016-我的前端之路:工具化與工程化獲得更多關于2016年前端總結。本文主要內容翻譯自,筆者對于每個條目進行了些許完善。本文...
摘要:的錯誤監控插件監聽了事件,因此可以自動捕獲未處理錯誤。自從年雙十一正式上線,累計處理了億錯誤事件,付費客戶有金山軟件百姓網等眾多品牌企業。 譯者按: 通過監聽unhandledrejection事件,可以捕獲未處理的Promise錯誤。 原文: Tracking unhandled rejected Promises 譯者: Fundebug 為了保證可讀性,本文采用意譯而非直...
閱讀 1259·2021-09-22 15:18
閱讀 2589·2021-09-22 15:17
閱讀 2218·2019-08-30 15:55
閱讀 1567·2019-08-30 15:54
閱讀 1032·2019-08-30 13:12
閱讀 619·2019-08-30 13:12
閱讀 1673·2019-08-29 11:33
閱讀 1433·2019-08-26 17:04