Feliz: 第三方指导

创建于 2019-08-11  ·  25评论  ·  资料来源: Zaid-Ajaj/Feliz

恕我直言,这看起来很棒。

这是否易于扩展 3rd 方的东西,例如Material-UI

有一些关于如何做到这一点的指南会很棒。 :)

最有用的评论

你好@cmeeren ,我想我们可以考虑解决这个问题,对吧? 我将关闭问题,如果需要,请重新打开以进一步讨论

所有25条评论

恕我直言,这看起来很棒。

谢谢! 很高兴你喜欢它:微笑:

这是否易于扩展 3rd 方的东西,例如 Material-UI?

当然可以,但是方法有点不同,因为我们没有使用有区别的联合。 我尝试在时间允许的情况下写一些东西,但这个想法是你基本上有两个选择:

  • 1) 扩展prop静态类型,增加更多功能
  • 2)创建一个类似的prop静态类型,特定于您的库(可能是Mui ),它具有 Mui 所需的所有属性。 您甚至可以使用Mui prop起别名,并使用 Mui 特定(重载)的属性和函数扩展Mui

扩展很简单:

type prop with
  static member hello (value: string) = Interop.mkAttr "hello" value 

这是否是库的“最终形式”以及扩展它的方式,仍在考虑中,因为我首先想通过构建 3rd 方库来尝试它,看看它是如何进行的

谢谢! 我已经在为 MUI 测试一些东西了,只是针对 API 的一小部分,看看它是如何工作的。 可能会在一周/结束的某个时间分享,如果你能看看它只是为了看看我是否走上正轨并追随费利斯精神,那就太好了。 到目前为止似乎成功了!

但是,我没有扩展现有的prop类型。 我在另一个命名空间( Feliz.MaterialUI )中定义了一个单独的prop类型。 看起来很棒的作品; 如果您同时打开FelizFeliz.MaterialUI ,您当然可以访问所有匹配类型的所有成员。

我有一个类型Mui对应于Html并包含实际组件。

