简介
Elm 是一门专注于Web前端的纯函数式言语。你能够没听说过它,但肯定听说过Redux,而Redux的中心reducer就是受到了Elm的启示。
跟着全部React社区往函数式方向生长,Elm作为前端函数式编程的前驱和风向标,毫无疑问是值得去进修和自创的。
假如你盘算最先函数式编程,与其浏览细碎的文章试图弄邃晓那些艰涩的Monad/Functor们,着手写点熟习的东西也许是更好的体式格局。接下我会以罕见的Counter/CounterList为例,一步步地带你相识怎样运用Elm构建运用。
因为内容较多,计离别四篇,大抵内容散布以下:
基础篇:Elm引见、基础。运用在线编辑器完成Counter
范例篇:Elm的范例体系
进阶篇:当地工程的搭建,在当地完成Counter List
完结篇:处置惩罚副作用,Elm与Redux对照
下载和预备
本文的内容都基于官网供应的在线编辑器,能够稍后再设置当地环境
你能够在官网下载装置包,作为前端开发者,从NPM下载也是很好的挑选,个人引荐后者
在装置胜利后,翻开命令行输入elm,会看到版本和协助信息。
有效的进修材料
官网供应了文档 和大批的examples ,但是个人一向不太喜好Elm的一点就是官方文档,无论是构造的合理性照样完全性都有所短缺,即使是像Syntax如许务求周全的处所,也有许多脱漏的知识点,在无形中增加了初学者的进修本钱。
本文接下来会只管解说涉及到的知识点,假如遇到困难,除了官网外,以下两个链接也是不错的补充:
Learn X in Y minutes:能够看成是对官网Syntax 的补充,不仅覆蓋了一些官网疏忽的点,许多诠释也越发细致
Elm for JS:针对Javascript开发者的罕见疑点解答,进修历程中有明白不了的处所无妨看看。
Hello world
根据套路,如今是Hello world时刻,官网有在线版,代码以下:
import Html exposing (text)
main =
text "Hello, World!"
异常简朴,却隐含了几个主要的知识点:
函数挪用
text "Hello, World"
是Elm中的函数挪用,类似于JS中的text("Hello world")
,它将一个字符串转换成Html文本。
在许多言语中,函数挪用都是括号,参数用逗号分开,比方fn(arg1, arg2)
,Elm的函数挪用符为空格,参数也运用空格分开,这点初看起来别扭,实际上并不难顺应。
挪用符和分开参数都是空格,怎样辨别呢?
答案是不须要辨别,Elm一切函数都是自动柯里化的,关于柯里函数
fn(arg1, arg2)
和fn(arg1)(arg2)
等价,运用空格作为挪用符,即(fn arg1) arg2
,注重这里的括号仅用来示意代码实行递次,省略后即为fn arg1 arg2
模块援用
第一行代码的import Html exposing (text)
是模块援用,和ES6中的import {text} from 'Html'
异常类似,但有一点须要注重,它同时导入了Html
和text
,而非只要text
,让我们考证一下,修正在线Hello world中的代码:
import Html exposing (text)
main =
Html.div [] [text "Hello, World!"]
我们能够在代码中运用Html.div
,证实Html
一样被导入了当前作用域。Html.div
也是个函数,吸收两个数组,前者为属性数组,后者则是子元素。这类建立元素的体式格局实在异常罕见:React.createElment和hyperscript都是这个套路。
没用过
React.createElement
?JSX帮你做了罢了
因为Html包含了险些一切浏览器标签的衬着函数,一个个写进exposing难免烦琐(设想下有若干原生标签)。让我们再做一点细小的事情,运用exposing(..)
来让代码越发简约。同时,我们尝试给div增加class属性
import Html exposing (..)
import Html.Attributes exposing (..)
main =
div [class "hello"]
[ span [] [text "Hello, World!"]
]
因为不够严谨,并不引荐在临盆代码中运用
exposing(..)
和衬着标签一样,在Elm中属性的建立也是由函数完成的,上例我们运用了Html.Attributes
模块的class
函数
Counter
有了Hello world的履历,让我们再往前一步,建立一个在线版的Counter,这里是React做的结果展现:https://jsfiddle.net/Kpaxqin/pu53jd89/2/
静态View和数据
上面我们运用了Html.div
来衬着div,同理,我们能够运用Html.button
来衬着按钮。轻微修正下适才的代码即可:
import Html exposing (..)
main =
div []
[button [] [text "-"]
,text (toString 1)
,button [] [text "+"]
]
如今div有三个子元素——两个button和一个数字,一个静态的Counter就这么构建出来了,异常简朴。
抽象是程序员的基础素养,把数字1
写死在视图里显然是很业余的表现。将衬着视图这个行动封装成函数越发合理:
import Html exposing (..)
view model =
div []
[ button [] [text "-"]
,text (toString model)
,button [] [text "+"]
]
initModel = 3
main = view initModel
在这里我们建立了一个函数,第一行是 函数名 + 参数
,和挪用一样都运用空格分开,等号背面的就是函数体,除非一个函数迥殊简朴,多半时刻我们倾向于将函数体换行写。
Update
有了静态界面,接下来应当让它“动”起来,响运用户操纵了。
起首,让我们定义两种操纵:
type Msg = Increment | Decrement
接下来,定义这两种操纵怎样转变数据:
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
update函数中的msg是我们方才定义的Msg范例的音讯,model则是当前数据的值,假如你相识Redux的话肯定会想:这不就是Reducer的(action, state)=> nextState
吗?确切云云,Reducer的观点恰是受到了Elm的启示,在终究章我们会继承讨论这个话题
另有一点你能够已注重到了,无论是前面的view照样这里的update函数,它们都没有return关键字!这是函数式言语异常主要的特性:一切都是expression,都须要有返回值。这强迫你去表达要什么
,而不是做什么
。
简朴的例子就是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'
}
对要什么
的剖析在函数式头脑中异常主要,通常会和递归联系起来,本文并不盘算深切,发起有兴致相识的朋侪能够进修Elm官网Examples中 functional stuff – recursion 小节下的例子
动态View
之前我们建立了一个静态的View,它没有任何事宜相干的代码,因而也不能够响运用户行动。接下来让我们补全这一部份
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
函数,onClick Decrement
能够明白为当click事宜发作时,它会输出一个Decrement
音讯。
但是向谁输出?输出的音讯怎样传递给update函数呢?让我们回忆一下一切的代码:
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
目前为止,界面仍然是静态的。我们有了数据,具有行动的视图,按行动转变数据的逻辑,却没有将它们粘合
成一个运用。
Elm为我们供应了如许的要领,在Html.App模块中
import Html.App as App
main = App.beginnerProgram {model = initModel, view = view, update = update}
注重这里的要领名叫beginnerProgram
,它的参数离别代表了:Model
, View
, Update
,这是,Elm架构的最简形状(不斟酌异步等副作用),也是任何相符Elm架构的组件都必不可少的三个部份,完全代码以下:
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}
小结
经由过程这个简朴的Counter相信你已对Elm有了开端的相识,假如回忆上面的代码你会发明实在函数式言语并非那末艰涩或深邃。
下一章中我们将会相识Elm的范例,并用范例优化Counter的代码。