摘要:由于內(nèi)容較多,計劃分四篇,大致內(nèi)容分布如下基礎(chǔ)篇介紹基礎(chǔ)。接下來讓我們補(bǔ)全這一部分在第行我們引入了模塊中函數(shù),可以理解為當(dāng)事件發(fā)生時,它會輸出一個消息。我們有了數(shù)據(jù),具備行為的視圖,按行為改變數(shù)據(jù)的邏輯,卻沒有將它們粘合成一個應(yīng)用。
簡介
Elm 是一門專注于Web前端的純函數(shù)式語言。你可能沒聽說過它,但一定聽說過Redux,而Redux的核心reducer就是受到了Elm的啟發(fā)。
隨著整個React社區(qū)往函數(shù)式方向發(fā)展,Elm作為前端函數(shù)式編程的先驅(qū)和風(fēng)向標(biāo),毫無疑問是值得去學(xué)習(xí)和借鑒的。
如果你打算開始函數(shù)式編程,與其閱讀零碎的文章試圖弄明白那些晦澀的Monad/Functor們,動手寫點(diǎn)熟悉的東西也許是更好的方式。接下我會以常見的Counter/CounterList為例,一步步地帶你了解如何使用Elm構(gòu)建應(yīng)用。
由于內(nèi)容較多,計劃分四篇,大致內(nèi)容分布如下:
基礎(chǔ)篇:Elm介紹、基礎(chǔ)。使用在線編輯器實(shí)現(xiàn)Counter
類型篇:Elm的類型系統(tǒng)
進(jìn)階篇:本地工程的搭建,在本地實(shí)現(xiàn)Counter List
完結(jié)篇:處理副作用,Elm與Redux對比
下載和準(zhǔn)備本文的內(nèi)容都基于官網(wǎng)提供的在線編輯器,可以稍后再配置本地環(huán)境
你可以在官網(wǎng)下載安裝包,作為前端開發(fā)者,從NPM下載也是很好的選擇,個人推薦后者
在安裝成功后,打開命令行輸入elm,會看到版本和幫助信息。
有用的學(xué)習(xí)資料官網(wǎng)提供了文檔 和大量的examples ,然而個人一直不太喜歡Elm的一點(diǎn)就是官方文檔,無論是組織的合理性還是完整性都有所欠缺,即使是像Syntax這樣務(wù)求全面的地方,也有很多遺漏的知識點(diǎn),在無形中增加了初學(xué)者的學(xué)習(xí)成本。
本文接下來會盡量講解涉及到的知識點(diǎn),如果遇到困難,除了官網(wǎng)外,以下兩個鏈接也是不錯的補(bǔ)充:
Learn X in Y minutes:可以看成是對官網(wǎng)Syntax 的補(bǔ)充,不僅覆蓋了一些官網(wǎng)忽略的點(diǎn),很多解釋也更加詳細(xì)
Elm for JS:針對Javascript開發(fā)者的常見疑點(diǎn)解答,學(xué)習(xí)過程中有理解不了的地方不妨看看。
Hello world按照套路,現(xiàn)在是Hello world時間,官網(wǎng)有在線版,代碼如下:
import Html exposing (text) main = text "Hello, World!"
非常簡單,卻隱含了幾個重要的知識點(diǎn):
函數(shù)調(diào)用text "Hello, World"是Elm中的函數(shù)調(diào)用,類似于JS中的text("Hello world"),它將一個字符串轉(zhuǎn)換成Html文本。
在很多語言中,函數(shù)調(diào)用都是括號,參數(shù)用逗號分隔,比如fn(arg1, arg2),Elm的函數(shù)調(diào)用符為空格,參數(shù)也使用空格分隔,這點(diǎn)初看起來別扭,實(shí)際上并不難適應(yīng)。
模塊引用調(diào)用符和分隔參數(shù)都是空格,如何區(qū)分呢?
答案是不需要區(qū)分,Elm所有函數(shù)都是自動柯里化的,對于柯里函數(shù)fn(arg1, arg2)和fn(arg1)(arg2)等價,使用空格作為調(diào)用符,即(fn arg1) arg2,注意這里的括號僅用來表示代碼執(zhí)行順序,省略后即為fn arg1 arg2
第一行代碼的import Html exposing (text)是模塊引用,和ES6中的import {text} from "Html"非常相似,但有一點(diǎn)需要注意,它同時導(dǎo)入了Html和text,而非只有text,讓我們驗(yàn)證一下,修改在線Hello world中的代碼:
import Html exposing (text) main = Html.div [] [text "Hello, World!"]
我們可以在代碼中使用Html.div,證明Html同樣被導(dǎo)入了當(dāng)前作用域。Html.div也是個函數(shù),接收兩個數(shù)組,前者為屬性數(shù)組,后者則是子元素。這種創(chuàng)建元素的方式其實(shí)非常常見:React.createElment和hyperscript都是這個套路。
沒用過React.createElement?JSX幫你做了而已
由于Html包含了幾乎所有瀏覽器標(biāo)簽的渲染函數(shù),一個個寫進(jìn)exposing不免繁瑣(想象下有多少原生標(biāo)簽)。讓我們再做一點(diǎn)微小的工作,使用exposing(..)來讓代碼更加簡潔。同時,我們嘗試給div添加class屬性
import Html exposing (..) import Html.Attributes exposing (..) main = div [class "hello"] [ span [] [text "Hello, World!"] ]
由于不夠嚴(yán)謹(jǐn),并不推薦在生產(chǎn)代碼中使用exposing(..)
和渲染標(biāo)簽一樣,在Elm中屬性的創(chuàng)建也是由函數(shù)完成的,上例我們使用了Html.Attributes模塊的class函數(shù)
Counter有了Hello world的經(jīng)驗(yàn),讓我們再往前一步,創(chuàng)建一個在線版的Counter,這里是React做的效果展示:https://jsfiddle.net/Kpaxqin/pu53jd89/2/
靜態(tài)View和數(shù)據(jù)上面我們使用了Html.div來渲染div,同理,我們可以使用Html.button來渲染按鈕。稍微修改下剛才的代碼即可:
import Html exposing (..) main = div [] [button [] [text "-"] ,text (toString 1) ,button [] [text "+"] ]
現(xiàn)在div有三個子元素——兩個button和一個數(shù)字,一個靜態(tài)的Counter就這么構(gòu)建出來了,非常簡單。
抽象是程序員的基本素養(yǎng),把數(shù)字1寫死在視圖里顯然是很業(yè)余的表現(xiàn)。將渲染視圖這個行為封裝成函數(shù)更加合理:
import Html exposing (..) view model = div [] [ button [] [text "-"] ,text (toString model) ,button [] [text "+"] ] initModel = 3 main = view initModel
在這里我們創(chuàng)建了一個函數(shù),第一行是 函數(shù)名 + 參數(shù),和調(diào)用一樣都使用空格分隔,等號后面的就是函數(shù)體,除非一個函數(shù)特別簡單,多數(shù)時候我們傾向于將函數(shù)體換行寫。
Update有了靜態(tài)界面,接下來應(yīng)該讓它“動”起來,響應(yīng)用戶操作了。
首先,讓我們定義兩種操作:
type Msg = Increment | Decrement
接下來,定義這兩種操作如何改變數(shù)據(jù):
update msg model = case msg of Increment -> model + 1 Decrement -> model - 1
update函數(shù)中的msg是我們剛剛定義的Msg類型的消息,model則是當(dāng)前數(shù)據(jù)的值,如果你了解Redux的話一定會想:這不就是Reducer的(action, state)=> nextState嗎?確實(shí)如此,Reducer的概念正是受到了Elm的啟發(fā),在最終章我們會繼續(xù)探討這個話題
還有一點(diǎn)你可能已經(jīng)注意到了,無論是前面的view還是這里的update函數(shù),它們都沒有return關(guān)鍵字!這是函數(shù)式語言非常重要的特點(diǎn):一切都是expression,都需要有返回值。這強(qiáng)制你去表達(dá)要什么,而不是做什么。
簡單的例子就是case語句和if語句:
/* case statement */ //elm case arg of value1 -> result1 value2 -> result2 //javascript switch (expression) { case value1: /*do sth*/ return result1; break case value2: /*do sth*/ return result2; break /* if statement */ //elm //else is required if 3 > 2 then "cat" else "dog" //javascript if (3 > 2) { return "cat" } else { //else statement is optional return "dog" }
對要什么的分解在函數(shù)式思維中非常重要,通常會和遞歸聯(lián)系起來,本文并不打算深入,建議有興趣了解的朋友可以學(xué)習(xí)Elm官網(wǎng)Examples中 functional stuff - recursion 小節(jié)下的例子
動態(tài)View之前我們創(chuàng)建了一個靜態(tài)的View,它沒有任何事件相關(guān)的代碼,因此也不可能響應(yīng)用戶行為。接下來讓我們補(bǔ)全這一部分
import Html exposing (..) import Html.Events exposing (onClick) view model = div [] [ button [onClick Decrement] [text "-"] ,text (toString model) ,button [onClick Increment] [text "+"] ]
在第2行我們引入了Html.Events模塊中onClick函數(shù),onClick Decrement可以理解為當(dāng)click事件發(fā)生時,它會輸出一個Decrement消息。
可是向誰輸出?輸出的消息如何傳遞給update函數(shù)呢?讓我們回顧一下所有的代碼:
import Html exposing (..) import Html.Events exposing (onClick) type Msg = Increment | Decrement update msg model = case msg of Increment -> model + 1 Decrement -> model - 1 view model = div [] [ button [onClick Decrement] [text "-"] ,text (toString model) ,button [onClick Increment] [text "+"] ] initModel = 3 main = view initModel
目前為止,界面仍然是靜態(tài)的。我們有了數(shù)據(jù),具備行為的視圖,按行為改變數(shù)據(jù)的邏輯,卻沒有將它們粘合成一個應(yīng)用。
Elm為我們提供了這樣的方法,在Html.App模塊中
import Html.App as App main = App.beginnerProgram {model = initModel, view = view, update = update}
注意這里的方法名叫beginnerProgram,它的參數(shù)分別代表了:Model, View, Update,這是,Elm架構(gòu)的最簡形態(tài)(不考慮異步等副作用),也是任何符合Elm架構(gòu)的組件都必不可少的三個部分,完整代碼如下:
import Html exposing (..) import Html.Events exposing (onClick) import Html.App as App type Msg = Increment | Decrement update msg model = case msg of Increment -> model + 1 Decrement -> model - 1 view model = div [] [ button [onClick Decrement] [text "-"] , text (toString model) , button [onClick Increment] [text "+"] ] initModel = 3 main = App.beginnerProgram {model = initModel, view = view, update = update}小結(jié)
通過這個簡單的Counter相信你已經(jīng)對Elm有了初步的了解,如果回顧上面的代碼你會發(fā)現(xiàn)其實(shí)函數(shù)式語言并不是那么晦澀或高深。
下一章中我們將會了解Elm的類型,并用類型優(yōu)化Counter的代碼。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/79639.html
摘要:如果不聲明類型呢如果注釋掉類型注解重新編譯,還是會報錯,只是錯誤信息變了,這次是第行即使沒有顯式的類型注解,的類型推導(dǎo)系統(tǒng)也會發(fā)揮作用,此處通過類型推導(dǎo)認(rèn)為函數(shù)的參數(shù)應(yīng)該是字符串,但是傳入了數(shù)字,因此報錯。 記得Facebook曾經(jīng)在一次社區(qū)活動上說過,隨著他們越來越多地使用Javascript,很快就面臨了曾經(jīng)在PHP上遇到的問題:這東西到底是啥? 動態(tài)語言就像把雙刃劍,你可以愛死它...
摘要:函數(shù)式編程,一看這個詞,簡直就是學(xué)院派的典范。所以這期周刊,我們就重點(diǎn)引入的函數(shù)式編程,淺入淺出,一窺函數(shù)式編程的思想,可能讓你對編程語言的理解更加融會貫通一些。但從根本上來說,函數(shù)式編程就是關(guān)于如使用通用的可復(fù)用函數(shù)進(jìn)行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數(shù)式編程(Functional Programming),一...
摘要:我的目的是確保所有引用的使用都是絕對安全的,編譯器會自動進(jìn)行檢查。它導(dǎo)致了數(shù)不清的錯誤漏洞和系統(tǒng)崩潰,可能在之后年中造成了十億美元的損失。這個函數(shù)將使用一個表示我們希望進(jìn)行轉(zhuǎn)換的函數(shù)參數(shù),并返回一個包含轉(zhuǎn)換結(jié)果的新參數(shù)。 翻譯原文出處:Building a Maybe in JavaScript 鄙人翻譯略差且略有出入,別見笑。 很多時候我們會碰到:Uncaught TypeError...
摘要:值得一瞥的相關(guān)技術(shù)趨勢從屬于筆者的前端入門與工程實(shí)踐,推薦閱讀我的前端之路工具化與工程化獲得更多關(guān)于年前端總結(jié)。的不少開發(fā)者都是的粉絲,他們的以及都是基于構(gòu)建的。 2017值得一瞥的JavaScript相關(guān)技術(shù)趨勢從屬于筆者的Web 前端入門與工程實(shí)踐,推薦閱讀2016-我的前端之路:工具化與工程化獲得更多關(guān)于2016年前端總結(jié)。本文主要內(nèi)容翻譯自,筆者對于每個條目進(jìn)行了些許完善。本文...
摘要:不過今天我希望能夠更進(jìn)一步,不僅僅再抱怨現(xiàn)狀,而是從我個人的角度來給出一個逐步深入學(xué)習(xí)生態(tài)圈的方案。最后,我還是想提到下對于的好的學(xué)習(xí)方法就是回顧參照各種各樣的代碼庫,學(xué)習(xí)人家的用法與實(shí)踐。 本文翻譯自A-Study-Plan-To-Cure-JavaScript-Fatigue。筆者看到里面的幾張配圖著實(shí)漂亮,順手翻譯了一波。本文從屬于筆者的Web Frontend Introduc...
閱讀 1630·2023-04-25 18:19
閱讀 2078·2021-10-26 09:48
閱讀 1079·2021-10-09 09:44
閱讀 1730·2021-09-09 11:35
閱讀 3027·2019-08-30 15:54
閱讀 2021·2019-08-30 11:26
閱讀 2285·2019-08-29 17:06
閱讀 884·2019-08-29 16:38