F# 및 WebAssembly

F# 및 WebAssembly


내가 F#과 웹 개발에 대해 이야기할 때, 나는 Fable을 이야기하는 경향이 있다. 이것은 F# -> JS 컴파일러이다. (Fable 4+에서 JS뿐만 아니라 정식으로 겨냥될 것이다.) 어떤 의미에서 보면, 당신은 기본적으로 F#을 Typescript나 Flow 또는 다른 JS 컴파일러로 교체하는 것이다.
오늘 우리는 BoleroFun.Blazor을 토론할 것이다. 이것은 마이크로소프트 전단 프레임 Blazor의 일부 F#에 대한 추상적인 것이다. 객관적으로 말하자면 blazor는 Angular, Vue, React, Svelte, Aurelia과 유사한 대체품의 직접적인 경쟁 상대이다.
나는 많은 사람들이 인내심, 절망적으로 자바스크립트의 소멸을 기다리고 있다는 것을 알고 있지만, 나는 이 날이 영원히 오지 않을 것이라고 생각한다. 나는 언어가 창고의 많은 부분을 채울 수 있다는 것을 알고 있다. (만약 전부가 아니었다면) 얼마나 유용한지 알고 있다. (우리는 이미 JS로 해냈다. 수천 수만의 사람들이 그것을 좋아한다) 그래서 한번 보자!

볼레로


볼레로부터가 쉬워요.
dotnet new -i Bolero.Templates
dotnet new bolero-app -o MyApp
cd MyApp && dotnet run -p src/MyApp.Server

Most of the features I will talk about here are well described in bolero's website so don't forget to check them out there as well


관리형 모델


Bolero (blazor처럼) 는 서버 쪽과 클라이언트 프로그램에 사용할 수 있습니다. 기본적으로 템플릿은 백엔드와 전방 프로젝트를 동시에 제공합니다. HTML 템플릿을 다시 불러올 수 있는 기능이 있지만, 서버 프로젝트를 사용해서 전방 프로그램을 실행할 때만 작동합니다.

태그 및 DSL


Bolero가 HTML을 작성하는 두 가지 주요 방법 중 하나는 HTML 템플릿을 사용하는 것이고, 다른 하나는 HTML DSL을 사용하는 것이다. 이 두 가지 방법은 결국 효과를 낼 수 있다.

HTML 템플릿


Bolero는 사용자 정의 HTML Type Provider을 사용하여 HTML 템플릿을 안전하게 작성하고 핫 리로드 기능을 제공합니다.
type Hello = Template<"""<div id="hello">Hello, world!</div>""">
// or using a filepath
type Hello = Template<"templates/hello.html">
이렇게 하면 F# 값을 삽입할 수 있는 구멍이 있을 수 있는 HTML 템플릿에 대한 정보가 들어 있는 유형이 제공됩니다.
간단한 카운터 만들어 달래요.
<section class="${Classes}">
  <p>Count: <span>${Count}</span></p>
  <button onclick="${Increment}">Increment</button>
  <button onclick="${Decrement}">Decrement</button>
  <button onclick="${Reset}">Reset</button>
</section>
HTML을 템플릿으로 사용하면 F# 코드의 빈틈을 처리할 수 있습니다
type Counter = Template<"templates/counter.html">

let getCounter initial =
    let mutable count = initial
    let getCounterCls count = if count > 10 "warning" else "normal"
    // let's fill each of the wholes we made on the template
    Counter()
        .Classes($"counter {getCounterCls count}")
        .Count(count)
        .Increment(fun _ -> count <- count + 1)
        .Decrement(fun _ -> count <- count - 1)
        .Reset(fun _ -> count <- initial)
        // once we're done call Elt() to get the instance of the template
        .Elt()

// use this somewhere else
let startsAt100 = getCounter 100
let startsAt0 = getCounter 0
마지막 .Elt()은 템플릿의 실례를 가져옵니다.
너도 끼워 넣은 템플릿, 입력과 라디오를 얻을 수 있다. 예를 들어 mutable 키워드에 놀라지 마라. 이것은 단지 간단한 예일 뿐이다. 정상적인 상황에서 너는 Elmish을 사용할 수 있다
만약 순수한 HTML을 좋아하지 않는다면, F#DSL에 대해 계속 토론합시다

HTML DSL


HTMLDSL은 상당히 표준적인 DSL로 하나의 함수로 구성되어 있다. 이 함수는 두 개의 목록을 매개 변수로 하고 첫 번째는 속성에 사용하며 두 번째는 하위 요소에 사용한다. 우리의 반례는 다음과 같다.
let getCounter initial =
    let mutable count = initial
    let getCounterCls count = if count > 10 "warning" else "normal"

    section [ attr.``class`` $"counter {getCounterCls count}" ] [
        p [] [ text "Count: "; span [] [ text $"%i{count}" ] ]
        button [ on.click(fun _ -> count <- count + 1) ] [ text "Increment" ]
        button [ on.click(fun _ -> count <- count - 1) ] [ text "Decrement" ]
        button [ on.click(fun _ -> count <- initial) ] [ text "Reset" ]
    ]

