这是一个比您意识到的要大得多的问题,因为在它下面是“我应该如何最好地安排我的 Fable 应用程序”的问题?您在回答中采用的方法与在 Javascript 应用程序中使用 JQuery 大致平行:您编写的代码直接操作 DOM 以实现您想要的结果。如果这是您选择的架构风格——对于非常简单的应用程序,这是一个非常好的选择——那么你的答案就无法改进了。 “直接操作 DOM”方法唯一会遇到问题的情况是,如果您的应用程序增长到比普通大小更大的大小。那时,您将需要更好的架构。稍后我会推荐一个更好的架构,但首先,让我推荐对您的代码进行小的调整。将 toggle 函数移到 toggleButton 之外会使其更加通用:
module Markup
open System
open Fable.Helpers.React
open Fable.Helpers.React.Props
open Fable.Import
let toggleBlockElem id _ =
let contentElement = Browser.document.getElementById id
let display = if contentElement.style.display = "none" then "block" else "none"
contentElement.style.display <- display
let toggleButton text content =
let id = Guid.NewGuid().ToString()
div []
[
button [ OnClick (toggleBlockElem id) ] [ str text ]
div [ Id id ] [ str content ]
]
如果您发现自己需要这些功能,则可以在 toggleLink 或 toggleCheckbox 等函数中重用此函数。
现在,我之前提到过,如果您的网络应用程序变大,我会推荐一种不同的架构。我推荐的架构是Elmish。它基于Elm 中使用的架构;如果你不熟悉它,其基本思想类似于 React。这是一个模型/更新/视图架构。 模型 是不可变的数据结构。还定义了一个 message 类型;在 Elmish 中,这可能是一个 F# 区分联合。 update 函数将模型和消息作为其两个参数,并返回一个新模型。 view 函数接受一个模型和一个“dispatch”函数作为它的两个参数(“dispatch”函数将由 Elmish 提供,你不必编写它) 并返回类似 HTML 元素的抽象树。 Elmish 然后将这些元素传递给 React 之类的东西来进行实际的 DOM 更新。 (本质上,React 将区分“旧”DOM 树和“新”DOM 树,其过程很像 Elm 的 the one described here)。
所有这些都需要考虑,所以让我们看一个简单的示例,它只是切换div 的可见性。 注意:我没有对此进行测试,只是将它输入到 Stack Overflow 答案框中。下面的代码可能存在错误;它旨在作为说明性示例而不是工作示例。 (有关 Elmish 的一个非常全面的实际工作示例,请参阅 https://mangelmaxime.github.io/fulma-demo/ - 尽管请注意,该示例具有一个高级架构,在数据模型中包含了多个父子层次结构的“层”,并且可能很难将您的想法包裹起来乍一看)。
让我们从定义数据模型开始。由于这是一个微不足道的示例,因此该模型同样微不足道;如此微不足道,事实上,我将向它添加一些额外的数据,以便使用记录类型而不是单个布尔值实际上是有意义的:
type Model = { Visible: bool;
DataNotAppearingInThisFilm: int }
(更高级的模型可能涉及Map<string, bool> 类型以跟踪多个divs 的可见状态)。
消息类型也很简单,所以为了让它更有趣,我们将允许“显示”、“隐藏”和“切换”消息:
type Msg =
| Show
| Hide
| Toggle
(更高级的版本是Show of string等,传递div的ID来显示)。
update 函数同样简单:
let update msg model =
match msg with
| Show -> { model with Visible = true }
| Hide -> { model with Visible = false }
| Toggle -> { model with Visible = not model.Visible }
最后,view 函数将如下所示:
let view model dispatch =
div []
[
button [ OnClick (fun _ -> dispatch Toggle) ] [ str (if model.Visible then "Hide" else "Show") ]
div [ Display (if model.Visible then "block" else "none") ] [ str "content" ]
]
或者,将toggleButton 拉出到它自己的函数中,这样我就可以展示它是如何工作的:
let toggleButton dispatch text content =
div []
[
button [ OnClick (fun _ -> dispatch Toggle) ] [ str text ]
div [ Display (if model.Visible then "block" else "none") ] [ str content ]
]
let view model dispatch =
div []
[
str "Other elements might go here"
toggleButton dispatch (if model.Visible then "Hide" else "Show") "content"
]
注意我需要将dispatch 函数作为参数传递给toggleButton,以便toggleButton 能够为其OnClick 构造正确的行为。
将所有这些放在一起看起来像这样:
open ALotOfModules
type Model = { Visible: bool;
DataNotAppearingInThisFilm: int }
type Msg =
| Show
| Hide
| Toggle
let update msg model =
match msg with
| Show -> { model with Visible = true }
| Hide -> { model with Visible = false }
| Toggle -> { model with Visible = not model.Visible }
let toggleButton dispatch text content =
div []
[
button [ OnClick (fun _ -> dispatch Toggle) ] [ str text ]
div [ Display (if model.Visible then "block" else "none") ] [ str content ]
]
let view model dispatch =
div []
[
str "Other elements might go here"
toggleButton dispatch (if model.Visible then "Hide" else "Show") "content"
]