运行应用程序
在本指南的过程中,我们已经多次看到运行Halogen应用程序的标准方法。在本章中,我们将了解运行Halogen应用程序时实际发生的情况以及如何从外部控制正在运行的应用程序。
使用runUI和awaitBody
PureScript应用程序使用它们Main模块中的main函数作为它们的入口点。这是Halogen应用程序的标准main函数:
module Main where
import Prelude
import Effect (Effect)
import Halogen.Aff as HA
import Halogen.VDom.Driver (runUI)
main :: Effect Unit
main = HA.runHalogenAff do
body <- HA.awaitBody
runUI component unit body
-- 假设您已经为您的应用程序定义了一个根组件
component :: forall query input output m. H.Component query input output m
component = ...
main中使用的最重要的函数是runUI函数。为runUI提供根组件、根组件的input值和对DOM元素的引用,它将向Halogen虚拟DOM提供您的应用程序. 然后,虚拟DOM将在该元素上渲染您的应用程序,并在应用程序运行期间保持它。
runUI
:: forall query input output
. Component query input output Aff
-> input
-> DOM.HTMLElement
-> Aff (HalogenIO query output Aff)
如您所见,runUI函数要求您的Halogen应用程序最终可以在Aff monad中运行。在本指南中,我们使用了诸如monadefect和MonadAff之类的约束,这些约束是Aff所满足的,所以我们很清楚。
如果您选择为您的应用程序使用另一个
monad,那么您需要在将您的应用程序提供给runUI之前将其提升以在Aff中运行。Real World Halogen使用一个自定义的AppM monad,这是一个很好的例子。
除了runUI,我们还使用了另外两个辅助函数。首先,我们使用awaitBody等待页面加载,然后获取对<body>标签的引用作为应用程序控制的根HTML元素。其次,我们使用runHalogenAff从Effect内部启动异步effects(我们的Aff代码包含awaitBody和runUI).这是必要的,因为awaitBody、runUI和我们的应用程序运行在Aff monad中,而PureScript的main函数必须在Effect中。
我们在这里使用的main函数是运行Halogen应用程序的标准方式,它是页面上唯一运行的东西。但是,有时您可能会使用Halogen来接管页面的一部分,或者您可能正在运行多个Halogen应用程序。在这些情况下,您可能会用到一对不同的辅助函数:
awaitLoad阻塞,直到文档加载完毕,以便您可以安全地检索对页面上HTML元素的引用selectElement可用于定位页面上的特定元素以将应用程序嵌入其中
使用HalogenIO
当您使用runUI运行Halogen应用程序时,您会收到HalogenIO类型的函数记录. 这些函数可用于从应用程序外部控制您的根组件。从概念上讲,它们就像您的应用程序的临时父组件。
type HalogenIO query output m =
{ query :: forall a. query a -> m (Maybe a)
, messages :: Event output
, dispose :: m Unit
}
query函数类似于支持tell和request的H.query函数。这允许您从应用程序外部向应用程序的根组件发送query.messages事件可用于订阅来自组件的output消息流 —— 它就像我们提供给slot函数的处理程序,除了在这里我们可以执行一些effect而不是评估一个action。dispose函数可用于停止和清理Halogen应用程序。这将杀死所有分叉的线程,关闭所有订阅,等等。
您不能在应用程序的根目录中使用tell和request,但是可以使用mkTell和mkRequest函数(如下例所示)来达到类似的效果。
Halogen应用中一个常见的模式是使用一个Route组件作为应用的根,当URL改变时使用HalogenIO中的query函数来触发应用中的路由改变.您可以在Real World HalogenMain.purs文件中看到执行此操作的完整示例。
完整示例: 使用HalogenIO控制按钮
您可以将此示例粘贴到Try PureScript中,以探索使用HalogenIO来控制应用程序的根组件。
module Example.Driver.IO.Main where
import Prelude
import Data.Maybe (Maybe(..))
import Effect (Effect)
import Effect.Console (log)
import Halogen (liftEffect)
import Halogen as H
import Halogen.HTML as HH
import Halogen.Aff as HA
import Halogen.HTML.Events as HE
import Halogen.HTML.Properties as HP
import Halogen.Subscription as HS
import Halogen.VDom.Driver (runUI)
main :: Effect Unit
main = HA.runHalogenAff do
body <- HA.awaitBody
io <- runUI component unit body
_ <- liftEffect $ HS.subscribe io.messages \(Toggled newState) -> do
liftEffect $ log $ "Button was internally toggled to: " <> show newState
pure Nothing
state0 <- io.query $ H.mkRequest IsOn
liftEffect $ log $ "The button state is currently: " <> show state0
void $ io.query $ H.mkTell (SetState true)
state1 <- io.query $ H.mkRequest IsOn
liftEffect $ log $ "The button state is now: " <> show state1
-- Child component implementation
type Slot = H.Slot Query Message
data Query a
= IsOn (Boolean -> a)
| SetState Boolean a
data Message = Toggled Boolean
data Action = Toggle
type State = { enabled :: Boolean }
component :: forall i m. H.Component Query i Message m
component =
H.mkComponent
{ initialState
, render
, eval: H.mkEval $ H.defaultEval
{ handleAction = handleAction
, handleQuery = handleQuery
}
}
initialState :: forall i. i -> State
initialState _ = { enabled: false }
render :: forall m. State -> H.ComponentHTML Action () m
render state =
let
label = if state.enabled then "On" else "Off"
in
HH.button
[ HP.title label
, HE.onClick \_ -> Toggle
]
[ HH.text label ]
handleAction :: forall m. Action -> H.HalogenM State Action () Message m Unit
handleAction = case _ of
Toggle -> do
newState <- H.modify \st -> st { enabled = not st.enabled }
H.raise (Toggled newState.enabled)
handleQuery :: forall m a. Query a -> H.HalogenM State Action () Message m (Maybe a)
handleQuery = case _ of
IsOn k -> do
enabled <- H.gets _.enabled
pure (Just (k enabled))
SetState enabled a -> do
H.modify_ (_ { enabled = enabled })
pure (Just a)
下一步
本指南展示了Halogen应用的基本构建块。我们了解了Halogen如何提供一种类型安全、声明式的方式来使用称为组件的可重用部分构建复杂的应用程序.我们学习了如何编写生Halogen HTML的函数、如何编写单个组件以及如何在其他组件中渲染组件。我们还学习了组件如何相互通信以及如何运行完整的Halogen应用程序。
您现在知道Halogen是如何工作的,但您可能还不太习惯使用该库构建一个真正的应用程序。这是完全正常的!有更多资源可帮助您继续了解Halogen。
- 要更深入地了解您在本指南中学到的概念,请浏览概念参考。
- 要以慢节奏、自下而上的方式学习
Halogen,请尝试查看Jordan Martinez的Learn Halogen存储库。 - 要了解如何在
Halogen中构建真实世界的应用程序,请查看Real World Halogen手册和示例应用程序。