(目前我已将组件特定的道具放置在prop的单独子模块中,如 #13 中所述。)

Feliz 的一个可能改进点是有一个reactElementcreateElement接受的不是string ,而是ReactElementType (我认为)。 这样我们就可以调用createElement (importDefault "@material-ui/core/Button") 。 我目前自己创建了这两个助手。

顺便说一句,所有成员都应该是inline吗? 有什么优点/缺点? 我注意到您没有在上面使用inline ,但是 Feliz 中的所有内容都是inline

谢谢! 我已经在为 MUI 测试一些东西了,只是针对 API 的一小部分,看看它是如何工作的。 可能会在一周/结束的某个时间分享,如果你能看看它只是为了看看我是否走上正轨并追随费利斯精神,那就太好了。 到目前为止似乎成功了!

棒极了! 如果你愿意,我一定很乐意看看

但是,我没有扩展现有的道具类型。 我在另一个命名空间(Feliz.MaterialUI)中定义了一个单独的道具类型。 看起来很棒的作品; 如果您同时打开 Feliz 和 Feliz.MaterialUI,您当然可以访问所有匹配类型的所有成员。

我有一个类型 Mui 对应于 Html 并包含实际组件。

这就是我会为梅做的

(目前我已将特定于组件的 props 放置在 prop 的单独子模块中,如 #13 中所述。)

对梅来说很有意义,因为你有多少选择

Feliz 的一个可能改进点是有一个 reactElement 和 createElement 不接受字符串,而是接受 ReactElementType (我认为)。 这样我们就可以调用 createElement (importDefault "@material-ui/core/Button")。 我目前自己创建了这两个助手。

我正在审查我在Interop模块中使用的成员,我刚刚暴露了我在库中使用的任何内容,将重新考虑稳定版本

顺便说一句,所有成员都应该内联吗? 有什么优点/缺点? 我注意到您没有在上面使用 inline,但 Feliz 中的所有内容都是内联的。

我应该内联上面的扩展成员!

经验法则是:如果属性的值是原始的,例如字符串/int/bool/enum,则内联该属性,但如果您的属性根据输入计算值,那么最好不要内联,因为每次用户调用内联函数时,整个函数体都内联在该调用位置,因此如果用户在整个应用程序中使用同一个函数 10 次,则函数体将内联编译 10 次,而不是定义一次并引用 10 次

经验法则是:如果属性的值是原始的,例如字符串/int/bool/enum,则内联该属性,但如果您的属性根据输入计算值,那么最好不要内联,因为每次用户调用内联函数时,整个函数体都内联在该调用位置,因此如果用户在整个应用程序中使用同一个函数 10 次,则函数体将内联编译 10 次,而不是定义一次并引用 10 次

很高兴知道! 但是(在 Fable 的上下文中)为什么首先要内联呢? “简单”函数/方法体有什么好处?

  • 捆绑包大小:如果不使用这些函数(并且有 100 个),它们仍然会被编译,扩大捆绑包大小 AFAIK(影响当然仍然相对于应用程序的总大小)
  • 泛型类型参数:与 Feliz 无关,但在 Fable 的上下文中,如果调用位置未内联(所有泛型类型参数必须在编译时静态解析),则具有泛型类型参数的泛型函数将无法编译,特殊情况除外您可以在其中使用[<Inject>] ITypeResolver<'t>作为静态类的可选参数(只有高度专业化的库使用此功能,请参阅 Fable.SimpleJson/Thoth.Json)

我认为当你制作生产包时,babel 会进行 tree-shaking 并删除未使用的功能。 内联会打败它。

@Luiz-Monad 那么您是说理想情况下,Feliz 中的任何内容都不应该内联吗? 出于捆绑大小原因的内联会适得其反吗?

@Luiz-Monad 你说的太棒了! 至少如果编译以这种方式工作。 这是您可以尝试使用 REPL 的示例:

module App

type prop = 
  // does useless stuff
  static member f() = 
    [ 1 .. 100 ]
    |> List.map (fun x -> x * 20)
    |> List.collect (fun n -> [n; n])
    |> List.fold (+) 0

  // does useless stuff
  static member inline k() = 
    [ 1 .. 100 ]
    |> List.map (fun x -> x * 20)
    |> List.collect (fun n -> [n; n])
    |> List.fold (+) 0

  static member g() = 1

let value = prop.g()

printfn "%d" value

其中prop包含:

  • f()包含函数体 -> 未内联且未使用
  • k()包含函数体 -> 内联但未使用
  • g()包含一个函数 -> 不是内联且已使用

您会认为f()g()都不会被编译,但事实并非如此,无论如何都会编译f() (未内联且未使用),但k() (内联,未使用)不会进入编译包

import { fold, collect, map, ofSeq, ofArray } from "fable-library/List.js";
import { type } from "fable-library/Reflection.js";
import { rangeNumber } from "fable-library/Seq.js";
import { toConsole, printf } from "fable-library/String.js";
import { declare } from "fable-library/Types.js";
export const prop = declare(function App_prop() {});
export function prop$reflection() {
  return type("App.prop");
}
export function prop$$$f() {
  return fold(function folder(x$$1, y) {
    return x$$1 + y;
  }, 0, collect(function mapping$$1(n) {
    return ofArray([n, n]);
  }, map(function mapping(x) {
    return x * 20;
  }, ofSeq(rangeNumber(1, 1, 100)))));
}
export function prop$$$g() {
  return 1;
}
export const value = prop$$$g();
toConsole(printf("%d"))(value);

它确实有效,但是您需要为此配置webpack ,因为摇树本身并不是寓言,所以它在 REPL 中不起作用。

/// Library.fs
module Library

type prop = 
    // does useless stuff
    static member f() = 
      [ 1 .. 100 ]
      |> List.map (fun x -> x * 20)
      |> List.collect (fun n -> [n; n])
      |> List.fold (+) 0

    // does useless stuff
    static member inline k() = 
      [ 1 .. 100 ]
      |> List.map (fun x -> x * 20)
      |> List.collect (fun n -> [n; n])
      |> List.fold (+) 0


type AppMain =
    static member g() = 1

//// App.fs
module App

let value = Library.AppMain.g ()

printfn "%d" value

  declare(function Library_prop() { 
     // see its empty, this weren't removed too because of `keep_classnames: true, keep_fnames: true ` in the terser plugin
  });
  declare(function Library_AppMain() {
  });
  !function toConsole(arg) {
    return arg.cont(function (x) {
      console.log(x)
    })
  }(printf('%d')) (1),
  __webpack_require__.d(__webpack_exports__, 'value', function () {
    return 1
  })

此外,您的测试有一个警告,入口文件有点特殊,因为它的功能不会被删除。 (我想这与静态类的初始化有关,必须调用静态初始化构造函数来对模块产生副作用)

请参阅我为此测试制作的这个存储库https://github.com/Luiz-Monad/test-tree-shaking

非常感谢示例回购! 我肯定会进一步调查,看看内联是否真的在 Feliz 的上下文中做任何有用的事情

我肯定会进一步调查,看看内联是否真的在 Feliz 的上下文中做任何有用的事情

酷,期待听到你的发现:)

棒极了! 如果你愿意,我一定很乐意看看

请查看cmeeren/fable-elmish-electron-material-ui-demo 上的feliz分支

大多数代码是基于 (HTML) API 文档自动生成的。 有一个生成器项目(丑陋和 hacky,但可以完成工作)和一个用于实际绑定的项目。 在渲染器项目中,只有App.fs已转换为使用新的 Feliz 样式绑定。

请在您喜欢的时候看看它,如果您有任何问题,请告诉我您的想法。

@cmeeren看起来非常好,比当前的 API 恕我直言要好得多,但它仍然有点难以阅读,但这更多的是库本身的性质,它有很多非常具体的部分,你必须熟悉。 我认为有些地方可以改进,举个例子:

Mui.appBar [
  prop.className c.appBar
  prop.appBar.position.fixed'
  prop.children [
    Mui.toolbar [
      prop.children [
        Mui.typography [
          prop.typography.variant.h6
          prop.typography.color.inherit'
          prop.text (pageTitle model.Page)
        ]
      ]
    ]
  ]
]

我个人对此片段的完美版本是将其变成以下内容:

Mui.appBar [
  AppBar.className c.appBar
  AppBar.position.fixed'
  AppBar.children [
    Mui.toolbar [
      Toolbar.children [
        Mui.typography [
          Typography.variant.h6
          Typography.color.inherit'
          Typygraphy.text (pageTitle model.Page)
        ]
      ]
    ]
  ]
]

这样很容易找到 Mui 元素,因为您可以“点通”Mui,一旦找到您的元素( appBar ),您就可以“点通”模块名称( AppBar )定义属性等

也许将AppBar也保留为小写

我想你明白了,但更准确地说,这个 API 的一般语法如下,其中{Element}是一个反应元素:

Mui.{element} [
  {Element}.{propName}.{propValue}
  {Element}.children [
    Mui.{otherElem} [
      {OtherElem}.{propName}.{propValue}
      // etc.
    ]
  ]
]

你怎么看待这件事? 如果你想看到具体的实现,这个 API 还启发了库的fabulous-simple-elements的想法

我认为这很完美,而且正是我想征求你意见的那种事情。 我最初选择将所有内容都放在prop之下,因为这就是主库的工作方式,但当然你实际上并没有任何特定于组件的道具,而 MUI 更多的是更少只有特定于组件的道具。

我认为坚持使用小写模块名称可能看起来最好(并节省额外的击键),但我愿意接受反驳。

很好,我已经自动生成了这些东西,这使得更改变得简单。

我会更新并让你知道。

不过,我特别想就一件事发表意见。 这是ClassName的东西。 makeStyles钩子返回一个对象,它的 props 与传入的对象相同,但是每个 (JSS) 元素都已替换为字符串,这是要使用的类名(并且可以传递给例如prop.className )。

现在,没有办法在 F# 中输入它,所以我必须使用我所拥有的。 通常样式对象的所有道具都是IStyleAttribute list 。 这意味着我可以为prop.className添加一个接受IStyleAttribute list的重载,这当然是一个谎言,因为在运行时它是一个字符串。 如果你真的通过了它IStyleAttribute list它会失败。 除了prop.className之外,这也适用于所有classes.<element>.<classesName> (用于<element>.classes [ ... ] )。 它们还接受类名(字符串)。

如您所见,我所做的是“要求”将样式对象的所有IStyleAttribute list属性包装在asClassName中,这基本上只是拆箱到IClassName (如果您愿意,可以使用string的代理)。 然后我在prop.className中添加了一个重载,接受IClassName ,并使所有classes道具都接受IClassName 。 我喜欢它的类型更强,但我不喜欢它需要额外的类型(每个顶级 CSS 规则都需要asClassName )。 如果你错过它,编译器会抱怨,但它不会告诉你该怎么做,而且它仍然是额外的噪音。

你对此有什么意见吗?

另外,我注意到了这一点:

f# listItem.divider ((page = Home))

在这里,双括号是必需的,否则 F# 会将其解释为尝试调用listItem.divider并将(不存在的) page参数设置为Home (而不是value参数设置为page = Home )。 你有没有办法避免这种情况?

你好@cmeeren ,首先,我非常喜欢这种语法:

Mui.appBar [
  prop.className c.appBar
  appBar.position.fixed'
  appBar.children [
    Mui.toolbar [
      toolbar.children [
        Mui.typography [
          typography.variant.h6
          typography.color.inherit'
          prop.text (pageTitle model.Page)
        ]
      ]
    ]
  ]
]

它看起来很干净,很简单! 虽然如果我是你,我可能会将一些通用的prop函数复制到特定于组件的道具中,例如appBar.className而不是(或旁边) prop.className以便它们看起来都是对称的,但更重要的是,将IClassName重载给特定于 Mui 的组件,而不是使用字符串的通用prop.className ,因为makeStyles也是特定于 Mui 的构造,它只适用于 Mui 组件是有道理的。

我认为您以最好的方式解决了makeStyles问题,至少现在我想不出更好的方法(虽然我不是asClassName的忠实粉丝,也许Styles.createClass而是

至于listItem.divider ((page = Home))这很棘手,您可以添加一个虚拟函数let when (x: bool) = x但这只是噪音。 我认为最好将其归档为编译器错误,因为我想不出 F# 编译器无法解决函数的正确重载的原因,我自己没有尝试过,但我会在时间允许时研究它

最后,这周我的反应不如往常,因为我现在正在度假,所以我可能无法阅读/检查所有内容等。

虽然如果我是你,我可能会将一些通用的prop函数复制到特定于组件的道具中,例如appBar.className而不是(或旁边) prop.className以便它们看起来都是对称的,但更重要的是,将IClassName重载给特定于 Mui 的组件,而不是使用字符串的通用prop.className ,因为makeStyles也是特定于 Mui 的构造,它只适用于 Mui 组件是有道理的。

现在看看 :) 我在过去的几天里做了很大的改进和扩展,只是推送它们(没有完成,我目前正在检查所有 MUI 组件以验证和改进生成的道具)。

我认为你以最好的方式解决了makeStyles问题,至少现在我想不出更好的方法(虽然我不是asClassName的忠实粉丝,也许Styles.createClass而是

我希望它尽可能简短,因为它会被大量使用,但我对其他名称持开放态度。 虽然我有一半的想法只是有一个IStyleAttribute list超载。 它可能会消除相当多的噪音,我怀疑即使在技术上可以不正确地使用它也会非常危险。

至于listItem.divider ((page = Home))这很棘手,您可以添加一个虚拟函数let when (x: bool) = x但这只是噪音。 我认为最好将其归档为编译器错误,因为我想不出 F# 编译器无法解决函数的正确重载的原因,我自己没有尝试过,但我会在时间允许时研究它

谢谢,我现在提出了一个问题: https ://github.com/dotnet/fsharp/issues/7423

最后,这周我的反应不如往常,因为我现在正在度假,所以我可能无法阅读/检查所有内容等。

明白了,没问题。 如果我遇到问题,我会继续发布问题,您只需在自己的时间回复。

现在看看 :) 我在过去的几天里做了很大的改进和扩展,只是推送它们(没有完成,我目前正在检查所有 MUI 组件以验证和改进生成的道具)。

看起来真的很好,生成的文档也是如此😍也许是时候放入它自己的 repo 了?

我希望它尽可能简短,因为它会被大量使用,但我对其他名称持开放态度。 虽然我有一半的想法只是有一个 IStyleAttribute 列表重载。 它可能会消除相当多的噪音,我怀疑即使在技术上可以不正确地使用它也会非常危险。

这也行,寓言库一直在欺骗类型系统;)

