恕我直言,这看起来很棒。
谢谢! 很高兴你喜欢它:微笑:
这是否易于扩展 3rd 方的东西,例如 Material-UI?
当然可以,但是方法有点不同,因为我们没有使用有区别的联合。 我尝试在时间允许的情况下写一些东西,但这个想法是你基本上有两个选择:
prop
静态类型,增加更多功能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
类型。 看起来很棒的作品; 如果您同时打开Feliz
和Feliz.MaterialUI
,您当然可以访问所有匹配类型的所有成员。
我有一个类型Mui
对应于Html
并包含实际组件。
(目前我已将组件特定的道具放置在prop
的单独子模块中,如 #13 中所述。)
Feliz 的一个可能改进点是有一个reactElement
和createElement
接受的不是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 的上下文中)为什么首先要内联呢? “简单”函数/方法体有什么好处?
[<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 ,我想我们可以考虑解决这个问题,对吧? 我将关闭问题,如果需要,请重新打开以进一步讨论
最有用的评论
你好@cmeeren ,我想我们可以考虑解决这个问题,对吧? 我将关闭问题,如果需要,请重新打开以进一步讨论