前言

前端技术如今蓬勃发展,时时刻刻都有新的技术栈诞生,这使得各位前端们常常大呼学不动了。既然如此笔者为何还要去学一门和 TypeScript 类似的新语言呢,难道 TypeScript 不够用嘛。

TypeScript 生态现在已经足够强大了,那么学习一个新的 ReScript 又有什么优势呢?ReScript 是对函数式友好的编程语言,函数默认自带柯里化,原生支持 pipe 功能,友好的类型推导,以及没有 null 和 undefined 的概念来消除了因其而产生的 Bug 。还有一点就是官方的 compiler 是不依赖于 node 的原生二进制程序,所以编译速度是 TypeScript 所不能及的,虽然目前已经有诸如 esbuild 之类的 TypeScript 编译器,速度是够快,但不支持类型判断。

项目地址 在线 demo

下面笔者就从头开始写一个 Todo List 来简单的认识一下 ReScript

新建 Reducer.res

  • 定义数据类型
type action =| AddTodo(string)| RemoveTodo(int)| ToggleTodo(int)
// reducer action 直接用 ReScript 自带的 Variant 结构来定义,可以利用其强大的模式匹配能力减少样板代码
type todo = {id: int,completed: bool,text: string,
}
type state = {todos: array<todo>,nextId: int,
}
  • 添加 reducer 函数
let reducer = ({todos, nextId}, action) => {switch action {| AddTodo(text) => {todos: todos->Belt.Array.concat([{id: nextId,text: text,completed: false,},]),nextId: nextId + 1,}| RemoveTodo(id) => {todos: todos->Belt.Array.keep(todo => todo.id != id), nextId: nextId}| ToggleTodo(id) => {todos: todos->Belt.Array.map(todo => {...todo,completed: todo.id == id ? !todo.completed : todo.completed,}),nextId: nextId,}}
}

不用手动指定 reducer 参数的类型,编译器会根据上下位自动推断出 {todos, nextId}: state, action: action

因为有强大的匹配模式能力,代码结构清晰来许都,也不用写得像 TypeScript 那么啰唆;

TypeScript 版本:

type Action = {type: "ADD_TODO" | "REMOVE_TODO" | "TOGGLE_TODO"id?: numbertext?: string
}
type Todo = {id: numbertext: stringcompleted: boolean
}
type State = {todos: Todo[]nextId: number
}let reducer = ({ todos, nextId }: State, action: Action) => {switch (action.type) {caTodoItemse "ADD_TODO":return {todos: [...todos,{id: nextId,text: action.text,completed: false,},],nextId: nextId + 1,}case "REMOVE_TODO":return { todos: todos.filter((todo) => todo.id != action.id), nextId: nextId }case "TOGGLE_TODO":return {todos: todos.map((todo) => ({...todo,completed: todo.id == action.id ? !todo.completed : todo.completed,})),nextId: nextId,}}
}

添加组件

  • 添加 AddTodo 组件
@react.component
let make = (~onAdd: option<string> => unit) => {let inputValueRef: React.ref<option<string>> = React.useRef(None)let onSubmit = (e: ReactEvent.Form.t) => {e->ReactEvent.Form.preventDefaultonAdd(inputValueRef.current)}<form className="rounded-lg w-full grid px-6 grid-cols-12" onSubmit><inputclassName="border rounded-l-lg bg-slate-50 h-8 text-xl py-6 px-4 ring-sky-200 col-span-10  focus:(outline-none border-sky-400 ring-2 z-10) "onChange={e => e->ReactEvent.Form.target->(target => inputValueRef.current = target["value"])}required={true}/><buttontype_="submit"className="bg-white border rounded-r-lg cursor-pointer flex border-l-0 text-lg ring-sky-400 col-span-2 items-center justify-center hover:(bg-slate-100) focus:(ring-2 bg-slate-50) "><Icon.Plus size={36} stroke={1} /></button></form>
}

使用 tailwindcss 来简化样式的书写

  • 然后引入到 App
open Reducer@react.component
let default = () => {let (state, dispatch) = reducer->React.useReducer({todos: [],nextId: 1,})let onAdd = value => {switch value {| Some(str) =>if Js.String.trim(str) != "" {str->AddTodo->dispatch}| None => ()}}<div className="bg-white rounded-md mx-auto border-1 shadow-md mt-20 pt-4 pb-8 w-[480px]"><h1 className="text-center"> {React.string("Todo List")} </h1><Addtodo onAdd /></div>
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o9T6vTOa-1655351428688)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/61f23e4dd9db4215bf6745d55d066ff7~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]

ReScript 的模块导入不用写特定的语法,只要用文件名就可以了,比如我有个模块文件名为 Foo.res ,在另外一个文件理要引入模块 Foo 里面的 name 值,因为 ReScript 默认导出所有变量,只需这么写就可以直接引入

