2020 年帮我完成大事的 Web 开发工具
我每天使用的 4 个 Git 代码片段
在 VS Code 中构建第二个大脑
一切都是状态机
使用 RamdaJS 进行函数式编程
就这样结束了!
终于……2020 年那场“垃圾桶大火”结束了。我当然有很多东西想留下来。但就像凤凰涅槃一样,我想重点介绍一些真正让我作为 Web 开发者的生活变得更美好的工具和实践🚀
2019年,当我在Web开发领域真正站稳脚跟时,我写过一篇类似的文章。现在,我要把它变成每年的传统!🥳
让我们开始吧:
- 🌳 4 个定义我的工作流程的 Git 代码片段
- 🧠我的使命是在 VS Code 中构建第二个大脑
- ⚙️发现一切都是带有 XState 的状态机
- 🐏使用 Ramda 在 JS 中进行函数式编程的乐趣
前进!
我每天使用的 4 个 Git 代码片段
今年我开始了我的第一份全职程序员工作,这意味着我在很短的时间内学到了很多新技巧。自然而然地,我开始将我每天使用的工作流程自动化。
如果你之前见过这个工作流程,请阻止我:
- 我在 JIRA 上领取了一张票
- 我需要拉取最新的主分支
- 我需要签出一个新的分支
- 我需要将该分支推送到原点并与我的团队合作并打开我的 PR
如果我们正忙着修复 bug,我一天大概会做 5 次以上。但时间紧迫的时候,很容易出现两种情况:a)从旧的“主”分支开始,或者b)在 PR 提交前羞愧地复制粘贴:
To push the current branch and set the remote as upstream, use
git push --set-upstream origin crap-not-again
你知道每次看到这个你都会有点害怕😬
为了缓解这种情况,我创建了4 个快捷方式来完成我的日常任务 💪
# Stash what I'm working on and checkout the latest master
alias gimme="git stash push -u && git checkout master && git pull -r"
# Grab the latest master and come back to the branch I was on (with stashing!)
alias yoink="gimme && git checkout - && git stash pop"
# Checkout a new branch and push it to origin (so I don't forget that set-upstream)
woosh() {
git checkout -b $1 && git push -u origin HEAD
}
# ALL TOGETHER NOW
# Stash my current WIP, checkout a new branch off the latest master, and push it to origin
alias boop="gimme && woosh"
让我们进一步了解这些命令的作用:
gimme:
此命令假设您可能尚未进入主分支。它会首先存储您正在处理的所有内容,包括带有-u
标记的“未跟踪”/新文件。然后,它会转到主分支并拉取最新版本。该-r
标记将确保“变基”到最新版本,从而避免不必要的合并冲突。yoink:
这一步会逐步完成gimme
。完成后,我们不会继续留在主分支上,而是会使用参数返回到刚才所在的分支-
。这对于获取最新更改并强制 rebase我们正在处理的分支非常方便。woosh:
这解决了我们--set-upstream
之前的问题。现在,你无需稍后再推送到原点,而是可以检出一个新分支并立即推送。没错,有些情况下你不想把本地分支放在远程,但根据我的经验,这种情况很少见。如果你HEAD
以前没见过这个参数……记住它!这是一种非常巧妙的方法,可以自动填充当前分支的名称,而无需手动输入🔥boop:
这个命令功能齐全。它会存储你当前的工作,抓取最新的主分支,并在你开始工作之前将其推送到 origin。这是我最常用的命令!
另外还要提到Jason Lengstorf(boops 之王)的命名约定😁
在 VS Code 中构建第二个大脑
这一年我都在写,写,再写。作为一名新晋全栈开发者,要学的知识实在太多了,但要找到自己一周前写的东西总是那么难!
多年来,我一直饱受笔记应用疲劳的折磨。但终于……我觉得我找到了一个近乎完美的平台,可以存放我工作和生活中的所有记录。
进入第二大脑🧠
我听说过这个概念有一段时间了,但从未意识到它其实是一种做笔记的策略。正如你所料,它的核心就是尽可能多地写作,这样你就能以笔记的形式积累相当于大脑的知识。
这个概念源自纸笔时代使用的Zettelkasten方法。它建立在一些非常基本的原则之上:
- 每条笔记都被视为一个独特的想法集合,由某种独特的 ID 标记
- 笔记应该形成一棵不断延伸、相互关联的想法/思绪的树状结构。这通过笔记之间的“链接”(指向这些唯一ID的引用)来实现,就像网络上的超链接一样!
- 假设你的 Zettelkasten 变得相当大,你可以用标签或目录索引多个笔记“树”
关于如何正确制作 Zettelkasten,有无数的建议。但总的来说,很明显,物理 Zettelkasten与 Web 的工作方式完美契合。那么,为什么不使用一堆 HTML 文件来创建一个呢?或者更好的是,使用 Markdown 文件?
使用 VS Code + Foam
我最近发现了一个名为Foam的项目,它实际上并不是一个独立的项目;它是一系列可以很好地协同工作的扩展,并提供了一些有关如何充分利用它们的有用指南。
您只需克隆一个仓库,然后见证奇迹发生!它会推荐您编辑、链接和查看笔记所需的所有扩展程序。但最终,您实际上只是在计算机上编写了一堆 Markdown 文件,并享受了一些额外的便利。
鸟瞰视角
Foam 笔记法的一个关键点值得讨论:你永远不需要按目录对笔记进行分组。我们习惯于使用文件系统来组织一切,但说实话,我们的大脑可不是这么运作的!
Foam 专注于通过链接而非文件夹层级来连接笔记。这使得定义可能在很多地方引用的笔记变得更容易。您无需查找笔记的具体目录,只需引用文件本身即可。
Foam 会通过图形可视化扩展功能,帮你发现链接中自然浮现的任何模式。它基本上就是一张你头脑中的大地图,你可以点击进去探索!
这是我最近学习 Rust 语言的挑战生成的图表。请注意,它与目录树所要求的父子关系并不完全匹配。例如,最左边的“结构体简介”被“枚举”和“Rust 所有权”引用。但你不可能让同一个文件同时位于多个目录中!这就是使用自由格式链接的妙处;任何内容都可以引用其他内容,因此它不像一棵树,而更像一个有目的的、错综复杂的鸟巢😁
我的大脑的隐喻
如果你像我一样每天都用 VS Code,那么第一次上手会非常容易。如果你的新年计划是多写博客,一定要试试看🎉
一切都是状态机
你是一台状态机。我是一台状态机。世界就是一台状态机。
...好吧,玩笑归玩笑,状态机确实适用于一切事物😆
你可能听说过XState,它是 JS 应用中“全局状态管理”的解决方案。确实如此,但状态机的概念不仅仅是一个需要学习的库,更是一种思维方式。
可视化你的状态
图表绘制是 XState 的核心功能。大多数状态管理工具都难以追踪从一个 action/state/reducer 到另一个 action/state/reducer 的实际流程。XState提供了一个专门的沙盒供您使用!
我可以想出一些疯狂的例子来展示 XState 的工作原理,但默认模板已经做得很好了:
让我们在这里分解一些重要的 XState 术语:
- 有限状态是指那些被矩形框起来的单词(例如,idle、loading 等等)。如果你像我一样熟悉 Redux,你可以把它们看作是微型 Reducer。它们各自监听着不同的 action,这可能会导致你从一个 Reducer 跳转到下一个 Reducer。
- 动作由有限状态(FETCH、RESOLVE 等)之间的灰色气泡定义。这些气泡可以执行一些有趣的操作:让你进入另一个有限状态,为整个状态机设置一些“全局”状态(参见下一条),或者触发一些在状态机之外执行操作的“副作用” 。例如,
FETCH
动作可能会触发某种 API 调用。如果该 API 调用成功返回,我们将触发该RESOLVE
动作(由“加载”状态接收)。 - 上下文是状态机中所有状态共享的全局状态。它只是一个大型 JS 对象,你可以随时为其赋值或读取。在本例中,我们有一个“重试”获取数据的次数计数,该计数会在执行
RETRY
以下操作时更新:do / assign retries
这种方法有无数好处,但对我来说最大的好处是:你可以使用任何你想要的框架来处理 XState,甚至可以根本不用框架!这让我非常放心地全心投入这个库,因为我可以随身携带它😁
为了让您熟悉这个美丽新世界,我在互联网上找到了一些值得一看的高质量演示:
- 这是一篇关于跨框架使用状态机进行 UI 建模的演讲。毫无疑问,这是该主题的最佳会议演讲。
- 这篇文章是关于构建更复杂的 ReactJS 表单的。篇幅较长,但值得一看!
- 这个是关于如何创建 Vanilla JS 拖放交互的。这个更注重 CSS,也体现了
className
我上面展示的技巧。
使用 TypeScript 的单行状态机
这一年来,我开始越来越多地使用这种模式。实际上,它根本不用任何库!简而言之,它是一种简洁的简写,可以折叠不断增长的布尔值海洋:
const [formIdle, setFormIdle] = React.useState(true);
const [formInvalid, setFormInvalid] = React.useState(true);
const [formSubmitting, setFormSubmitting] = React.useState(true);
const [formSubmitted, setFormSubmitted] = React.useState(true);
...进入单一、类型安全的状态:
type FormState = 'idle' | 'invalid' | 'submitting' | 'submitted'
const [formState, setFormState] = React.useState<FormState>("idle");
这解决了像Redux 这样的工具似乎鼓励的一个老问题:你有一堆表示不同状态的布尔值,但在给定时间只有一个布尔值应该被置“开”。确保所有布尔值都在false
它们应该出现的时间点很麻烦,所以为什么不使用一个包含两个以上状态的布尔值呢?
这种方法还有许多其他好处,比如在合适的时机翻转 CSS 类。查看这篇文章,了解一些交互式代码示例 ✨
使用 RamdaJS 进行函数式编程
在我的全职工作所需的整个技术栈中,函数式编程一直是我要学习的共同点。
TypeScript 带来的静态类型检查优势,让函数式编程社区蓬勃发展。哎呀,几乎所有范畴论书籍中的概念,都有一整套辅助库!
我对真正的数学函数式编程(FP)的了解目前还停留在皮毛。不过,我已经找到了通往这个新世界的“入门药”:我需要把一大块数据转换成另一个看起来不同的数据块,我要用 10 个函数串联起来才能完成。
RamdaJS正是 JS 实现这些复杂功能所缺少的工具包。我可以滔滔不绝地讲解他们冗长文档中的每一个小函数,但让我们先来谈谈其中最重要的亮点。
布尔运算
首先,Ramda 提供了一些合并多个数组的快捷方式。我们来考虑一下咖啡店的一些基本库存管理。在结账之前,我们需要排除所有缺货的商品。通常,我们会编写如下函数:
const orders = ["Macchiatto", "Cold brew", "Latte"]
const outOfStockItems = ["Macchiatto"]
const validOrders = orders.filter(order => !outOfStockItems.includes(order))
效果还不错……但可读性不太好。看我们一口气重写这种操作:
// subtract out all the outOfStockItems from orders
const validOrders = difference(orders, outOfStockItems)
// -> ["Cold brew", "Latte"]
我们可以从这里做各种各样的事情!例如,我们可以通过union
将多个数组放在一起来一次性过滤掉它们:
const validOrders = difference(orders, union(outOfStockItems, itemsOutForDelivery))
...或者找出我们应该使用 SQL 启发式方法包括哪些项目innerJoin
:
// basically, filter our menuItems based on which ones were ordered
const itemsOrdered = innerJoin((item, order) => item.name === order, menuItems, validOrders)
像这样的布尔运算当然不是一个独特的概念。不过,我很高兴 Ramda 将它们全部包含在内,并且还提供了一个不错的 REPL 实践环境。如果您是第一次接触布尔运算,我强烈推荐这个交互式教程🔥
批量格式化evolve
好吧,这个助手真的让我大吃一惊。我相信你在尝试将一个对象转换为另一个对象时一定遇到过这种模式:
const orders = [{
name: "Macchiatto",
options: {
roomForCream: true,
cream: {
quantity: "1tbsp"
}
}
}...]
// now, we want to loop over all of these and convert that "quantity" to an int.
orders.map(order => ({
// make sure the name sticks around
...order,
options: {
// and roomForCream
...order.options,
cream: {
// ...and finally, parse quantity to an int
quantity: parseInt(order.options.cream.quantity)
}
}
}))
还不错,但是那些点链式操作让我头晕目眩。我们还得记住每一层的所有键,以免它们消失。展开运算符帮我们简化了这个过程,但仍然很容易忘记(尤其是在没有类型检查的情况下!)。要是我们可以只修改我们关心的键,而不用嵌套点就好了。
这正是evolve
! 🎉
orders.map(
evolve({
// ignore the properties we *don't* want to change, like the name
options: {
// ignore roomForCream too
cream: {
// and just put a reference to the parseInt function as-is. Don't even call it!
quantity: parseInt
}
}
})
)
这种方法确实有很多好处。我们不仅可以省略不想更改的键,还能轻松修改深层嵌套的字段!你还会注意到,我们可以直接将值传入 map,而不必像以前那样evolve
传入值(例如)。这是因为所有 Ramda 函数都是柯里化的,你可以从这个 Stack Overflow 答案中了解更多信息😁 剧透:这是一个很棒的功能。order
map(order => evolve(order))
就这样结束了!
我知道2020年对很多人来说都很难熬。如果你正在为家庭问题苦恼,在家照顾孩子,找新工作,或者宅在家里发疯……好吧,至少你熬到了今天🙂
如果您还有什么其他事情让您作为开发者或个人的生活变得更美好,请在下方评论区留言。我们即将迎来崭新的 2021 年,期待听到一些新的观点!
哦,如果您错过了,我推出了“网络魔法”时事通讯来探索更多这样的知识!
这玩意儿探讨的是Web 开发的“首要原则”。换句话说,究竟是哪些糟糕的浏览器 API、扭曲的 CSS 规则以及半无障碍的 HTML 支撑着我们所有的 Web 项目?如果你想要超越框架,那么亲爱的 Web 魔法师,这玩意儿就是为你准备的🔮
赶紧在这里订阅吧!我保证永远教书,绝不会发垃圾信息❤️
文章来源:https://dev.to/bholmesdev/the-web-dev-tools-that-helped-me-get-s-done-in-2020-gpl