我为什么要写周刊
前几天特地写了篇文章来分享一下我为什么要办这个周刊:《我为什么要写周刊》。
我搬新家了
在去年《2021 年度总结》有提到,我曾有过一段开小电动车上班的时光,通勤时间极短,幸福感倍高,但由于公司地址搬迁,就再也没有享受过这样的日子了,即便后来跳槽到新公司,通勤时间缩短了一半,但也要 40 分钟左右的通勤时间,所以,在租房合同到期后,我决定搬到公司附近。
但搬家真的好累,而且广州这几天连续不断地下雨,心情也变得烦躁起来,搬家过程不太顺利,期间还遗漏了物品在楼下没带上车,幸好楼下的邻居平时关系比较好,愿意帮我保管,第二天又跑回去拿。
但从明天开始就可以享受极短通勤距离的好处,想想还是蛮开心的。
分享文章
一些我本周阅读过的好文章、以及我的总结和启发;非常建议你阅读原文,而不是只看这些摘要,毕竟一千个读者就有一千个哈姆雷特,而且我的理解可能是错误的。
我对 Svelte 的看法
原文地址:《我对 Svelte 的看法》 | Randy’s Blog
简单来说 Svelte 是一个在编译时实现了 Reactivity (反应式) 的框架。
如何不用 defineProperty
实现 Reactivity:
- 只要在每次赋值的时候,手动执行
update
方法 - Svelte 在编译阶段自动帮我们做了这件事
- Svelte 使用了一个
label
语法($:),实现类似于 Vue 的computed
功能。
Svelte 的跨组件通讯:
- 通过在变量名前面加一个 $ 实现自动订阅
- 不是黑魔法,只是针对 $ 开头的变量自动转换成
subscribe
- 不是黑魔法,只是针对 $ 开头的变量自动转换成
Svelte 所有功能都在编译阶段完成,并且可以写更少的代码,它相对于 React 和 Vue 更简单,不需要学太多额外的 API 和新语法。
目前的适用场景:
- 适用做来做活动页。
我的启发
学习一个框架或者一个语言,并不一定非要把它用到生产上才算真的有用。更多地是因为想看看在面对同一个问题的时候,不同的人解决问题的思路是怎样的,从而帮助我们提高自己的视野,这才是学习框架和语言真正的魅力。
Svelte 的异步更新实现原理
原文地址:《Svelte 的异步更新实现原理》 | Randy’s Blog
如何异步更新:将所有导致 UI 更新的操作统一放到一个微任务里执行。
Svelte 的实际做法:
- 一个组件会被编译成一个 fragment
- 更新操作通过
$$invalidate
包裹 - 触发
schedule_update()
,通知框架需要被更新,框架会维护一个dirty_components
数组。 - 在微任务更新时会统一遍历
dirty_components
数组里的任务,触发组件的更新方法。
开发模式 “Development Mode” 是如何工作的?
原文地址:《开发模式 “Development Mode” 是如何工作的?》 | Dan Abramov
process.env.NODE_ENV
实际上是一个常量,在构建时会被替换成一个字符串,所以判断条件最终是:
// In development:
if ('development' !== 'production') { // true
doSomethingDev(); // 👈
} else {
doSomethingProd();
}
// In production:
if ('production' !== 'production') { // false
doSomethingDev();
} else {
doSomethingProd(); // 👈
}
以上判断条件会被「死码消除」给优化,不被执行的代码将被移除。
但是,如果写成这样,则不会奏效:
let mode = 'production';
if (mode !== 'production') {
// 🔴 not guaranteed to be eliminated
}
因为 JavaScript 并没有智能到这种程度。
我的启发
以前看 Vue 源码的时候发现很多文件都会重复好几次这样的代码:
if (process.env.NODE_ENV !== "production") {
if (something) {
console.warn("something error message");
}
}
非常好奇为什么不重构成这样:
// utils.js
const warn = (condition, message) => {
if (process.env.NODE_ENV !== "production") {
if (condition) {
console.warn(message);
}
}
};
// something.js
warn(xxx, "something error message");
现在知道为什么了,因为前者被「死码消除」后,所有相关代码都会被移除,
而后者,相当于调用了一个空的 warn
方法。
但没想到在 Vue3 中已经这么做了:defaultOnWarn。
延伸阅读:高效实现框架和 JS 库瘦身
通俗易懂的代数效应
原文地址:《通俗易懂的代数效应》 | Dan Abramov
代数效应是什么:
- 类似于 try…catch 语法,无论我们在多少层里面 throw 一个错误,外层的 catch 都能捕获到。
- 如果内层的 throw 还可以接受从 catch 发送的数据,这就是代数效应。
- 使用 async、await 的缺点是我们内部定义了 async、则会影响该函数底部所有的调用者。
React 中的代数效应:
- JS 并没有一个类似于 try…handle 的语法,但仅在 React 内部可以通过其它方式实现类似的效果。
- 典型的场景是:某个组件的渲染是异步的,需要等数据请求返回后再回过来继续渲染。
防御性 CSS
原文地址:《防御性 CSS》 | ishadeed
- 确保 flex 布局能够换行
- 确保文字过长时的间距以及换行处理
- 防止图片被拉伸或压缩
- 弹窗锁定滚动
- CSS 变量回退
- 尽量使用 min-height 固定宽度和高度
- 确保重置
background-repeat
- 使用
gap
设置 flex 布局间距 - 给图片设置一个 background-color,以确保图片上方的文字能够正常显示
- 使用
overflow: auto
- 图片
object-fit: cover
- 分组选择器只要有一个无效,则所有选择器不生效
我的启发
了解常用的防御性 CSS,可以避免很多开发中没有意识到的问题。
SSR、SSG、ISR、DPR 有什么区别?
原文地址:《SSR、SSG、ISR、DPR 有什么区别?》 | 前端里
- CSR(Client Side Rendering): 客户端渲染
- 缺点:SPA 应用不利于 SEO
- SSR(Server Side Rendering): 服务端渲染
- 优点:利于 SEO
- 缺点:需要更多的服务器计算资源和运维成本
- SSG(Static Site Generation): 静态网站生成
- 常年不变的直接生成为静态页面,页面中的动态内容使用 CSR
- 缺点:对于网页数量极多的页面,生成一次需要花费很长时间
- ISR(Incremental Site Rendering): 增量式网站渲染
- 关键性页面预渲染,缓存至 CDN;非关键性页面先显示 fallback,再使用 CSR 渲染,并且缓存至 CDN
- 缺点:需要先显示 fallback 内容,用户体验不好
- DPR(Distributed Persistent Rendering): 分布式持续渲染
- 基于 ISR 的基础上去除 fallback,并且使用「按需构建器」来响应未渲染的页面
不优雅的 React Hooks
原文地址:《不优雅的 React Hooks》 | 蚂蚁 RichLab 前端团队
「奇怪的」规矩:
- 命名:react hooks 要求 hooks 命名以 use 开头,作者觉得破坏了语义,应该以 _ 或者 $ 开头
- 调用时序:react hooks 区分不同的 state 是通过 hooks 的调用顺序,在内部维护一个链表,这个做法是相对简单粗暴的,所以这就是为什么 hooks 不能条件分支中使用,作者认为这种要求完全需要依赖开发者的经验或者 lint 工具,并不符合直觉。
- useRef 的「排除万难」:useRef 仅在组件 mount 阶段初始化,后面 update 时引用同一个变量,但是作者这样多少有点违反 react hooks 的设计初衷,也即 useRef 并不函数式。
有缺陷的生命周期:
- 构造时:Class Component 和 Function Component 之间存在一个很大的区别,那就是后者每次 re-render 时都在重新调用自身,所以它并没有 constructor 来做某些仅执行一次的操作。
- 设计混乱的 useEffect:它实际上只监听能够触发 re-render 的变量,也就是 state,但是它接受的参数 deps 又不做任何限制,很难不让人认为是设计缺陷。
useCallback:
- 性能问题?无限套娃✓:使用 useCallback 是为了解决每次渲染都会创建一个新的回调函数,从而导致重新渲染的问题,但是使用 useCallback 也会引发一个更大的问题,那就是当 useCallback 之前存在依赖关系时,它们的引用维护也变得复杂。调用某个函数时要小心翼翼,你需要考虑它有没有引用过时的问题,如有遗漏又没有将其加入依赖数组,就会产生 Bug。
小结:
- 本文没有鼓吹 Class Component 拒绝使用 React Hooks 的意思,反而是希望通过细致地比对二者,从而更深入理解 Hooks。
- React Hooks 的各种奇怪之处,也正是潜在症结之所在。
- 在 Hooks 之前,Function Component 都是 Stateless 的,小巧、可靠但功能有限。Hooks 为 Function Component 赋予了 State 能力并提供了生命周期,使 Function Component 的大规模使用成为了可能。
- Hooks 的「优雅」来自向函数式的致敬,但 useRef 的滥用让 Hooks 离「优雅」相去甚远。
- 大规模实践 React Hooks 仍然有诸多问题,无论是从语义理解抑或是封装的必要性。
- 创新不易,期待 React 官方之后会有更好的设计吧。
DevTools 实现原理
原文地址:《DevTools 实现原理》 | vivo 互联网浏览器内核团队
DevTools 架构:
- Chrome:TCP + CDP
- CDP(Chrome DevTools Protocol)本质上是一个 JSON 协议,目前使用 Puppeteer 实现
- 使用 WebSocket
- Android:利用 ADB forward 端口转发能力连接 PC 和 Android 的网络访问
内核实现
- Server 层,用于接收外部网络发过来的操作请求。
- Agent 层,对于 Server 层发过来的请求,进行拆解,根据操作的类型不同,再分发给不同的 Agent 来处理。
- Session 层,Session 是对不同的业务模块进行了一层抽象。过了 Session 层后,将会进入不同的业务模块,可以到达 V8,Blink 等。
- 业务层,就是具体的功能模块,比如 V8 模块,主要负责 JavaScript 的调试相关能力的支撑。
有趣的链接
-
Tree 树形目录可视化生成器:我们都知道
tree
这个命令,可以打印输出现有目录的树形结构,但现实中还存在一种情况,那就是需要输出并不存在的目录结构,这种需求在写开发设计文档时尤为常见,所以除了构造一个临时的目录,还可以使用这个工具直接生成树形目录结构。 -
Z-Library:世界上最大的数字图书馆,在这里可以找到大量的电子书籍,格式包括:pdf、epub、mobi 等,对于我这种不爱看实体书的人来说,简直是神器。但需要注意每天一个 ip 会限制下载数量,未注册用户是 5 次,注册用户是 10 次,当然也可以通过捐赠来提高下载次数的限制。
-
毕方铺:网盘搜索神器,个人认为是所有网盘搜索神器中最好的一个,所有资源都为用户自愿分享,下载需要耗费金币,你也可以上传你想要的分享的资源,别人购买你就能获取金币,当然到达一定金币后就可以提现。
-
Feed Creator:制作 RSS 源的工具,包括网页生成 RSS、合并多个 RSS 等功能;相信有些同学知道 RSSHub 这个开源项目,显然这个项目更加强大,但并不是所有场景都需要用到 RSSHub,这时候就可以使用 Feed Creator。分享一下我个人会在什么场景下使用 Feed Creator 而不是 RSSHub:对于某些访问需要科学上网的网站,我们可以直接通过它生成一个国内可以直接访问的 RSS 源。
-
Microsoft 的 Rust 入门教程:要说这两年前端最新的技术莫过于 Rust 了,大量的前端基础设施都通过 Rust 重写了一遍,哪怕笔者这种学不动的人也需要学一些 Rust 的知识,但无奈一直没有找到满意的 Rust 教程,前段时间听闻微软出了一个 Rust 教程,第一次打开后就直接跟着学了两个星期,根本停不下来,真心不错。xdm,Rust 学起来吧!