谢谢,我现在提出了一个问题:dotnet/fsharp#7423

惊人的! 多谢

看起来真的很好,生成的文档也是如此😍也许是时候放入它自己的 repo 了?

我一直在考虑这个问题,但是仍然存在一些重要的错误(例如#27)用于 nuget 的预发布(希望不会太长)。

@Zaid-Ajaj 我即将完成 Feliz.MaterialUI。 将很快发布到一个单独的仓库。 让你复习它会很棒,首先是检查一些设计决策并确保与 Feliz 的一致性,还要检查一些实现的东西(例如,我是否使用了你将在内部制作的东西,或者我有什么东西'不是从 Feliz 使用,但应该使用)。

当我创建新的存储库时,如果我创建问题来解释我想要审查的内容并标记你,是否可以?

我现在已将 Feliz.MaterialUI 的草稿上传到cmeeren/Feliz.MaterialUI 。 我已经创建了几个我想要审查的问题。

如果您能抽出时间来看看它们,我将不胜感激!

无需在每个问题上花费大量时间; 由于我之前评论中提到的原因,我真的只是想要第二个意见。 如果你愿意,我很高兴进入杂草,但即使只是“这看起来不错”也会很棒。

当然,不用着急。 :)

很棒的工作@cmeeren! 乍一看,绑定看起来很干净,我会在接下来的几天里看看每个问题,我保证:smile:

嘿! 有机会继续看问题吗? 再次不着急,只是一个友好的提醒,因为我已经有几个星期没有收到你的消息了😃

我实际上已经研究过这些问题,但就像我之前所说的,API 是否好来自用例,这就是为什么我要求您发布 alpha 版本以便我可以尝试但我还没有是时候这样做了(这周我一直在想:微笑:)

你好@cmeeren ,我想我们可以考虑解决这个问题,对吧? 我将关闭问题,如果需要,请重新打开以进一步讨论

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

nojaf picture nojaf  ·  4评论

l3m picture l3m  ·  10评论

alfonsogarciacaro picture alfonsogarciacaro  ·  11评论

Dzoukr picture Dzoukr  ·  9评论

heimeshoff picture heimeshoff  ·  6评论