// use this somewhere else
let startsAt100 = getCounter 100
let startsAt0 = getCounter 0
이 방법의 주요 장점은 F# 언어의 강력한 기능을 충분히 활용할 수 있다는 것이다.함수를 호출할 수 있습니다.ifs, 패턴 일치, 상자를 열면 사용할 수 있는 안전성, 그리고 F#로 할 수 있는 모든 일을 사용할 수 있습니다.
이 코드는 F# 코드이므로 변경할 때 컴파일해야 합니다.서버를 언제든지 다시 시작해서 변경해야 할 것이다. 이것은 느리고 일부 사람들을 낙담하게 할 수도 있다. 이것은 충분히 이해할 수 있다.

느릅나무


F# 애호가들에게 유행하는 구조는 elmish 구조(MVU라고도 부른다)를 사용하는 능력이다. bolero는 구성 요소 형식으로 elmish를 제공한다. 이것은 매우 편리하다. 왜냐하면 elmish는 더 복잡한 응용 프로그램에서 하나의 주elmish 순환이 아니라 확장할 수 없기 때문이다.너는 너의 사이트의 모든 부분을 서로 다른elmish 순환으로 할 수 있으며, 필요하지 않을 때 일부 논리, 심지어 페이지를 결합시킬 수 있다.
우리 의 반례 를 다시 한 번 복습합시다
type State = { count: int }
type Msg =
    | Increment
    | Decrement
    | Reset

let init initial = { count = initial }

let update initial msg state =
    match msg with
    | Increment -> { state with count = state.count + 1}
    | decrement -> { state with count = state.count - 1}
    | Increment -> { state with count = initial }
이것은 elmish 모델의 핵심 부분입니다. HTML과 DSL 방법과 어떻게 함께 작업하는지 보실 수 있습니다
// the HTML template stays the same
let view state dispatch =
    let getCounterCls count = if count > 10 "warning" else "normal"
    // let's fill each of the wholes we made on the template
    Counter()
        .Classes($"counter {getCounterCls state.count}")
        .Count(count)
        .Increment(fun _ -> dispatch Increment)
        .Decrement(fun _ -> dispatch Decrement)
        .Reset(fun _ -> dispatch Reset)
        // once we're done call Elt() to get the instance of the template
        .Elt()
let view state dispatch =
    let getCounterCls count = if count > 10 "warning" else "normal"

    section [ attr.``class`` $"counter {getCounterCls count}" ] [
        p [] [ text "Count: "; span [] [ text $"%i{count}" ] ]
        button [ on.click(fun _ -> dispatch Increment) ] [ text "Increment" ]
        button [ on.click(fun _ -> dispatch Decrement) ] [ text "Decrement" ]
        button [ on.click(fun _ -> dispatch Reset) ] [ text "Reset" ]
    ]
이제 이 모든 것을 우리 구성 요소에 결합시키자
type MyCounter() =
    inherit ProgramComponent<State, Msg>()

    [<Parameter>]
    // Grab the value from outside of the component
    member val InitialValue: int option = None with get, set

    override this.Program =
        // ensure there's a default value for our elmish loop
        let initial = this.InitialValue |> Option.defaultValue 0

        // with partial application give it the initial value
        let update = update initial

        // Run the elmish program
        Program.mkSimple (fun _ -> init initial) update view
보시다시피 HTML이든 DSL이든 elmish 구성 요소의 작업 원리는 똑같습니다.다른 종류의elmish 모듈도 있습니다. 모듈의 성능을 미세하게 조정할 수 있습니다. 모듈은 View Components이라고 불리지만, 홈 작업으로 남겨 두겠습니다.😜
이 구성 요소를 사용하려면 다음과 같은 방식으로 호출할 수 있습니다
let startsAt0 = comp<MyCounter> [] []
let startsAt100 = comp<MyCounter> [ "InitialValue" => 100 ] []
우리는 다른 view 함수에서 이 함수를 사용할 수 있거나, 만약 내가 C#의blazor 파일에서 틀리지 않았다면, 내 말을 인용하지 마라. 왜냐하면 나는 아직 시도해 본 적이 없기 때문이다.
요컨대 이것은 볼레로의 요점이 아니라 다른 기능도 있다. 예를 들어 루트, 원격 처리, 주입 상호작용, JS 서비스에 의존하여 사용할 수 있지만 그 중 많은 것들이 자신의 글이 필요하다고 생각한다. 만약에 이런 것에 관심이 있다면 나에게 알려주세요!

재미있다.브라조


