Elm入门实践(一)——基本篇

简介

Elm 是一门专注于Web前端的纯函数式言语。你能够没听说过它,但肯定听说过Redux,而Redux的中心reducer就是受到了Elm的启示。

跟着全部React社区往函数式方向生长,Elm作为前端函数式编程的前驱和风向标,毫无疑问是值得去进修和自创的。

假如你盘算最先函数式编程,与其浏览细碎的文章试图弄邃晓那些艰涩的Monad/Functor们,着手写点熟习的东西也许是更好的体式格局。接下我会以罕见的Counter/CounterList为例,一步步地带你相识怎样运用Elm构建运用。

因为内容较多,计离别四篇,大抵内容散布以下:

  1. 基础篇:Elm引见、基础。运用在线编辑器完成Counter

  2. 范例篇:Elm的范例体系

  3. 进阶篇:当地工程的搭建,在当地完成Counter List

  4. 完结篇:处置惩罚副作用,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'异常类似,但有一点须要注重,它同时导入了Htmltext,而非只要text,让我们考证一下,修正在线Hello world中的代码:

import Html exposing (text)

main =
  Html.div [] [text "Hello, World!"]

我们能够在代码中运用Html.div,证实Html一样被导入了当前作用域。Html.div也是个函数,吸收两个数组,前者为属性数组,后者则是子元素。这类建立元素的体式格局实在异常罕见:React.createElmenthyperscript都是这个套路。

没用过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的代码。

    原文作者:kpaxqin
    原文地址: https://segmentfault.com/a/1190000005701562
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