周刊第1期:搬新家了

我为什么要写周刊

前几天特地写了篇文章来分享一下我为什么要办这个周刊:《我为什么要写周刊》。

我搬新家了

在去年《2021 年度总结》有提到,我曾有过一段开小电动车上班的时光,通勤时间极短,幸福感倍高,但由于公司地址搬迁,就再也没有享受过这样的日子了,即便后来跳槽到新公司,通勤时间缩短了一半,但也要 40 分钟左右的通勤时间,所以,在租房合同到期后,我决定搬到公司附近。

但搬家真的好累,而且广州这几天连续不断地下雨,心情也变得烦躁起来,搬家过程不太顺利,期间还遗漏了物品在楼下没带上车,幸好楼下的邻居平时关系比较好,愿意帮我保管,第二天又跑回去拿。

但从明天开始就可以享受极短通勤距离的好处,想想还是蛮开心的。

分享文章

一些我本周阅读过的好文章、以及我的总结和启发;非常建议你阅读原文,而不是只看这些摘要,毕竟一千个读者就有一千个哈姆雷特,而且我的理解可能是错误的。

我对 Svelte 的看法

原文地址:《我对 Svelte 的看法》 | Randy’s Blog

简单来说 Svelte 是一个在编译时实现了 Reactivity (反应式) 的框架。

如何不用 defineProperty 实现 Reactivity:

  1. 只要在每次赋值的时候,手动执行 update 方法
  2. Svelte 在编译阶段自动帮我们做了这件事
  3. Svelte 使用了一个 label 语法($:),实现类似于 Vue 的 computed 功能。

Svelte 的跨组件通讯:

  • 通过在变量名前面加一个 $ 实现自动订阅
    • 不是黑魔法,只是针对 $ 开头的变量自动转换成 subscribe

Svelte 所有功能都在编译阶段完成,并且可以写更少的代码,它相对于 React 和 Vue 更简单,不需要学太多额外的 API 和新语法。

目前的适用场景:

  • 适用做来做活动页。
我的启发

学习一个框架或者一个语言,并不一定非要把它用到生产上才算真的有用。更多地是因为想看看在面对同一个问题的时候,不同的人解决问题的思路是怎样的,从而帮助我们提高自己的视野,这才是学习框架和语言真正的魅力。

Svelte 的异步更新实现原理

原文地址:《Svelte 的异步更新实现原理》 | Randy’s Blog

如何异步更新:将所有导致 UI 更新的操作统一放到一个微任务里执行。

Svelte 的实际做法:

  1. 一个组件会被编译成一个 fragment
  2. 更新操作通过 $$invalidate 包裹
  3. 触发 schedule_update() ,通知框架需要被更新,框架会维护一个 dirty_components 数组。
  4. 在微任务更新时会统一遍历 dirty_components 数组里的任务,触发组件的更新方法。

开发模式 “Development Mode” 是如何工作的?

原文地址:《开发模式 “Development Mode” 是如何工作的?》 | Dan Abramov

process.env.NODE_ENV 实际上是一个常量,在构建时会被替换成一个字符串,所以判断条件最终是:

1
2
3
4
5
6
7
8
9
10
11
12
13
// In development:
if ('development' !== 'production') { // true
doSomethingDev(); // 👈
} else {
doSomethingProd();
}

// In production:
if ('production' !== 'production') { // false
doSomethingDev();
} else {
doSomethingProd(); // 👈
}

以上判断条件会被「死码消除」给优化,不被执行的代码将被移除。

但是,如果写成这样,则不会奏效:

1
2
3
4
let mode = 'production';
if (mode !== 'production') {
// 🔴 not guaranteed to be eliminated
}

因为 JavaScript 并没有智能到这种程度。

我的启发

以前看 Vue 源码的时候发现很多文件都会重复好几次这样的代码:

1
2
3
4
5
if (process.env.NODE_ENV !== 'production') {
if (something) {
console.warn('something error message');
}
}

非常好奇为什么不重构成这样:

1
2
3
4
5
6
7
8
9
10
11
// 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

  1. 确保 flex 布局能够换行
  2. 确保文字过长时的间距以及换行处理
  3. 防止图片被拉伸或压缩
  4. 弹窗锁定滚动
  5. CSS 变量回退
  6. 尽量使用 min-height 固定宽度和高度
  7. 确保重置 background-repeat
  8. 使用 gap 设置 flex 布局间距
  9. 给图片设置一个 background-color,以确保图片上方的文字能够正常显示
  10. 使用 overflow: auto
  11. 图片 object-fit: cover
  12. 分组选择器只要有一个无效,则所有选择器不生效
我的启发

了解常用的防御性 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 学起来吧!


作者 : 4Ark
来源 : https://4ark.me
著作权归作者所有,转载请联系作者获得授权。

🥳 加载 Disqus 评论