Fun Blazor는 bolero 위에 세워진 라이브러리로 bolero와 상호작용을 잘 할 수 있지만 DSL과 주 관리 부서에서 더 많은 옵션을 제공합니다.

Note that Fun.Blazor is still a young library but I find it promising and more ergonomic to use to the point I actually could favor it over other solutions.


놀자.Blazor는 제가 이 글을 위해 만든 아주 간단한 템플릿을 사용할 수 있어요.
https://github.com/AngelMunoz/Fun.Blazor.Sample
또는 공식 템플릿 사용
dotnet new --install Fun.Blazor.Templates::*
dotnet new fun-blazor-min-wasm -o MyApp
cd MyApp && dotnet run

I will continue here with the simplified template I made which differs a little bit from the official but the core concepts remain the same.


재미있다.Blazor는 세 가지 HTML 코드를 작성하는 대체 방법을 제공했다. 첫 번째는 하나의 계산 표현식(CE)을 사용하는 것이고, 두 번째는 Feliz 스타일의 DSL을 사용하는 것이며, 세 번째는 HTML 템플릿을 작성하는 것이다.
우리 는 다시 반례 를 수정합시다
let counter initial =
  adaptiview () {
    let! counter, setCounter = cval(initial).WithSetter()
    p () { childContent $"Count: {counter}" }

    button () {
      onclick (fun _ -> setCounter (counter + 1))
      childContent "Increment"
    }

    button () {
      onclick (fun _ -> setCounter (counter - 1))
      childContent "Decrement"
    }

    button () {
      onclick (fun _ -> setCounter (initial))
      childContent "Reset"
    }
  }
만약 JSX/Swift/Kotlin에 익숙하다면, 이것은 보기를 처리하는 가장 좋은 방법일 수도 있고, 가장 재미있는 방법일 수도 있습니다.Blazor가 HTML 내용을 작성하는 방식은 매우 재미있다.Blazor는 보일러가 제공하는 일반 DSL보다 조금 적은 상세한 방식으로 보기를 모델링할 수 있도록 사용자 정의 조작을 제공합니다. 주의해야 할 것은 bolero is working on a variation like this은 시간이 걸릴 수 있습니다.
let counter initial =
  adaptiview () {
    let! counter, setCounter = cval(initial).WithSetter()
    html.p [
        attr.childContent$"Count: {counter}"
    ]

    html.button [
        evts.click (fun _ -> setCounter (counter + 1))
        attr.childContent $"Increment"
    ]

    html.button [
        evts.click (fun _ -> setCounter (counter - 1))
        attr.childContent $"Decrement"
    ]

    html.button [
        evts.click (fun _ -> setCounter (initial))
        attr.childContent $"Reset"
    ]
  }
만약 당신이 Feliz이나 Avalonia.FuncUI을 사용한 적이 있다면, 이 DSL은 당신을 집에 있는 것처럼 느끼게 할 것입니다. 이것은 원래의 DSL보다 더욱 간결하고 즐거움에 있어서 기본적으로 같은 장점을 줄 것입니다.Blazor의 성능은 약간 떨어지지만, 그것은 실행 가능한 선택이다
let counter initial =
  adaptiview () {
    let! counter, setCounter = cval(initial).WithSetter()
    Template.html
      $"""
      <section>
        <p>Count: <span>{counter}</span></p>
        <button onclick="{fun _ -> setCounter (counter + 1)}">Increment</button>
        <button onclick="{fun _ -> setCounter (counter - 1)}">Decrement</button>
        <button onclick="{fun _ -> setCounter (initial)}">Reset</button>
      </section>
      """
  }
너는 이 점을 보고 생각할 수도 있다.

