路由
Rescript-React 附带路由!我们利用语言和库的特性,为了创建有以下优点路由:
最简单、最轻薄。
即插即用。
小巧但性能强劲。
路由如何工作?
可用函数如下:
RescriptReactRouter.push(string)
:用新的路径更新 URL。RescriptReactRouter.replace(string)
:类似push
,但是会替换当前 URL。RescriptReactRouter.watchUrl(f)
:开始监听 URL 更改。返回一个订阅 token。当 URL 更改后,使用RescriptReactRouter.url
记录作为参数调用回调函数。RescriptReactRouter.unwatchUrl(watcherID)
:停止监听 URL 更改。RescriptReactRouter.dangerouslyGetInitialUrl()
:在watchUrl
之外,获取url
记录。稍后进行说明。RescriptReactRouter.useUrl(~serverUrl)
:返回组件内的url
记录。
如果你想要了解更多路由接口的底层实现细节,请参考 RescriptReactRouter 实现。
路由匹配
没有 API ! watchUrl
返回如下结构的 url
记录:
类似 www.hello.com/book/10/edit?name=Jane#author
这样的地址会被解析为:
基础示例
让我们看看 ReScript React 路由的例子:
直接获取路由
在特定场合中,你可能想在 watchUrl
之外操作 url
记录。例如,如果你将 watchUrl
放在组件的 didMount
中,从而在 URL 更改时触发组件的 state 更改,你可能还希望由 URL 决定初始状态。
换句话说,如果你需要在应用的逻辑开始时就读取 url
记录的内容,我们提供了 dangerouslyGetInitialUrl()
接口。
注意:我们将其标记为“危险”,是为了提醒你不要在任意组件的例如 render
内读取 url
,因为如果组件不包含watchUrl
订阅来在 URL 变更时重新渲染,则获取的 url
信息可能已经过时。请你只将 dangerouslyGetInitialUrl
和 watchUrl
一起使用。
推送新路由
在应用的任何地方,只需要调用例如 RescriptReactRouter.push("/books/10/edit#validated")
的命令,就会触发 URL 变更(但页面不会刷新),而且 watchUrl
的回调会被调用。
我们可能会在未来为类型化路由 + 承载 payload 提供更好的基础设施!
注意:由于浏览器限制,无法检测到通过 JavaScript 更改的 URL (又叫 pushState)。解决方案是更改 URL,然后触发“popState”事件(被 watchUrl
监听)。这就是 RescriptReactRouter.push
的内部原理。
因此,如果出于一些原因(例如增量迁移),你需要绕过 RescriptReactRouter.push
更新 URL,使用 window.dispatchEvent(new Event('popState'))
。
设计决策
我们一直努力降低 Rescript-React 的性能开销和学习成本,路由设计也是一样。除浏览器特性检测外,整个路由的实现大约 20 行代码。回想起来,这样设计似乎是明确的,但是为了达到这个目的,我们必须深入挖掘 ReactJS 内部机制和 future proposals,来确保我们理解了状态更新机制,future context proposal,生命周期顺序等概念。并在这个过程中拒绝一些糟糕的 API 设计。很高兴能达成如此明确的解决方案!
路由 API 也没有规定路由匹配需要返回组件,状态更新,还是副作用。它的灵活性足以适配现有的应用。
在性能方面,类 JavaScript API 倾向于使用路由字符串的 JS 对象 -> 回调。为了支持模式匹配,我们不会那样做,因为在 ReScript 中后者不分配内存,而是在 C++ 中(通过 JS JIT)编译为快速跳转表。实际上,在路由匹配中只有创建 url
记录时进行了内存分配。