// Foo.res
let name = "ReScript"
// Test.res
let lastName = Foo.name
  • 添加 TodoItem 组件
type todo = Reducer.todo
@react.component
let make = (~todo: todo, ~onToggle: int => unit, ~onRemove: int => unit) => {let {id, text, completed} = todo<li className="border-b flex py-4 px-6 text-3xl items-center"><inputtype_="checkbox"className="h-6 m-0 mr-4 w-6 accent-sky-600"checked={completed}onChange={_ => onToggle(id)}/><spanclassName={`-mt-1 break-words max-w-[350px] ${completed? "text-gray-500 line-through": "text-black"}`}>{React.string(text)}</span><buttonclassName="bg-transparent cursor-pointer ml-auto h-6 p-0 w-6 hover:text-rose-600"onClick={_ => onRemove(id)}><Icon.Trash size={24} /></button></li>
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t5jkEybG-1655351428689)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/de56db53e7c0409c8af5faafb3df48e1~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]

  • 添加 TodoList 组件
open Reducer@react.component
let make = (~todos: todos, ~onDispatch: action => unit) => {<><ul className="border-t list-none py-0 px-0">{todos->Belt.Array.keep(({completed}) => {switch filter {| #showAll(_) => true| #showActive(_) => !completed| #showCompleted(_) => completed}})->Belt.Array.map(todo =><TodoItemkey={Js.Int.toString(todo.id)}todoonToggle={id => id->ToggleTodo->onDispatch}onRemove={id => id->RemoveTodo->onDispatch}/>)->React.array}</ul></>
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4NJG0ZAR-1655351428689)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c6b049241d0147c1883ec906e34b0c13~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]

TodoList 加上标签切换功能

open Reducerlet filterTitles = [#showAll("All"), #showActive("Active"), #showCompleted("Completed")]@react.component
let make = (~todos: todos, ~onDispatch: action => unit) => {let (filter, setFilter) = React.useState(_ => #showActive("Active"))let allNumber = todos->Belt.Array.lengthlet activeNumber = todos->Belt.Array.keep(({completed}) => !completed)->Belt.Array.lengthlet completedNumber = allNumber - activeNumber<><div className="border-t flex mt-6 px-6 justify-end"><ul className="flex list-none my-0 px-0">{filterTitles->Belt.Array.map(item => {let #showAll(title) | #showActive(title) | #showCompleted(title) = itemlet count = switch item {| #showAll(_) => allNumber| #showActive(_) => activeNumber| #showCompleted(_) => completedNumber}let active = item == filter<likey={title}className={`${active? "bg-gray-50 border-t-sky-400": "bg-gray-200"} rounded-b-lg border border-t-2 border-gray-200 cursor-pointer flex ml-2 py-2 px-2 items-center`}onClick={_ => setFilter(_ => item)}>{React.string(title)}<span className="rounded-md bg-sky-600 text-sm text-white ml-1 px-1 pb-[0.8px]">{count->Js.Int.toString->React.string}</span></li>})->React.array}</ul></div><ul className="border-t list-none py-0 px-0">{todos->Belt.Array.keep(({completed}) => {switch filter {| #showAll(_) => true| #showActive(_) => !completed| #showCompleted(_) => completed}})->Belt.Array.map(todo =><TodoItemkey={Js.Int.toString(todo.id)}todoonToggle={id => id->ToggleTodo->onDispatch}onRemove={id => id->RemoveTodo->onDispatch}/>)->React.array}</ul></>
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WsaIpcbt-1655351428690)(https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e7bf504fc6e54e63b42815a126a0a805~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]

  • 然后把 TodoList 引入到 App 中就完成了。
<div className="bg-white rounded-md mx-auto border-1 shadow-md mt-20 pt-4 pb-8 w-[480px]"><h1 className="text-center"> {React.string("Todo List")} </h1><Addtodo onAdd /><TodoList todos={state.todos} onDispatch={dispatch} />
</div>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nrtR2m2w-1655351428690)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/71f937e00a4f450bb1480f18f75b75ea~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]

总结

ReScript 是对函数式友好的编程语言,有其独特的类型系统,不像 TypeScript 的类型是对 JavaScript 原有类型的补全。类型自动推导十分便利,不用再手动指定类型。Variant 数据结构和模式匹配极大方便了开发,减少样板代码的存在。 虽然类型推导很便利,但没有 TypeScript 那样的类型体操,遇到比较复杂的数据结构需要手动定义。

TypeScript 用腻了?来换换口味吧——使用 ReScript + React 写一个简单的Todo List相关推荐

  1. 【应用推荐】高度可定制的新一代浏览器vivaldi,chrome用久了也该换换口味了

    [应用推荐]高度可定制的新一代浏览器vivaldi,chrome用久了也该换换口味了 写在前面 初识vivaldi vivaldi 亮点功能 我的vivaldi 写在前面 仍记得在上大学之前,一直都用 ...

  2. 一个简单案例教你如何用Typescript写Vuex

    案例源代码: github.com/danielhuoo/- 前言 相信很多人都像我一样,学习使用了vuex后,想把项目改写成Typescript.但是官方教程要么晦涩难懂,要么缺少鲜活的例子.我花了 ...

  3. 换换口味,体验一下Mandriva

    上次安装上mandriva,对linux不熟悉,连安装软件都成了问题,装一个flashplayer也总是提示失败.今天又重新安装上,经过没数次的失败,终于成功安装上了,并且在线更新了系统,现在看着美观 ...

  4. 安装Jetbrains Mono字体,换换口味

    Windows10 下三步走 一.下载字体 二.安装字体(重点) 三.配置字体 Jetbrains IDEA VS Code 一.下载字体 戳这里直接下载 进入 Jetbrains Mono 字体的网 ...

  5. api响应泛型参数 swagger_你还在用丝袜哥(Swagger)?今天不如换换口味!

    今天给大家安利一款接口文档生成器--JApiDocs. Swagger想必大家都用过吧,非常方便,功能也十分强大.如果非要说Swaager有什么缺点,想必就是注解写起来比较麻烦.如果我说有一款不用写注 ...

  6. 用 TypeScript 写一个轻量级的 UI 框架之十三:Grid 表格组件(下)

    脏数据处理 Grid 的多行数据,修改后要提交到后台.如果大批量的数据一次性提交到后台恐怕不大合理.如果只是修改过那行的数据才提交过去,是比较合理的方式.这些修改的数据,我们称为"脏数据(D ...

  7. 围绕sqlite构建一个简单的Typescript ORM

    目录 背景 使用代码 兴趣点 背景 我经常使用C#, 当我与database通信时,我喜欢使用基于ORM的结构. 现在我开始使用构建应用程序react-native并希望sqlite用作存储. 但我讨 ...

  8. typescript 怎么表示当前时间减一个月_TypeScript类型元编程:实现8位数的算术运算...

    失业中在 github 闲逛,看到有人用类型实现的一个4位虚拟机,为什么是4位呢,因为 TypeScript 的类型实例化深度有限制,没法实现太大的数字计算.说到用类型实现数字计算,一大堆邱奇数就冒出 ...

  9. TypeScript + React 学习render props

    ###前言 一直想学学TypeScript,尝试尝试带类型的js怎么写,有啥优点.正好这两天有时间结合着react写写小例子. 搭建环境 不想折腾webpack来自己配ts+react的环境就用typ ...

最新文章

  1. module 'tensorflow' has no attribute 'Session'
  2. python 闭包(closure)
  3. SEGW activation check bypass via debugging
  4. php 商品规格笛卡尔积,PHP 求多个数组的笛卡尔积,适用于求商品规格组合【原创】...
  5. [笔记] 如何从不同扩展名的数字证书中提取明文信息? *.pem *.der *.crt *.cer *.key之间的区别是什么?...
  6. TFS 2010 使安装更容易,让VSS历史
  7. paip.提升效率--输入框不能粘贴的问题
  8. lingo3d_基于官方教程的分析
  9. 职称评审要满足什么条件
  10. ubuntu16.04版本系统清理和美化
  11. 计算机考研833大纲,考研833之计算机组成与系统结构提纲
  12. 什么是RPM安装???
  13. java trim 空指针_trim()空指针异常问题!
  14. Linux_进程控制(创建进程,等待进程,进程终止)
  15. 双目立体匹配_DispNet网络
  16. 思科《计算机网络》期末考试答案A卷
  17. 软考:中级软件设计师总结
  18. CDMA码分多址原理
  19. Ubuntu安装Skype
  20. 我的新博客地址https://xmmup.com

热门文章

  1. 基于web的健身俱乐部网站系统的设计与实现
  2. 【C++】八皇后问题(竖列递进)
  3. 数据结构c语言版入门教材,《数据结构(C语言版)》C语言-教材-数据结构.pdf
  4. 解读这40项IT技能都能帮你年薪超过12万美元
  5. GPS软件接收机(2)——选星
  6. 解决同花顺导出Excel文件无法读取的问题xlrd.biffh.XLRDError: Unsupported format, or corrupt file: Expected BOF record;
  7. 深度学习主题月:飞桨三大系列锦标赛奖金翻倍,快来报名!
  8. ChinaRAP中国道路(路网)风险评估系统研究与应用
  9. 苹果震撼发布首款头显,搭配强悍Mac和iOS 17等全新产品,价值25000元!
  10. fiddler4修改请求和返回数据