Why on earth would I use this version? no syntax coloring, no nothing ewww!

  • 만약에 VScode를 사용한다면 Highlight HTML/SQL templates in F# 확장을 사용할 수 있습니다. HTML 스마트 감지, 문법 착색 및 F# 지원을 자동으로 받을 수 있습니다!
  • 만약에 Visual Studio를 사용한다면 Html for F#을 사용할 수 있습니다. 이것은 HTML 스마트감을 주지 않지만 최소한 HTML 문법 착색을 제공합니다. 이것은 매우 인기 있는
  • 입니다.
    그 밖에 HTML 템플릿의 장점 중 하나는 사용률이 끊임없이 상승하는 웹 구성 요소/사용자 정의 요소를 사용할 수 있다는 것이다. 유감스럽게도 이러한 구성 요소/사용자 정의 요소는 볼레로의 템플릿처럼 다시 불러올 수 없지만, 자신만의 장점이 있다

    국가 관리


    재밌지만또한 Elmish를 사용할 수 있습니다. 또한 웹 Assembly는 F# 코드를 사용하기 때문에 다양한 방식으로 보기의 상태를 처리할 수 있습니다. 이것은 대량의 라이브러리에 접근하여 이 라이브러리를 이용할 수 있음을 의미합니다.Fable는 어느 정도 자바스크립트 자체의 제한을 받기 때문에 F# 언어에서 얻을 수 있는 모든 개선은 자바스크립트가 이해할 수 있는 내용에만 한정됩니다.

    사용자 적응 뷰


    하지만 사실은 그렇지 않다!재미있다.Blazor는 FSharp.Data.Adaptive 라이브러리를 이용했다. 이 라이브러리의 작업 방식은 excel 전자 표의 단원격과 비슷하지 않고 모든 값이 고정되어 있다. 다른 계산이 없으면 필요에 따라 성능 업데이트를 할 수 있다. adaptiview() {}의 도움으로 똑같은 효과적인 업데이트를 이용하여 F#의 성능 보기를 허용할 수 있다. 본질적으로 adaptiviews은 갈고리와 유사한 추상적이다.
    adaptiview() {
        let! value, setValue = cval("my initial value").WithSetter()
        // do whatever else you want with the value and set value
    }
    

    상점.


    만약 Sutil/Svelte를 좋아하고 상점을 이용하여 상태 관리를 하는 것을 좋아한다면 Fun을 사용하세요.Blazor 구성 요소가 최적의 선택입니다.
    let counterComponent (initial: int) =
      html.inject (fun (hook: IComponentHook) ->
        let counter = hook.UseStore initial
        // this value will automatically update any time counter changes
        let adaptiveValue = hook.UseAVal counter
    
        adaptiview () {
          // we can use the value directly if we bind it on the
          // adaptiview CE
          let! computed = adaptiveValue
          // use the computed value
          p () { childContent $"Count: %i{computed}" }
    
          button () {
            // publish updates to the store
            onclick (fun _ -> counter.Publish(computed + 1))
            childContent "Increment"
          }
    
          button () {
            onclick (fun _ -> counter.Publish(computed - 1))
            childContent "Decrement"
          }
    
          button () {
            onclick (fun _ -> counter.Publish(initial))
            childContent "Reset"
          }
        })
    
    이것은 아마도 강력한 방법일 것이다. 왜냐하면 당신은 일부 함수를 사용하여 저장을 얻고 업데이트를 전달할 수 있기 때문이다. 적응 값은 시종 최신식일 것이다. 당신은 심지어 같은 저장소에서 여러 개의 적응 값을 생성할 수 있다.이것은 서브어셈블리나 다른 보기에서도 공유 저장소의 장점을 누리고 자신의 적응치를 생성할 수 있다는 것을 의미한다.

    구성 요소


    우리는 방금 저장과 자체 적응치를 갖춘 구성 요소를 보았지만, 그것은 즐거움의 장점을 가져왔다.Blazor 구성 요소는 여기에 그치지 않으며 Blazor 구성 요소에 원활하게 액세스할 수 있습니다.
    example의 경우 첫 번째 렌더링을 클릭한 후 비동기식으로 FSharp.Control.Reactive을 사용하여 관찰 대상과 통합할 수 있습니다.
    html.inject(fun (hook: IHookComponent, themeService: IThemeService) ->
        let theme = hook.UseStore Theme.Dark
    
        // Event
        hook.OnFirstAfterRender
        // async task
        |> Observable.map themeService.GetTheme
        |> Observable.switchTask
        // update the store
        |> Observable.subscribe theme.Publish
        // ensure disposal from the component's subscription
        |> hook.AddDispose
    
        nav() {
            // the rest of the view code
        }
    )
    
    다른 블라조르의 생명주기 연결도 이 정도는 할 수 있다.
    재미있다.Blazor는 elmish 이외에 상태를 처리하는 방법을 제공합니다. 이 방법들은 매우 강력해서 기존 프레임워크/방법을 사용할 때 발생하는 번거로움을 피할 수 있다고 믿습니다

    결어


    그렇지!나는 F#의 Web Assembly에 대해 약간의 사랑을 주었다. 나는 여전히 완전한 웹 assembly를 사용하는 것을 고려하고 있지만 웹 assembly는 브라우저의 표준의 일부이기 때문에 적합한 플러그인이 아니기 때문에 고려할 수 있는 대체 방안이 될 것이다(미안해, Silverlight)
    상태 처리 옵션(기본적으로 elmish)과 DSL 옵션을 감안하면 볼레로는 나를 진정으로 끌어당기지는 않았지만 재미있었다.Blazor와 그가 제공하는 상태 관리 옵션은 정말 시도할 만하다고 생각합니다. 이것은 여전히 젊은 라이브러리이기 때문에 당신이 그것을 시도하고 오류를 보고하며 피드백을 제공하고 성장할 수 있도록 도와주세요!

    좋은 웹페이지 즐겨찾기