Skip to main content

· 3 min read
加菲猫

📒 开发小技巧总结

如何优雅删除数组中的元素:

const [imageList, setImageList] = useState([]);

const handleRemoveImage = (idx) => {
// 第一种:mutable 方式
// 由于 `splice` 方法直接修改原数组,所以数组指针实际上没有变化
// 当 setState 的时候,React 内部使用 `Object.is` 严格相等比较
// 由于指针没有改变,不会触发 React 调度更新
// 这里需要手动浅拷贝,创建一个新数组
imageList.splice(idx, 1);
setImageList([...imageList]);

// 第二种:immutable 方式
// 用 `filter` 返回一个新数组,简单直接
setImageList(l => l.filter((_, index) => index !== idx));
}

📒 如何优雅地编写一个高逼格的JS插件

📒 React 渲染的未来

📒 不破不立 —— 挥别 less-loader,Ant Design 5.0 Alpha 发布

📒 Vue3组件库打包指南,一次生成esm、esm-bundle、commonjs、umd四种格式

📒 Three.js 进阶之旅:模型光源结合生成明暗变化的创意页面-光与影之诗 💡

📒 Three.js 进阶之旅:基础入门(下)

📒 Three.js 进阶之旅:基础入门(上)

📒 使用 Vite 插件自动化实现骨架屏

📒 刚插上网线,电脑怎么知道自己的IP是什么

⭐️ Redis 实现分布式锁的 7 种方案

📒 Koa的洋葱中间件,Redux的中间件,Axios的拦截器让你迷惑吗?实现一个精简版的就彻底搞懂了

📒 学习 Babel 插件,把 Vue2 语法自动转成 Vue3!

📒 如何编写神奇的「插件机制」,优化基于 Antd Table 封装表格的混乱代码

📒 从龟速 11s 到闪电 1s,详解前端性能优化之首屏加载

📒 前端框架:性能与灵活性的取舍

⭐️ 【面试高频题】难度 1.5/5,多解法经典面试题

📒 React 中常见的 8 个错误,如何避免

⭐️ 在撸 Vue 的 ⌘+K 唤起菜单库时,我学到了很多

📒 Go 实战技巧:避免内存分配的小技巧

· 9 min read
加菲猫

📒 看了 web.dev 的 631 篇博客,我总结了这些内容

📒 Go开源库、大项目的公共包,是这么用建造者模式的

📒 【第2747期】Islands Architecture(上)

📒 Go语言中常见100问题-#13 Creating utility packages

📒 快速在你的vue/react应用中实现ssr(服务端渲染)

📒 探究 Go 源码中 panic & recover 有哪些坑

📒 抖音二面:为什么模块循环依赖不会死循环?CommonJS和ES Module的处理不同

📒 Go语言中常见100问题-#12 Project misorganization

📒 Go 的 IO 流怎么并发?小技巧分享

📒 构建 webpack5 知识体系【近万字总结】

📒 如何实现卡片滚动效果

实现一个横向卡片滚动的效果,有两个需求:

  • 需要有滚动条,这样可以支持有触摸板的设备(即不能通过 overflow: hidden; 加上 transform: translateX(); 或者相对定位偏移实现)
  • 还要提供左右切换的按钮,这样便于鼠标操作,同时点击需要过渡动画

根据第一点就可以确定,滚动的实现是父容器设置 overflow-x: auto; 实现的。那怎么实现第二个需求呢?

观察一下 ahooks 的 useInfiniteScroll 源码,控制滚动的核心就是 scrollTopscrollHeightclientHeight 这几个参数:

packages/hooks/src/useInfiniteScroll/index.tsx:81
// 滚动触发的回调函数
const scrollMethod = () => {
const el = getTargetElement(target);
if (!el) {
return;
}

const scrollTop = getScrollTop(el); // 滚动的距离,该参数值可以动态修改
const scrollHeight = getScrollHeight(el); // 滚动内容实际的高度,该参数值只读
const clientHeight = getClientHeight(el); // 外部滚动容器的高度,该参数值只读

if (scrollHeight - scrollTop <= clientHeight + threshold) {
loadMore();
}
};

// 绑定滚动事件监听器
useEventListener(
'scroll',
() => {
if (loading || loadingMore) {
return;
}
scrollMethod();
},
{ target },
);

https://github.com/alibaba/hooks/blob/master/packages/hooks/src/useInfiniteScroll/index.tsx

那么在卡片滚动的需求中,对应的参数值为 scrollLeftscrollWidthclientWidth。只要 scrollLeft + clientWidth < scrollWidth,就可以继续向右滚动:

import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useMemorizedFn } from "ahooks";

export const useCarouselScroll = <T extends HTMLElement>({
target,
customWidth,
}: {
target: React.MutableRefObject<T>;
customWidth?: number;
}) => {
const [enablePrev, setEnablePrev] = useState(false);
const [enableNext, setEnableNext] = useState(false);

const { scrollLeft, clientWidth, scrollWidth } = target.current;
const threshold = customWidth || clientWidth; // 一次滚动的距离

useEffect(() => {
setEnablePrev(scrollLeft > 0);
setEnableNext(scrollLeft + clientWidth < scrollWidth);
}, []);

const prev = () => {
if (enablePrev) {
if (threshold < scrollLeft) {
// 可以滚动一屏的距离
target.current.scrollLeft -= threshold;
} else {
// 滚动不足一屏距离
target.current.scrollLeft -= scrollLeft;
setEnablePrev(false);
}

setEnableNext(true);
}
};

const next = () => {
if (enableNext) {
if (scrollLeft + clientWidth + threshold < scrollWidth) {
// 可以滚动一屏的距离
target.current.scrollLeft += threshold;
} else {
// 滚动不足一屏距离
target.current.scrollLeft += (scrollWidth - scrollLeft - clientWidth);
setEnableNext(false);
}

setEnablePrev(true);
}
};

return {
enablePrev,
enableNext,
prev: useMemorizedFn(prev),
next: useMemorizedFn(next),
};
};

过渡动画的实现就很简单了,一行 CSS 代码搞定:

scroll-behavior: smooth;

📒 Next.js prefetch 策略

很多全栈框架,例如 Next.js 都会做 prefetch 预加载 chunk。Next.js 提供了一个 <Link /> 组件,可以实现 client-side route transitions,同时这个 <Link /> 还具有 prefetch 功能:

Any <Link /> that is in the viewport (initially or through scroll) will be preloaded. Prefetch can be disabled by passing prefetch={false}. When prefetch is set to false, prefetching will still occur on hover. Pages using Static Generation will preload JSON files with the data for faster page transitions. Prefetching is only enabled in production.

参考一下 <Link /> 源码实现:

https://github.com/vercel/next.js/blob/canary/packages/next/client/link.tsx#L205

📒 【一库】yalc: 可能是最好的前端link调试方案(已经非常谦虚了)

📒 使用antv/G2生态半年有感

📒 Go:符号表是什么?如何使用?

📒 Dracula UI

刚开源,特点如下。

1、为黑暗模式而建。大多数模板都是使用浅色的,后来才改成深色的。深色主题不应该是一个事后的想法,它们应该是一个首要任务。

2、设计师友好。通过使用一个高度可配置的设计系统,加快原型设计阶段。通过利用精心制作的Figma文件,轻松进行合作。

3、友好的开发者体验。不用担心类名,只需使用 Visual Studio 的代码片段。你可以利用自动完成的优势,也可以从你的代码编辑器中直接访问整个文档。

https://ui.draculatheme.com/

📒 React 18 快在哪

主要是 3 点。

1、由于 React 18 有 Automatic Batching,多个 setState 只会触发一次渲染,而在 18 之前,setState 几次就会渲染几次 2、React 18 引入了新的服务器渲染架构,会带来显著的性能提升,请检查你的框架是否支持 3、React 18 引入了用于把状态更新标记为可终端的 startTransition,虽然不能让页面变快,但如果用户在可中断的状态更新中点击,会让你的应用更具有响应性

https://www.reddit.com/r/reactjs/comments/xmr9tg/comment/ippsuin/

📒 再见 useEvent

useEvent 原计划解两个问题,1)渲染优化,2)useEffect 重新触发问题。但是发现没办法一下子做两件事。于是 useEvent RFC 废弃,这两个问题会拆开了来解。渲染优化问题倾向用 React Forgot 解,useEffect 重新触发问题会通过另一个专门解此问题的 RFC 来解,命名上应该不再会用 useEvent。

https://github.com/reactjs/rfcs/pull/220#issuecomment-1259938816

📒 Umi x Valtio

Umi 在 4.0.23 中加入了 Valtio 插件。这是在大量调研之后,基于中台项目的场景做的决定,使用 Valtio 作为下一代的数据流。

为啥是 valtio?

valtio 的特点是外部多 Store + 基于 Proxy。1)个人用不惯 jotai 和 recoil 这种 react 内部原子化的数据流方案,感觉和被 redux 培养起来的心智模型有冲突,所以会更倾向于外部 Store 一些,2)由于场景是中后台,对于兼容性的容忍度比较高,比如不用兼容 IE11,所以完全可以用基于 Proxy 的数据流方案,这类数据流方案在更新数据和读取数据时都更简单。

为啥不是 zustand?

1、zustand 和 valtio 是同一个作者写的,功能覆盖上其实比较类似,最大的区别是 valtio 基于 proxy 而 zustand 不是。个人有几个方面的考虑,1)更新数据,2)读取数据,3)类型提示。

2、更新数据的方式更符合人性,比如可以直接 state.todos['321'].completed = true,而不用 setState(todos => ({ …todos, 321: { …todos['321'], completed: true } }))。当然,这一点非 proxy 方案可基于 immer 实现和 proxy 方案类似的操作。

3、读取数据默认高性能,无需 selector。非 proxy 方案比如 react-redux 和 zustand 为了性能优化,避免不必要的 re-render,通常会通过 selector 选择 store 的一部分,这会带来不必要的心智负担。基于 Proxy 的方案是响应式的,无需 selector,默认高性能。

4、类型提示的差异主要在扩展上。valtio 用的是组合式,zustand 用的是 middleware。没具体试过 zustand 的 middleware,但个人理解,理论上 middleware 的类型提示没有 valtio 友好。比如 valtio 的 proxyWithHistory 会把数据结构改成 { value, history, redo, undo, … } 这种,在类型提示上可以完美衔接。

https://umijs.org/docs/max/valtio

· 7 min read
加菲猫

📒 你写 Go 代码写注释吗?谈谈 Go 代码注释问题

📒 程序员新人频繁使用count(*),被组长批评后怒怼:性能并不拉垮!

📒 【第2745期】React 可组合 API 的设计原则

📒 推荐12个值得学习的TypeScript宝库!

📒 【第2744期】更好的 React SSR

📒 Java19 正式 GA!虚拟线程性能炸裂!!

📒 [翻译]Go 运行时的未来 Go Runtime: 4 years later

📒 我认为 Go 的成功归功于这 5 个方面

⭐️ 给想转Go或者Go进阶同学的一些建议

📒 【第2742期】Remesh 介绍:用以开发大型复杂 Web App 的 DDD 框架

📒 Monorepo Handbook 新鲜出炉

TurboRepo 团队近日发布了 Monorepo 手册,包含关于 Monorepo 你需要知道的一切,并提供了详尽的配置示例,内容如下:

  • 什么是 Monorepo?
  • 安装包 (npm、pnpm、Yarn 1、Yarn >=2)
  • Workspaces 工作区
  • 迁移到 Monorepo
  • 任务编排
  • 构建
  • Docker 部署
  • 共享代码
  • Lint
  • 测试
  • 发布 (Changesets)
  • @manypkg/cli 处理包版本依赖

https://turborepo.org/docs/handbook/what-is-a-monorepo

📒 前端食堂技术周刊第 54 期:TS 4.9 Beta、Monorepo Handbook、第 92 次 TC39 会议、将 StoryBook Stories

📒 HTTPS 就一定安全?我不信

从客户端的角度看,其实并不知道网络中存在中间人服务器这个角色。

那么中间人就可以解开浏览器发起的 HTTPS 请求里的数据,也可以解开服务端响应给浏览器的 HTTPS 响应数据。相当于,中间人能够 “偷看” 浏览器与服务端之间的 HTTPS 请求和响应的数据。

但是要发生这种场景是有前提的,前提是用户点击接受了中间人服务器的证书

中间人服务器与客户端在 TLS 握手过程中,实际上发送了自己伪造的证书给浏览器,而这个伪造的证书是能被浏览器(客户端)识别出是非法的,于是就会提醒用户该证书存在问题。

抓包工具 之所以可以明文看到 HTTPS 数据,工作原理与中间人一致的。

对于 HTTPS 连接来说,中间人要满足以下两点,才能实现真正的明文代理:

  • 中间人,作为客户端与真实服务端建立连接这一步不会有问题,因为服务端不会校验客户端的身份;
  • 中间人,作为服务端与真实客户端建立连接,这里会有客户端信任服务端的问题,也就是服务端必须有对应域名的私钥;

中间人要拿到私钥只能通过如下方式:

  • 去网站服务端拿到私钥;
  • 去CA处拿域名签发私钥;
  • 自己签发证书,且被浏览器信任;

不用解释,抓包工具只能使用第三种方式取得中间人的身份。

使用抓包工具进行 HTTPS 抓包的时候,需要在客户端安装根证书,这里实际上起认证中心(CA)的作用。

我们要保证自己电脑的安全,不要被病毒乘虚而入,而且也不要点击任何证书非法的网站,这样 HTTPS 数据就不会被中间人截取到了。

当然,我们还可以通过 HTTPS 双向认证来避免这种问题。

一般我们的 HTTPS 是单向认证,客户端只会验证了服务端的身份,但是服务端并不会验证客户端的身份。

如果用了双向认证方式,不仅客户端会验证服务端的身份,而且服务端也会验证客户端的身份。

HTTPS 就一定安全?我不信

📒 tauri 快速上手

快速上手 3 命令。

$ npm create tauri-app
$ npm run tauri dev
$ npm run tauri build

https://blog.logrocket.com/tauri-electron-comparison-migration-guide/

📒 新一波 JavaScript 框架

作者从 Web 之初、CGI、PHP 开始讲起,讲 JavaScript 历史画卷一幅幅展开,既有 Ajax、jQuery、Backbone、YUI 等一代代的 JavaScript 库前辈,又有 Astro、Fresh、Remix 等新一波的 JavaScript 框架。推荐阅读,从中可以了解为什么会有这些库和框架,以及他们都是为了解什么而生的?

所以,既然每个框架都会被历史所代替,前端开发者应该如何保持竞争力?

https://frontendmastery.com/posts/the-new-wave-of-javascript-web-frameworks/

📒 TypeScript 4.9 satisfies

satisfies 是 TypeScript 4.9 新引入的操作符,用于只做校验但不改变表达式的类型结果。通常用于 Object 声明,因为既需要保留每个属性的类型,又需要做 key 值校验。

比如,

type Colors = 'red' | 'green' | 'blue';
const foo = {
red: [1],
green: true,
blue: 'ok',
black: {},
//~~~~~~~~~~ 既校验了 key,要求满足 Colors,这里的 black 会抛错
} satisfies Record<Colors, unknown>;

// 又让每个属性拥有完整类型
foo.red.at(0);
foo.blue.startsWith('o');

https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/#the-satisfies-operator

📒 Go语言中常见100问题-#11 Not using the functional options pattern

📒 Go语言爱好者周刊:第 161 期

· 8 min read
加菲猫

📒 前端也要懂算法,不会算法也能微调一个 NLP 预训练模型

📒 Go 语言官方依赖注入工具 Wire 使用指北

📒 超干货!彻底搞懂Golang内存管理和垃圾回收

📒 好用的map-struct转换库 mergo

⭐️ 分享三个阅读 Go 源码的窍门

⭐️ 聊聊分布式一致性!

📒 深入浅出带你走进Redis!

📒 工作中常见的 6 种设计模式,你用过几种

📒 基于 Next.js 和 tailwind.css 搭建博客

https://tailwindcss.com/docs/typography-plugin#adding-custom-color-themes

使用 Next.js 和掘金 API 打造个性博客

⭐️ 如何保障 MySQL 和 Redis 的数据一致性

📒 electron-vite:新一代 Electron 开发利器,带你轻松玩转 Electron

📒 create-vite 原理揭秘

📒 字节二面:Redis 的大 Key 对持久化有什么影响

📒 美团三面:一直追问我, MySQL 幻读被彻底解决了吗

⭐️ Go学设计模式--原型模式的考查点和使用推荐

📒 使用viper实现yaml配置文件的合并

📒 「Go工具箱」一个简单、易用、安全的类型转换工具

📒 探索组件在线预览和调试

📒 Golang 面试相关

LeetCode 刷题指南

https://www.yuque.com/go-interview/set/xq5788

面试题库收集

https://www.yuque.com/go-interview/set/interview-index

📒 已入字节的大佬各厂三年Go面经

📒 掌握了这一招,Go的多版本管理不用愁

📒 react-spring 如何用旁路设计赋能前端交互

📒 解读 State of CSS 2022,让你早点下班的新特性

📒 从useEffect看React、Vue设计理念的不同

📒 前端食堂技术周刊第 53 期:React Router 6.4、VS Code August 2022、2022 Google 谷歌开发者大会、Meta 开源

📒 语雀桌面端技术架构实践

📒 package.json 配置完全解读

📒 函数组合

函数组合有很多场景,这篇文章介绍了如何把他用在 React 项目中,换一种代码组织方式,让代码更简洁、优雅和可扩展。

假如你有一个项目,项目里有这样的需求,1)检查用户登录状态,2)特性检测按需渲染,3)埋点日志需求,4)layout 渲染。你可能会这么写。

const App = () => {
useEffect(() => {
if (!user.isLogin) signIn()
}, []);

useEffect(() => {
log({ pageName, uid })
}, []);

return user.isAdmin
? <AdminComponent />
: <>{ features.includes('foo') && <Foo /> }<Conent /></>;
}

这里的 问题是,如果有多个组件都需要类似的逻辑,你可能就要复制粘贴了。解是用 Provider + HOC + Composition + Currying。

const App = withProviders({ showFooter: false })(() => {
return <Content />;
});

withProviders 如下。

const withProviders = (options) =>
compose(
withUser,
withFeatures,
withLogger,
withLayout(options)
);

withLogger 如下。

const withLogger = (WrappedComponent) => {
return (props) => {
useEffect(() => { log({ pageName, uid }) }, []);
return <WrappedComponent {...props} />;
};
}

https://medium.com/javascript-scene/why-every-react-developer-should-learn-function-composition-23f41d4db3b1

📒 初级程序员 vs. 高级程序员

要分别初级程序员和高级程序员,其中一点是看他们关注的是「软件」还是「系统」。

初级工程师关心编写软件的问题。 他们重视代码质量,采用最佳实践,努力采用最先进的技术。他们在学习新技术方面投入了大量时间。对他们来说,最终的目标是创建优雅的、可执行的、可维护的软件。

高级工程师关心的是建立系统。 对他们来说,创建软件只是其中的一个步骤。首先,他们会质疑这个软件是否需要被建造。他们会问它能解决什么问题,为什么要解决这些问题。他们询问谁将会使用这个软件,在什么规模上使用。他们考虑软件将在哪里运行,以及他们将如何监测它是否正常工作。他们还决定如何衡量该软件是否真正解决了它应该解决的问题。

https://codewithstyle.info/software-vs-systems/

📒 React Router 6.4

React Router 发布 6.4,大量新功能,包括数据加载/突变/重新验证、错误/中断/竞争条件处理以及支持 Suspense 的加载/骨架 UI 等。

1、路由创建方式变更,之前用 BrowserRouter + Routes,现在改用 createBrowserRouter 创建路由和 RouterProvider 渲染路由。

<RouterProvider router={createBrowserRouter([
{ path: '/', element: <Root /> }
])} />

2、路由级的数据流,基于 loader + action + Form,在路由配置中声明 loader 和 action,然后如下使用即可。

处理数据加载。

// 声明数据
export async function loader() {}
// 使用数据
useLoaderData();

处理数据提交。

// 使用定制表单元素
<Form />
// 处理表单提交
export async function action() {}

3、延迟数据加载方案,基于 defer + useAsyncValue/Await。由于考虑到 CLS(Content Layout Shift),默认没有做延迟数据加载,开发者可以手动开启。用哪种方式其实是需要权衡的,各有利弊。

// 1、loader 里延迟返回
export async function loader() {
return defer({ count: 0 });
}

// 2、渲染时用 Await 延迟渲染,不阻塞 Suspense 的瀑布流
<Await resolve={data.count} errorElement={<p>error load count</p>}>
{(count) => <p>{count}</p>}
</Await>

4、ScrollRestoration 可以模拟浏览器在加载器完成后位置变化时的滚动恢复,以确保滚动位置恢复到正确的位置,甚至跨域。

<ScrollRestoration />

📒 Go 代码风格没人喜欢?不对,Gofmt 是所有人的最爱...

⭐️ 程序里对象很深很大,可以用这个设计模式缓解一下

📒 「Go工具箱」推荐一个变量调试神器:go-spew

📒 【第2734期】JavaScript & Node.js 的测试最佳实践 - 第二章:后端测试

📒 Go语言爱好者周刊:第 160 期 — 竟然这么多人不理解 map 的 make 含义

📒 简单的 redis get 为什么也会有秒级的延迟

📒 golang net/url 路径穿越漏洞

· 9 min read
加菲猫

📒 Babel 插件:30分钟从入门到实战

⭐️ 全面解读!Golang中泛型的使用

📒 Umi4.0多页签设计

📒 V8 是如何执行 JavaScript 代码的

📒 「Go工具箱」推荐一个非常简单的深拷贝工具:deepcopy

📒 ECMAScript 2023将新增的9个数组方法

📒 关于 React Re-Render

📒 Go:基于 Redis 实现的延迟队列详解

📒 这个帮你理清Go程序调用栈的工具,1.17以上可用

📒 一起来实现一个Antd Form

📒 如何检测 JavaScript 原生函数是否被打过 "猴子补丁"

📒 鹅厂后台大佬教你Go内存管理!

📒 Plasmo Framework:次世代的浏览器插件开发框架

⭐️ 不得不知道的Golang之sync.Map解读!

📒 前端食堂技术周刊第 52 期:Babel 7.19.0、Fresh 1.1、React为什么重新渲染、新的 Web 性能指标

📒 我被骂了,但我学会了如何构造高性能的树状结构

📒 「Go工具箱」一个将非负整数转换成唯一、无序ID的工具:hashids

📒 【图书】程序员的底层思维

📒 作为大厂面试官,原来这种学生最吃香!

📒 Why Storybook in 2022

https://storybook.js.org/blog/why-storybook-in-2022/

📒 如何像gitlab-runner那样将Go应用安装为系统服务

📒 React Router v6 完全指南

📒 【第2726期】开发模式 "Development Mode" 是如何工作的

📒 你信吗?Go 泛型竟然已经被迅速采用

📒 做一个不崩溃的核酸系统到底有多难

📒 写给前端仔的自动化测试入门小作文

📒 「Go工具箱」一个兼具单机和集群模式的轻量级限流器:throttled

📒 CSR 最佳实践

随着 Next.js 和 Remix 的流行,SSR 看似已成为 React 社区的首选。但如果你用 SSR 是为了性能和 SEO,那可能可以重新考虑下,因为 CSR 也能做到。

关于性能。

1、减少尺寸。1)少用依赖,2)选择轻量级的依赖,比如用 day.js 代替 moment,用 zustand 代替 redux toolkit。

2、缓存。利用 webpack 的 cacheGroups 设置,提取依赖,当依赖没有变更时,hash 值不变,提高缓存利用率。推荐配置如下,让每个依赖拥有单独的文件和 hash,这样单个依赖变更时不会影响其他依赖。

optimization: {
runtimeChunk: 'single',
splitChunks: {
chunks: 'initial',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
// 加这句可以避免异步 chunk 的 vendor 重复问题,比如 a 和 b 都依赖 moment,不加这句 moment 会被打两遍而不是被提取出来
chunk: 'all',
// 让每个依赖拥有单独的文件和 hash
name: ({ context }) => (context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/) || [])[1]
}
}
}
}

3、Code Splitting。通常有两种,1)基于路由的 Code Splitting,当用户访问页面 A 时无需加载页面 B、C、D 的脚本,2)大依赖 Code Splitting,让整体页面更快出来,让大依赖的部分不影响页面渲染速度。

4、预加载异步 Chunk。主要避免出现下图中最后一个资源文件的瀑布流现象,思路是生成和路由对应的 assets 表,然后在 HTML 最前面加入「匹配路由生成 link preload 标签」的脚本。

5、生成静态数据。在 build 阶段从 CMD 或服务器上把数据拉下来,存成 json 或其他格式,用户请求时就只需从本地读取即可,访问本地或就近的 CDN 肯定比访问远程服务器更快。如果要重新生成数据,重新跑 build 或者重新执行脚本就好。

6、预加载数据。这和「4」类似,4 预加载的是 JS,这里需要预加载数据。做法也和「4」类似,把数据请求和路由做关联,然后运行时「匹配路由生成针对数据请求的 link preload 标签」。

7、预加载其他页面的数据。当 hover(desktop)或进入 viewport(mobile)时,做的对应 Link 的 preload as fetch。

8、避免序列化的渲染。比如一个应用有 Nav 导航 + 主内容,是应该先出导航再出主内容,还是应该导航和主内容都好了之后一起出?作者觉得应该是后者,实现方法是通过调整 Suspense 元素的位置。这一点其实我是有疑问的,我觉得前一种渲染方式也挺好,避免长时间的白屏。

9、Transition 切换页面(依赖 React 18)。当我们切换页面时,有两个选择。1)切过去,等 loading,渲染;2)等 loading,切过去+渲染。基于 React 18 的 useTransition 可以实现后者,代码如下。

const n = useNavigate();
startTransition(() => n(to));

10、预加载异步页面。作者介绍了个方法,把 React.lazy 封一下,在 window load 事件之后延迟自动执行。

const lazyPrefetch = chunk => {
window.addEventListener('load', () => {
setTimeout(chunk, 200)
}, { once: true });
return React.lazy(chunk);
}

11、Module Federation。

https://github.com/theninthsky/client-side-rendering

📒 人手 10x 工程师

先看两种 10x 工程师。1)写 10x 代码的工程师,但这类工程师对于负责 Review 他代码的同学会很有挑战,2)能评估复杂问题,站在技术前沿,先人一步拿出优雅解决方案的工程师,这类工程师一个团队通常没有几个。

你可能会觉得这两种都比较遥远,别灰心,还有一种 10x 工程师。他关注开发体验(dx)和团队健康,关注小但有倍增效应的事。比如添加缓存以加快持续集成,比如定期修复项目 setup 说明,比如增加 precommit 或加快自动化测试后让故障发生在本地而不是 CI 环境。这些变化随着时间推移所产生的累积效应,虽然不是你自己做了 10x 的工作,但通过提高一群工程师的生产力也能达到 10x 提效。

https://typeofnan.dev/10x-engineering-for-the-rest-of-us/

📒 开源相关

  • Next.js 发布 12.3,Fast Refresh 支持 .env 和 tsconfig.json,检测到 ts 或 tsx 文件自动完成 TypeScript 配置、SWC Minifier Stable
  • Meta 开源 shumai,基于 Bun + Flashlight 的 JavaScript 机器学习库
  • module-federation/nextjs-mf,让 Next.js 支持 Module Federation,目前 CSR Only
  • gradejs/gradejs,GradeJS 无需源码即可分析生产阶段的 webpack 产物构成,包括潜在问题,依赖了哪些包,等等

📒 Go语言爱好者周刊:第 159 期 — 这道题目有点意思

· 28 min read
加菲猫

📒 CSS 如何实现文本换行

在开发的时候遇到一个问题,后端给的文本包含换行符 /n,前端如何实现换行展示。最开始以为只能通过富文本展示,但实际上 CSS 中有一个属性可以支持换行:

white-space: pre-wrap;

https://developer.mozilla.org/zh-CN/docs/Web/CSS/white-space

📒 Go 生态:Prometheus的四种指标类型,我终于搞懂了

📒 Go 语言创始人:复制亿点点代码比用别人轮子好!

📒 「Go工具箱」推荐一个实现进度条功能的工具:uiprogress

📒 给蚂蚁金服antv提个PR,以为是改个错别字,未曾想背后的原因竟如此复杂!

📒 前端相关文章汇总

https://medium.com/@bytefer

https://medium.com/pixel-and-ink/a-peek-at-userequest-hook-ba960cbddbf8

https://javascript.plainenglish.io/15-utility-types-that-every-typescript-developer-should-know-6cf121d4047c

https://blog.bitsrc.io/6-best-ways-to-create-a-new-react-application-57b17e5d331a

📒 Golang 技术方案

https://kms.netease.com/topics/topic/612/item/14727

https://kms.netease.com/article/27452#%E9%80%89%E5%9E%8B

https://github.com/NetEase-Media/ngo

📒 依赖注入简介

https://blog.codeminer42.com/dependency-injection-in-js-ts-part-1/

📒 JavaScript 模块中的默认导出很糟糕吗

https://www.lloydatkinson.net/posts/2022/default-exports-in-javascript-modules-are-terrible/

📒 VS Code 发布新版本

保持上下文的 "粘性滚动"不再是一个实验性功能。将与 TypeScript v4.8 一起发布。

https://code.visualstudio.com/updates/v1_71

📒 使用 gRPC 构建安全 API

一个允许两个 Node 应用程序通过 HTTP/2 和基于协议缓冲区的 gRPC 机制进行通信的演练教程。

https://snyk.io/blog/building-a-secure-api-with-grpc/

📒 最小化依赖关系的四种方法

在接连不断的供应链事故(和漏洞),或者查看了 node_modules 文件夹的最终体积后,你可能会想要将依赖关系保持在最低限度。这篇文章介绍了一些方法。

https://blog.appsignal.com/2022/08/31/4-ways-to-minimize-your-dependencies-in-nodejs.html

📒 安装并运行 bin 脚本

npm 包可以通过 package.json 的 bin 属性指定它们提供的 shell 脚本和可运行文件。Axel 深入研究了它的工作原理,并且提供了两种方式来安装提供此类脚本的软件包。

https://2ality.com/2022/08/installing-nodejs-bin-scripts.html

📒 Create Rust App: 用一行命令开启现代 Rust + React Web 应用程序

如果你想用 Rust 构建应用程序的后端,那么该库提供了一种 CRA 式的体验。它使用 Vite 并且新增了 SQLite 支持。

https://github.com/Wulf/create-rust-app

📒 何时使用 useLayoutEffect 而不是 useEffect

你的 UI 渲染过程中有烦人的闪烁吗?可能是你错误使用了其中之一的 Hook : 这与 Hook 是在浏览器绘制之前还是之后触发有关。

https://javascript.plainenglish.io/react-hooks-when-to-use-uselayouteffect-instead-of-useeffect-3271a96d881a?gi=622ccbf807f3

📒 美团二面:考我幻读,结果答的不好

📒 码住!Golang并发安全与引用传递总结

先看一个在Go中关于Map类型并发读写的经典例子:

var testMap  = map[string]string{}
func main() {
go func() {
for{
_ = testMap["bar"]
}
}()
go func() {
for {
testMap["bar"] = "foo"
}
}()
select{}
}

以上例子会引发一个Fatal error:

fatal error: concurrent map read and map write

产生这个错误的原因就是在Go中Map类型并不是并发安全的,出于安全的考虑,此时会引发一个致命错误以保证程序不出现数据的混乱。

Golang 如何检测 Map 并发异常?

对于查询操作,大致检查并发错误的流程如下:在查询前检查并发flag是否存在,如果存在就抛出异常。

if h.flags&hashWriting != 0 {
throw("concurrent map read and map write")
}

对于修改操作则如下:

  • 写入前检查一次标记位,通过后打上标记
  • 写入完成再次检查标记位,通过后还原标记
 //各类前置操作
....
if h.flags&amp;hashWriting != 0 {
//检查是否存在并发
throw("concurrent map writes")
}

//赋值标记位
h.flags ^= hashWriting
....
//后续操作
done:
//完成修改后,再次检查标记位
if h.flags&hashWriting == 0 {
throw("concurrent map writes")
}
//还原标记位取消hashWriting标记
h.flags &^= hashWriting

如何避免 Map 的并发问题?

go官方认为因为Map并发的问题在实际开发中并不常见,如果把Map原生设计成并发安全的会带来巨大的性能开销。因此需要使用额外方式来实现。

  1. 自行使用锁和map来解决并发问题
type cocurrentMap = struct {
sync.RWMutex
m map[string]string
}

func main() {
var testMap = &cocurrentMap{m:make(map[string]string)}
//写
testMap.Lock()
testMap.m["a"] = "foo"
testMap.Unlock()
//读
testMap.RLock()
fmt.Println(testMap.m["a"])
testMap.RUnlock()
}

这个方法存在问题就是并发量巨大的时候,锁的竞争也会带来巨量消耗,性能一般

  1. 使用sync.Map

sync.Map通过巧妙的设计来提高并发安全下Map的性能,其设计思路是通过空间换时间来实现的,同时维护2份数据,read&dirty。read主要用来避免读写冲突。

其数据结构如下:

type Map struct {
mu Mutex //锁
read atomic.Value //readOnly
dirty map[interface{}]*entry //*entry
misses int
}

type readOnly struct {
m map[interface{}]*entry
amended bool // true if the dirty map contains some key not in m.
}

type entry struct {
p unsafe.Pointer // *interface{}
}

使用示例如下:

var m sync.Map
// 写
m.Store("test", 1)
m.Store(1, true)

// 读
val1, _ := m.Load("test")
val2, _ := m.Load(1)
fmt.Println(val1.(int))
fmt.Println(val2.(bool))

//遍历
m.Range(func(key, value interface{}) bool {
//....
return true
})

//删除
m.Delete("test")

//读取或写入
m.LoadOrStore("test", 1)

这里对sync.Map的原理不做深入展开,只提几点特性:

  • read和dirty是共享内存的,尽量减少冗余内存的开销
  • read是原子性的,可以并发读,写需要加锁
  • 读的时候先read中取,如果没有则会尝试去dirty中读取(需要有标记位readOnly.amended配合)
  • dirty就是原生Map类型,需要配合各类锁读写
  • 当read中miss次数等于dirty长度时,dirty会提升为read,并且清理已经删除的k-v(延迟更新,具体如何清理需要enrty中的p标记位配合)
  • 双检查(在加锁后会再次对值检查一遍是否依然符合条件)
  • sync.Map适用于读多写少的场景
  • sync.Map没有提供获取长度size的方法,需要通过遍历来计算

切片类型 Slice 是并发安全的吗

与Map一样,Slice也不是并发安全的。但是在切片中并不会引发panic,如果程序无意中对切片使用了并发读写,严重的话会导致获取的数据和之后存储的数据错乱,所以这里要格外小心,可以通过加锁来避免。

切片除了并发有问题外,还有几点注意:

  • Go只会对基础值类型在传参中使用深拷贝,实际上对于Slice和Map类型,使用的是浅拷贝,Slice作为传参,其指向的内存地址依然是原数据
  • Slice扩容机制的影响:向Slice中添加元素超出容量的时候,我们知道会触发扩容机制,而扩容机制会创建一份新的【原数据】此时,它与浅拷贝获取到的变量是没有任何关联的

码住!Golang并发安全与引用传递总结

📒 ES6你用过哪些惊艳的写法

📒 用代码画时序图!YYDS

📒 面试官:mysql查询 limit 1000,10 和limit 10 速度一样快吗

📒 10 best practices to build a Java container with Docker

📒 How to create a systemd service in Linux

📒 Go 1.19.1 和 Go 1.18.6 终究还是来了

📒 5分钟自建数据库可视化平台,在线管理数据库也太方便了!

📒 匿名 iframe:COEP 的福音!

📒 这些强大的 JS 操作符,你都知道吗

📒 「Go工具箱」一个基于双向链表实现的LRU缓存工具

📒 这个 Go 开发的网络抓包工具,不仅好用还支持ES检索

📒 一道简单又有意思的 JavaScript 手写题 — 异步加法 asyncAdd

📒 高并发下的网络 IO 模型设计

📒 qiankun微前端改造实战(架构设计+代码实现)-超级详细vue代码干货篇!(伸手党福利)

📒 Go语言中常见100问题-#8 any says nothing

📒 读 Node.js 源码深入理解 cjs 模块系统

📒 当 React Query 遇见 React Router

React Router 6.4 即将正式发布,React Router 也加入了远程状态管理的数据获取游戏。本文将带你了解 React Router 和现有的远程状态管理库(如:React Query)之间的竞争和关联,作者认为他们是天造地设的一对。

https://beta.reactrouter.com/en/dev

https://tkdodo.eu/blog/react-query-meets-react-router

隔壁家 Vue Router 的数据获取相关提案 Vue Router Data Loaders。

https://github.com/vuejs/rfcs/discussions/460

📒 重新思考流行的 Node.js 模式和工具

Node.js 最佳实践、JavaScript 和 Node.js 测试最佳实践 的作者 Yoni Goldberg 对 Node.js 中的流行工具发出了灵魂拷问,并给出了他的思考:

  • Node-convictDotenv 更好;
  • 从 Controller 调用 Service 时,要尽量抽象 Service 的内容(多用心封装封装),尽可能屏蔽掉技术细节和复杂性,让看你代码的同学赏心悦目一些;
  • Nest.js 中万物皆可依赖注入,但简单点也许世界会更美好;
  • 不一定要用 Passport.js
  • SuperTest 的三合一语法有时候并没有那么好用;
  • Fastify 装饰器错误姿势
  • catch 子句的正确姿势;
  • 避免重复使用日志工具 Morgan
  • 减少使用 process.env.NODE_ENV 当作判断条件。

https://practica.dev/blog/popular-nodejs-pattern-and-tools-to-reconsider/

📒 8 月登陆网络平台的新内容

Firefox 104、Chrome 104、Chrome 105 发布稳定版本。

  • Chrome 104 新增 CSS transform 属性单独定义
  • Chrome 104 新增 媒体查询的新语法,支持比较运算符,更加符合人体工程学
  • Chrome 105 新增 容器查询和 :has(),响应式的最佳拍档
  • Chrome 105 新增 Sanitizer API,防止 XSS 攻击的灭菌武器
  • Chrome 105 新增 :modal 伪类
  • Firefox 104 支持 The findLast() 和 findLastIndex()

https://web.dev/web-platform-08-2022/

📒 pnpm v7.10.0

Time-based 依赖解析模式

  1. 直接依赖将安装其最低版本,比如 foo@^1.1.0,将会安装 1.1.0
  2. 间接依赖只会安装被选中的直接依赖在其发布时间点之前的版本

据作者说,这种模式是为了减少供应链攻击导致项目“噶”的风险,因为它能保证间接依赖不会比直接依赖更加新。这样如果间接依赖被攻击,也不会安装被攻击的版本。不过这种解析模式需要拿到 npm 的完整元数据,所以速度会很慢。解法是自建 Verdaccio,并将 registry-supports-time-field 设置为 true。

能减少供应链攻击的风险是好事,但是感觉这种模式本身存在很多问题,比如不遵守 semver 语义,虽然社区里很多项目都没有好好遵守 :)、或者当旧的间接依赖修复了某个 bug,但是有 bug 的版本还是会被安装。

https://github.com/pnpm/pnpm/releases/tag/v7.10.0

📒 前端食堂技术周刊第 51 期:pnpm v7.10.0、8 月登陆网络平台的新内容、重新思考流行的 Node.js 模式和工具、打包 JavaScript 库的

📒 从Vuex到Pinia

📒 程序员有很厉害,不外传的代码吗?可以运行,但不能动的那种!

📒 你想知道的前后端协作规范都在这了

📒 1.3w字,一文详解死锁!

📒 【第2724期】前后端数据接口协作提效实践

📒 去字节面试,直接让人出门左拐:Bean 生命周期都不知道!

📒 聊聊数据库建表的15个小技巧

📒 如何优化你的 Node.js API

📒 金九银十Go面试题进阶知识点:select和channel

📒 禁用 Cookie 后会怎样

会报错!解法很简单也很粗暴,就是加 try…catch。

https://blog.tomayac.com/2022/08/30/things-not-available-when-someone-blocks-all-cookies/

📒 现代 JS 库打包

这是一篇关于如何打包 JavaScript 库的现代化指南,介绍了维护 JavaScript 库的一些基础知识和最佳实践,推荐作为入门阅读。

https://github.com/frehner/modern-guide-to-packaging-js-library

📒 Error Boundary 指南

虽然理想情况下是在生产之前捕获错误,但有些错误是会躲过测试的,比如网络错误,而如果没有正确处理,这些错误会导致 React 声明周期崩溃,导致白屏,并最终影响你的用户。正确的方式是提供适当的视觉反馈和潜在的行动指引(例如:重试机制)来优雅的处理这些错误。

你可能会有几个疑问。1)为啥会白屏?因为从 React 16 开始,没有被捕获的错误将导致整个 React 组件树的卸载。2)为啥 try.catch 不行?因为 React Hooks 执行是异步的,捕获不了。

解法当然就是用 React 官方提供的 Error Boundary 了,通过 Error Boundary 把组件包起来,可以想象成是包了一层 try…catch,组件报错会到此为止,不会再往上报。一个简单的 ErrorBoundary 如下。

class ErrorBoundarySimple extends React.Component {
state = { hasError: false };
componentDidCatch(error: unknown) {
// report the error to your favorite Error Tracking tool (ex: Sentry, Bugsnag)
console.error(error);
}
static getDerivedStateFromError(error: unknown) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
render() {
if (this.state.hasError) { return <p>Failed to fetch users.</p>; }
return this.props.children;
}
}

但是官方的 Error Boundary 其实有缺陷,他不支持以下场景的报错,包括 Event handlers、异步代码比如 setTimeout 或 requestAnimationFrame 回调、SSR、Error Boundary 自己抛的错。同时,我们可能还需要提供重试机制等行动指南。解法是用 react-error-boundary 这个库。

react-error-boundary 如何支持重试?他提供的 ErrorBoundary 组件可以配置 FallbackComponent 组件,而这个组件有 error 和 resetErrorBoundary 两个 props,后者用于重试。

import { ErrorBoundary } from 'react-error-boundary';
function ErrorFallback({ error, resetErrorBoundary }) {
return <>
<pre>error.message</pre>
<button onClick={resetErrorBoundary}>retry</button>
</>
}
export default function App() {
return <ErrorBoundary FallbackComponent={ErrorFallback}><Users /><ErrorBoundary>;
}

react-error-boundary 如何捕获 React 生命周期外的错误?比如 Event Handler 和异步代码。react-error-boundary 提供 useErrorHander hook,他会返回处理错误的函数。

import { useErrorHandler } from 'react-error-boundary';
export default App() {
const handleError = useErrorHandler();
function handleSubmit(event) {
fetch(xxx).then(...).catch(e => handleError(e));
}
// do render
}

https://meticulous.ai/blog/react-error-boundaries-complete-guide/

📒 10 个 React 新手陷阱

React 是 unopinionated 的,同样的问题会存在许多不同的解法,这给开发者留下了大量的空间来用自己愚蠢的想法把事情搞砸。在今天的文章中,我们将看看 React 中的 10 个陷阱(反模式),以及改进我们代码的技巧和窍门,同时也可以为前端技术面试做准备。

1、巨型组件。就是一个组件内很大,通常是因为不知道如何组织代码,或者不想把时间浪费在组织代码上。这样的组件很难理解、重构和测试。通过 WebStorm 的「Refactor - Move」或者 VSCode 的 Glean 插件快速提速代码到文件,可以节省你不少时间。

2、嵌套陷阱。不要在组件内定义子组件,比如 function Parent() { const Child = () => <div>Child</div>; return <><Child /></> },解法是把 Child 定义提到外面。

3、没有用 useMemo/useCallback。遇到重计算的点,每次 re-render 会重新执行导致性能消耗严重,比如 const total = expensiveCal(count)。解法是用 useMemo 避免重复计算,比如 const total = useMemo(() => expensiveCal(count), [count])。如果遇到函数,记得切换到 useCallback。

4、无意义的 div 元素。React 组件只能由一个根组件,当需要渲染多个相邻的元素时,你可能会引入 div 元素。但这不是必要的,不必要的 div 元素可能导致可访问性和 CSS 样式问题。更好的做法是用 Fragment 组件,比如 <><Foo /><Bar /></>

5、混乱的文件。随着组件越来越多,在一个文件中导出多个组件是「诱人」的。但是这样,也会很快让事情变得复杂。更好的做法是有一套自己的文件组织规范,比如一个组件一个文件,或者一个组件一个文件夹,同时在文件夹下可能还会有自己的测试文件等。

6、巨大的 Bundle 产物。大型复杂应用的最大问题之一就是产物很大,这会让页面加载变慢,因为浏览器需要很长时间来下载、解析和执行 JavaScript Bundle 产物。解法是应用内置了 code splitting 功能的框架,或者使用 Suepense + React.lazy + import()。

7、Prop Drilling。当上层组件中持有 state,而一个深度嵌套的组件需要使用这个 state 时,一种做法是用 props 透过中间组件一层层往下传,而实际上中间组件并不需要他们,这就是 Prop Drilling。解法是用 Redux 或其他数据流工具,或者使用 Context。

剩下 3 个是 Prop Plowing、Try Some Curry 和 Code Smarter,感觉不太重要,是为了凑 10 个加的,我就不介绍了,有兴趣的可以查看原文。

https://medium.com/@imranfarooq0306/10-react-traps-to-avoid-as-react-developer-5570808e346b

📒 useMemo + useCallback

这是一篇很好的 useMemo 和 useCallback 入门文章,但没有引入啥新知识,我就不展开了。

https://www.joshwcomeau.com/react/usememo-and-usecallback/

📒 React 18 SSR 流式渲染

React 18 SSR 流式渲染可基于 Suspense + Lazy、renderToPipeableStream 和 hydrateRoot 实现。Suspense + Lazy 在客户端用于代码拆分和懒加载,在服务端则用于流式渲染,renderToPipeableStream 用于流式返回数据,hydrateRoot 用于在流式返回后就开始注水而无需等到全部内容都返回。

以上完成了流式渲染,但还有个问题需要解,即「数据怎么获得」?并且需要同时考虑服务端和客户端。作者给的解是还处于实验阶段的 Data Fetching API。

const resource = fetchProfileData();

function Posts() {
const posts = resource.posts.read();
// render with posts
}

目前要支持 Data Fetching API,可将 Promise 包装成 Data Fetching API。

function wrapPromise(p) {
let status = 'pending';
let result;
let suspensder = p.then(r => {
status = 'success';
result = r;
}).catch(e => {
status = 'error';
result = e;
});
return {
read() {
if (status === 'pending') throw suspensder;
else if (status === 'error') throw result;
else if (status === 'success') return result;
}
}
}

Suspense 识别 Data Fetching 的 loading 状态是基于 read 方法的返回值。如果 throw promise 即还在 loading,如果 throw error 则出错,如果返回数据则完成 loading。

在 React 18 下,如何实现产品级的 SSR 和流式渲染

📒 开源推荐

1. clean-pkg-json

NPM 发包时不需要的 package.json 字段

https://github.com/privatenumber/clean-pkg-json

2. ts-prune

用于找到 TypeScript 项目未使用的 export 信息,消除 dead code

https://github.com/nadeesha/ts-prune

3. module-federation/typescript

Typescript Types Support For Module Federation

https://github.com/module-federation/typescript

4. React Router 6.4 将于下周发布

📒 Go 眼中的文件系统是什么? io.FS

Go 理解的文件系统,只要能实现一个 Open 方法,返回一个 File 的 interface ,这个 File 只需要实现 Stat,Read,Close 方法即可。

// 文件系统的接口
type FS interface {
Open(name string) (File, error)
}

// 文件的接口
type File interface {
Stat() (FileInfo, error)
Read([]byte) (int, error)
Close() error
}

有没有发现,OS 的 FS 已经满足了条件。所以,Go 的 FS 可以是 OS 的 FS ,自然也可以是其他的实现。

Go 在此 io.FS 的基础上,再去扩展接口,增加文件系统的功能。比如,加个 ReadDir 就是一个有读目录的文件系统 ReadDirFS :

type ReadDirFS interface {
FS
// 读目录
ReadDir(name string) ([]DirEntry, error)
}
tip

以上是 Golang 接口继承的写法,ReadDirFS 接口继承了 FS 接口,同时扩展了 ReadDir 方法。注意,Golang 是不支持面向对象的。

加个 Glob 方法,就成为一个具备路径通配符查询的文件系统:

type GlobFS interface {
FS
// 路径通配符的功能
Glob(pattern string) ([]string, error)
}

加个 Stat ,就变成一个路径查询的文件系统:

type StatFS interface {
FS
// 查询某个路径的文件信息
Stat(name string) (FileInfo, error)
}

这些非常经典的文件系统的定义 Go 在 io/fs 里面已经做好了。

我们的目标是实现一个 Go 的 FS ,这个定义已经在 io.FS 有了。我们只需要写一个结构体,实现它的方法,那么你就可以说这是一个 FS 了。

这里其实就可以有非常多的想象空间,比如,可以是 OS 的 FS,也可以是 memory FS ,hash FS 等等。网上有不少例子。但其实标准库已经有一个最好的例子,那就是 embed FS 。

我们来看下 embed 怎么实现一个内嵌的文件系统。embed 的实现在 embed/embed.go 这个文件中,非常精简。

首先,在 embed package 里定义了一个结构体 FS ,这个结构体将是 io.FS 的具体实现。

// 作为具体 FS 的实现
type FS struct {
files *[]file
}

// 代表一个内嵌文件
type file struct {
name string
data string // 文件的数据全在内存里
hash [16]byte // truncated SHA256 hash
}

embed 里面的 FS 结构体只需要实现 Open 这个方法即可:

// Open 的具体实现
func (f FS) Open(name string) (fs.File, error) {
// 通过名字匹配查找到 file 对象
file := f.lookup(name)
// 如果没找到
if file == nil {
return nil, &fs.PathError{Op: "open", Path: name, Err: fs.ErrNotExist}
}
// 如果是目录结构
if file.IsDir() {
return &openDir{file, f.readDir(name), 0}, nil
}
// 找到了就封装成 openFile 结构体
return &openFile{file, 0}, nil
}

上面的 Open ,如果是文件的化,返回的是一个 openFile 的结构体 ,作为 io.File 接口的具体实现:

// 代表一个文件的实现
type openFile struct {
f *file // the file itself
offset int64 // current read offset
}
func (f *openFile) Close() error { return nil }
func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil }
func (f *openFile) Read(b []byte) (int, error) {
// 判断偏移是否符合预期
if f.offset >= int64(len(f.f.data)) {
return 0, io.EOF
}
if f.offset < 0 {
return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid}
}
// 从内存拷贝数据
n := copy(b, f.f.data[f.offset:])
f.offset += int64(n)
return n, nil
}

如上,只需要实现 Read,Stat,Close 方法即可。这就是一个完整的、Go 层面的 FS 的实现。

tip

Go 的 FS 封装有啥用呢

  • 单测方便了,可以直接对应用进行测试,不必依赖底层实现
  • 封装出一个 io.FS 的抽象,意图和 OS 的 FS 解耦,类似 embed FS 这种非 OS 文件系统的需求,可以有方法扩展了

Go 眼中的文件系统是什么? io.FS

📒 对 Go2 错误处理提案的批判

📒 微服务配置中心, 这个方案 Go 里用起来不输 SpringCloud

📒 Go语言爱好者周刊:第 158 期

· 8 min read
加菲猫

📒 Resize Observer 介绍及原理浅析

📒 【第2722期】腾讯医药微信小程序性能优化:从0.9秒到0.5秒

📒 深入理解 Mocha 测试框架:从零实现一个 Mocha

📒 React 官网为什么那么快

📒 基于storybook组件库的技术选型该怎么选

📒 图解 Node.js 的核心 event-loop

我们平时说 Node.js 是单线程仅仅是指 node 执行我们的 JS 代码,更准确地说是 V8 执行 JS code 是发生在单线程里面的。实际上如果你打开 Node 进程,会发现它有不少 worker thread。这是一个典型的单进程多线程模型。这些 worker thread 被放置于线程池里面,而 V8 执行 JS code 的线程被称为主线程。

图解 Node.js 的核心 event-loop

📒 手把手教你手写 Vite Server(二)—— 插件架构设计

📒 为什么 Vue3 的 VNode 不能单独组成一棵完整的树

📒 深度解读 Vite 的依赖扫描

📒 浅谈MyBatis批量插入的3种方法,10w条数据使用foreach仅需2秒!!

📒 「Go工具箱」重磅推荐:一个国产的,star高达12k+的操作excel的包:Excelize

📒 拒绝 Go 代码臃肿,其实在这几块可以用下观察者模式

📒 几个非常有意思的javascript知识点总结

📒 AI数字绘画 stable-diffusion 保姆级教程

📒 Go 的零值有什么用?看看这 4 个场景

📒 对前端架构的理解 - 分层与抽象

📒 【第2719期】如何使用 HTTPS 进行本地开发

📒 Go 适合 IO 密集型?并不准确!

📒 Announcing Vituum - Template engines and more in Vite

📒 「Go工具箱」一个让终端内容彩色化的工具:Color

📒 如何让 Go 反射变快

📒 MySQL 单表最大两千万?我不信

📒 了解微前端,深入前端架构的前世今生

📒 Deno cheat sheet

🌛 用 Rust 实现的数据结构与算法合辑

📒 使用 React 18 和 Suspense 改善 INP(Interaction to Next Paint)指标

https://vercel.com/blog/improving-interaction-to-next-paint-with-react-18-and-suspense

📒 7 种创建新 React 应用的最佳方式

Create React App 可能是最有名的,但还有其他一些方法值得考虑,包括更大的框架(如 Next.js)或构建系统(如 NX)。

https://blog.bitsrc.io/6-best-ways-to-create-a-new-react-application-57b17e5d331a?gi=dc52c656ac21

📒 Advanced React component composition

https://frontendmastery.com/posts/advanced-react-component-composition-guide/

📒 Why React Re-Renders

如果你想要使 React 应用获得最好的性能,那么理解和正确处理渲染过程是非常重要的。有很多关于如何使 React 渲染更高效的文章,但这篇文章深入探讨了为什么 React 能够以一种可访问的、容易遵循的方式进行渲染。

https://www.joshwcomeau.com/react/why-react-re-renders/

why-did-you-render 是一个经典的工具,用于进一步深入这个问题。

https://github.com/welldone-software/why-did-you-render

⭐️ 工厂模式有三个Level,你能用Go写到第几层

📒 TypeScript 4.8 发布!重点新特性解读

📒 React + TypeScript 最小知识集

import React, { HTMLAttributes, PropsWithChildren } from 'react';

interface HelloProps extends HTMLAttributes<HTMLDivElement> {
name: string;
}

const Hello: React.FC<PropsWithChildren<HelloProps>> = ({
name,
children,
...rest
}) => {
return (
<div>
<div {...rest}>{`Hello, ${name}!`}</div>
{children}
</div>
);
};
  • React.FC 表示 Function Component 函数式组件
  • PropsWithChildrenprops.children 带类型
  • HTMLAttributes<HTMLDivElement>props 可以使用 html 属性比如 className

https://ente.io/blog/tech/typescript-for-react/

📒 Tauri vs. Electron

作者分别用 Tauri 和 Electron 实现了 Authme,然后从打包、启动时间、性能、后端、应用渲染、安全、自动更新、开发体验共 8 个维度进行了对比。

1、「打包」Tauri 完胜。1)尺寸上 Tauri 2.5M vs. Electron 85M,2)Tauri 的产物是二进制的,反编译解码逻辑所需成本相比 Electron 会高很多。 2、「启动时间」Tauri 胜。Tauri 2s vs. Electron 4s。 3、「性能」Tauri 完胜。 4、「后端」Electron 胜。因为 Electron 后端基于 Node,而 Tauri 基于 Rust。当然如果你会 Rust 则是另一回事。另外,Tauri 的 Roadmap 里有支持其他后端绑定的计划,比如 Deno,届时就又可以用 JavaScript/TypeScript 写后端了。 5、「应用渲染」Electron 胜。Electron 使用 Chromium,所以你的用户在 Windows、Linux 和 macOS 上看到的东西是一样的。Tauri 使用系统的 WebView,Windows 上使用 Edge Webview2(Chromium),Linux 上使用 WebKitGTK,macOS 上使用 WebKit。这里的问题是 Webkit 的支持总是落后一点,所以你可能会需要额外的补丁。 6、「安全」Tauri 胜。Tauri 内置大量安全功能,可以明确启用或禁用某些 API。Electron 中则可以完全访问 Node 的 API,所以相比来说更容易被黑客利用。 7、「自动更新」Electron 胜。Tauri 和 Electron 都内置了自动更新器,而 Tauri 的相对简单,同时需要维护依赖并手动更新 JSON,而 Electron 可基于 electron-updater 并直接从 Github 发布的二进制文件中提取,要方便很多。 8、「开发体验」Tauri 胜。基于 Tauri CLI 就会包含热重载、为所有平台构建你的应用程序、生成应用程序图标等全部功能,而 Electron 啥都没有提供,只有框架本身。

https://www.levminer.com/blog/tauri-vs-electron

📒 father 4 正式发布

father 4 的具备如下核心特性:

  • 双模式构建: 支持 Bundless 及 Bundle 两种构建模式,ESModule 及 CommonJS 产物使用 Bundless 模式,UMD 产物使用 Bundle 模式
  • 多构建核心: Bundle 模式使用 Webpack 作为构建核心,Bundless 模式使用 esbuild 及 Babel 两种构建核心,可通过配置自由切换
  • 类型生成: 无论是源码构建还是依赖预打包,都支持为 TypeScript 模块生成 .d.ts 类型定义
  • 项目体检: 对 NPM 包研发常见误区做检查,让每一次发布都更加稳健
  • 微生成器: 为项目追加生成常见的工程化能力,例如使用 jest 编写测试
  • 依赖预打包: 开箱即用的依赖预打包能力,帮助 Node.js 框架/库提升稳定性、不受上游依赖更新影响(实验性)

📒 3种方式!Go Error处理最佳实践

📒 超大体量项目,微前端落地方案,看完后悔来找我

📒 快速搭建 SpringCloud Alibaba Nacos 配置中心!

📒 k8s下微前端如何做金丝雀发布

· 8 min read
加菲猫

📒 【第2714期】从Multirepo到Monorepo 袋鼠云数栈前端研发效率提升探索之路

📒 抖音平台多产物代码隔离技术的实践与探索

📒 Hooks时代,如何写出高质量的react和vue组件

⭐️ Vite 约定式路由的最佳实践

📒 Go使用泛型后,这么写单元测试会失效

📒 如何保证数据库和缓存双写一致性

⭐️ 【第2713期】工程化思维:主题切换架构

📒 为了拿捏 Redis 数据结构,我画了 40 张图(完整版)

📒 美团一面:如何在 100 亿数据中找到中位数

📒 解决老工程 toast 组件问题

老工程 React 15.x,Webpack 2.x,工程里面原先没有组件库。

关于 npm 包可能存在部分 ES6 语法,低版本 Webpack 无法解析问题,可以用 Babel 转一下。同理 NPM 包使用了 ES Module 模块语法,也可以用 Babel 转为 CommonJS。现在主要的问题是,大多数 toast 组件,底层都用了 React Hooks,即使 ES 语法兼容,但是运行环境 React 版本太老还是无法兼容。最终还是选用 rc-notification,参考 antd 封装:

https://github.com/react-component/notification/tree/2.0.6

https://github.com/ant-design/ant-design/blob/2.13.14/components/message/index.tsx

📒 Kubernetes原理与架构初探

📒 VS Code 1.70 新特性

3-way merge editor

三路合并编辑器是在1.69版本里上线的,这个功能允许用户在VS Code内快速解决 Git 合并冲突。该功能启用后,可以通过点击「源代码控制视图」中的冲突文件来打开合并编辑器。

settings.json
{
"git.mergeEditor": true
}

Git 提交辅助操作

可以使用 git.postCommitCommand 来设置控制辅助操作,比如允许用户在提交信息后进行推送或者同步。

settings.json
{
"git.postCommitCommand": "sync"
}

分支保护

在之前的1.68版本中,VS Code就添加了 git.branchProtection 以用于配置受保护的特定分支。

settings.json
{
"git.branchProtection": [
"main"
]
}

Command Center

Command Center 功能启用后,其位于编辑器顶部的长条空间,让用户可以快速搜索项目中的文件。

settings.json
{
"window.commandCenter": true
}

sticky scroll

当用户在编辑器中滚动鼠标查看代码时,每个子模块(比如:类/接口/命名空间/函数/方法等等)代码的第一行会置顶固定住,以方便查看。

这对于一些长代码模块(比如一个很长的函数)的阅读体验提升还是非常有帮助的。

settings.json
{
"editor.experimental.stickyScroll.enabled": true
}

📒 Java中的语法糖甜不甜?巧用枚举实现订单状态转换!

📒 看 Go 中的 struct 如何被优化,还有小插曲

📒 Rust 与 Go 可以互操作

📒 SWC 入门介绍

📒 听说TCP能保证不丢包?图解TCP六大丢包场景

📒 Vue3中defineEmits、defineProps 是怎么做到不用引入就能直接用的

📒 浅析神经网络 Neural Networks

📒 在 Go 里用 CGO?这 7 个问题你要关注!

📒 分布式接口幂等性、分布式限流:Guava 、nginx和lua限流

📒 Go 探讨了 13 年,怎么解决再赋值的坑

📒 TypeScript Collections

用 TypeScript 编写的数据结构合集

https://github.com/basarat/typescript-collections

📒 Vite Rollup Plugins

Vite3 中使用 Rollup 插件的兼容性列表

https://github.com/patak-dev/vite-rollup-plugins

📒 Go语言从0到1实现最简单的数据库!

📒 一文读懂TypeScript类型兼容性

📒 Communicating Effectively As A Tech Lead

Addy Osmani 的新作,从 Simplify、Be concise and on point、Communicating with executives、Listen、Be proactive、Be thorough 和 Take notes 7 个维度介绍作为 TL 时如何高效沟通。

https://addyosmani.com/blog/communication-tech-lead/

📒 React Re-Renders

通过这篇文章,我学到了「两个误解」、「一个比喻」和「一个技巧」。

两个误解是,1)state 变更会让整个 app re-render?不是,state 变更只会让当前组件及其子组件 re-render,2)组件 re-render 是因为 props 变更?也不是,props 往上可以追溯到 state 变更,是 state 变更导致了子组件 re-render,而不是由 props 变更引起。

上述误解 2 的一个例外是 React.memo,应用 React.memo 后的组件只有 props 变更才会触发 re-render。你可能会想:为什么这不是默认行为?因为作为开发者,我们往往高估了重新渲染的成本。对于 props 很多且没有很多子组件的组件来说,相比 re-render,检查 props 是否变更带来的消耗可能更大。因此,如果对每个组件都进行 React.memo,可能会产生反效果。

一个比喻是,每次渲染都是由照相机拍摄的快照,React 通过玩「找不同」的游戏找出两张照片之间的差异,然后决定是否 re-render。而 React.memo 则是懒惰的摄影师,如果你要求他为完全相同的东西拍 5 张照片,它会拍 1 张照片并给你 5 份。只有当你的指令改变时,他才会拍下一张新的照片。

一个技巧是,借助 React Devtools Chrome 插件,在「设置 > Profiler」里开启「Record why each component rendered while profiling」,再通过录制的方式排查,就能知道每个 re-render 的原因。

原文链接:

https://www.joshwcomeau.com/react/why-react-re-renders/

中文翻译:

【第2709期】一份详尽的 React re-render 指南

📒 深入解读新一代全栈框架 Fresh

从定位上来看,Fresh 属于 Web 全栈开发框架,相比 Next.js 和 Remix, Fresh 有哪些值得一提的亮点:

  • Fresh 基于 Deno 运行时,由 Deno 原班人马开发,享有 Deno 一系列工具链和生态的优势,比如内置的测试工具、支持 http import 等等
  • 渲染性能方面,Fresh 整体采用 Islands SSR 架构(之前介绍的 Astro 也是类似),实现了客户端按需 Hydration,有一定的渲染性能优势
  • 还有一个比较出色的点是构建层做到了 Bundle-less,即应用代码不需要打包即可直接部署上线
  • 最后,不同于 Next.js 和 Remix,Fresh 的前端渲染层由 Preact 完成,包括 Islands 架构的实现也是基于 Preact,且不支持其它前端框架

开发者并不需要手写路由文件,Fresh 可以自动地生成服务端的路由到文件的映射关系。很明显 Fresh 实现了 约定式路由 的功能,跟 Next.js 类似。

深入解读新一代全栈框架 Fresh

· 11 min read
加菲猫

📒 玩转 Chrome DevTools,定制自己的调试工具

📒 【第2707期】由 esbuild JavaScript API 看跨语言调用

📒 Go 中闭包的底层原理是

⭐️ 使用 TypeScript 编写 React 的最佳实践!

📒 Webpack 构建优化

https://tsejx.github.io/webpack-guidebook/best-practice/optimization/collection

https://webpack.js.org/guides/code-splitting/

📒 k8s 部署相关

使用 Docker Desktop 搭建 k8s 集群

从Go程序第一行代码,到在 K8s 上运行,要经历多少步

在K8S上的Web服务该怎么做域名解析呢

https://kubernetes.io/zh-cn/docs/concepts/services-networking/ingress/

📒 字节一面:HTTPS 一定安全可靠吗

📒 如何在项目开发中逐步成长

📒 Golang 相关文章

Go 语言里使用 io.Readerio.Writer 两个 interface 来抽象 I/O,他们的定义如下:

type Reader interface {
Read(p []byte) (n int, err error)
}

type Writer interface {
Write(p []byte) (n int, err error)
}

io.Reader 接口代表一个可以从中读取字节流的实体,而 io.Writer 则代表一个可以向其写入字节流的实体。

⭐️ 能ping通,TCP就一定能连通吗

📒 Linux 是如何收发网络包的

📒 Golang 依赖注入相关文章

https://dev.to/dryrainbow/dependency-injection-in-golang-35oa

https://medium.com/avenue-tech/dependency-injection-in-go-35293ef7b6

https://medium.com/thirdfort/go-best-practices-how-to-code-comfortably-60118a27def8

https://levelup.gitconnected.com/better-error-handling-in-golang-theory-and-practical-tips-758b90d3f6b4

📒 【第2704期】网易严选多端组件库OSSA正式开源

📒 Golang 相关文章

Go 语言 Web 编程

Go 语言并发编程

Go 微服务实战

📒 React 18 新特性:Selective Hydration

https://github.com/reactwg/react-18/discussions/37

📒 一个简洁、强大、可扩展的前端项目架构是什么样的

📒 大家都能看得懂的源码之ahooks useInfiniteScroll

📒 esModuleInterop 是如何影响 tsc 的

📒 【第2703期】软件架构手册

📒 如何在 Web 上构建一个插件系统

📒 Go 的常量为什么只支持数字、字符串这样的基础类型

📒 两万字详解!InnoDB锁专题!

📒 细数线程池的10个坑

📒 Chrome DevTools 远程调试安卓网页的原理

📒 Windows 滚动条如何美化成 macOS 那样

📒 Go逃逸分析详解

📒 GO面试必知必会面试题

📒 最简单的单例模式,Go版本的实现你写对了吗

📒 伙计,Go项目怎么使用枚举

📒 Astro 1.0 正式发布,给前端带来了什么

📒 gum

迷人的 shell 脚本工具库,基于 go。

https://github.com/charmbracelet/gum

📒 React Query 转 RTK

本文是作者和 React Query 斗争了一年之后,最终弃 React Query 投 RTK 的故事。

React Query 处理了复杂的缓存,并根据需要重新加载数据。用户使用 useQuery 调用 API 时,数据被存储在 React Query 的缓存中。缓存的数据可通过 getQueryData 读取。如果要更新数据,使用 useMutation 会更新服务群上的数据,同时在 onSuccess 或 onMutate 中更新 React Query 缓存中的数据。更新的最简单方法是调用 invalidateQueries。而缓存失效正是作者觉得 React Query 难以驾驭的主要原因。

然后作者在对比之后选择了 Redux + Redux Toolkit(RTK)。作者希望选择全局 store 的数据流,同时还对比了竞品比如 Valtio(见上图)。Redux 的优点是社区、文档、明确的数据结构,缺点是更多脚手架代码、需要用 reselect 或其他来做渲染优化、尺寸比较大(18K)。

编者注:如果我是作者,在需要全局 store 的前提下,会更倾向于选择 valtio 或 zustand。1)脚手架代码少,这一点对外来说很关键,2)性能更好(valtio 基于使用决定 re-render),3)redux 的单 store 在未来可能成为瓶颈,比如要做组件提取时,多 store 会更灵活一些。

https://www.basedash.com/blog/why-we-had-to-move-away-from-react-query

📒 Suspense

作者在一年前写了第一篇,这是第二篇。时隔一年,这篇是他几个月的研究成果。

这段时间发生了什么?1)React 团队创建 React 18 工作小组讨论区,提供更多深入的信息,同时用于收集反馈,2)React 18 稳定版发布,3)一篇 React Labs 的帖子,介绍 React 团队正在探索的内容。

React 18 有啥新特性?1)没有并发模式,取而代之的是并发功能,仅在子树中启用,原因是为了向后兼容,2)Transition 允许将不紧急的更新标记为过渡(未来可能成为默认行为),3)基于 Suspense 的 Streaming SSR 允许在所有 HTML 被渲染之前进行流化,并且在 HTML 被完全流化之前就可以开始水化(目前需搭配 React.lazy 使用),4)选择性水化,5)新 Hooks,比如 useId 可用来生成在客户端和服务端稳定的 ID,6)自动批处理 让多个 setState 只导致单一渲染。

React 下一步做什么?1)Cache 组件,允许请求库做与 Suspense + 并发渲染兼容的数据缓存,这是 Suspense 目前缺的一环,2)React Server Component,3)用于 Assets 资源加载的 Suspense,比如字体、CSS 和字体等会在加载时导致布局偏移和混乱,4)React 编译器优化,React 慢的原因之一是因为有大量不必要的 re-render,自动插入 memo hook 可以提升性能,这在 React Conf 2021 中有过介绍,5)SuspenseList,在处理 Suspense 列表时有用,比如文章、评论或消息,允许协调子节点,决定他们显示的顺序,6)Offscreen API,允许保留 unmount 组件的状态,或者预先渲染用户可能会执行的 transition,比如做基于路由的预渲染。

跳出 React 从整体社区角度看。1)流式服务端渲染是为了提高响应速度,所以尽可能早地 flush,MarkoJS 在 2014 年开始支持,更早一些 Facebook 在 2009 年也有一门叫 BigPipe 的技术,2)SSR 在网络方面会更快,但同步水化依旧会成为大型项目的性能瓶颈,Islands architecture(群岛架构) 可以解这个问题,3)细粒度响应式,「React 虽然叫 react,但实际上不是 react」,响应式是框架中很流行的一种优化方法,比如 SolidJS,React 团队也有考虑过这个问题,但选择不追求,Dan 早在 2019 年就写过这个话题,4)Transition API 不算新技术,谷歌地图也有类似实现,同时 Scheduling(调度)问题正在通过标准的方式解决,5)SRC(服务端渲染组件)也不是新想法,2018 年就有 Phoenix LiveView 的实现,通过服务端状态和渲染实现 0 JS 的交互功能。

最后,作者还发现,React 项目内部子项目代号均是以 F 开头。比如 Fiber 是实现了异步渲染的重写的核心代号;Fizz 是新的服务器端渲染架构的代号、Flight 是服务器组件的代号、Fire、Flare 等。

https://blog.6nok.org/the-suspense-is-killing-me:-part-2/

📒 React TypeScript 备忘录

一份很全面的 React TypeScript 备忘录。

https://react-typescript-cheatsheet.netlify.app/

📒 React re-render 指南

关于 React 重渲染的系列指南,图文并茂,同时提供了代码示例和扩展资料。

https://www.developerway.com/posts/react-re-renders-guide

📒 ESLint 推出新的配置系统

回顾当前的 eslintrc 配置系统演进史,每一步的演化在当时来看都是不错的选择,比如 extends、Personal configs、多种配置文件格式、可共享的配置和依赖项(npm 背锅)、root、overrides、添加 extends 到 overrides 等等。

然而时至今日,随着 JavaScript 项目越来越庞大,从整体上再来看这些配置就太复杂了。为了简化配置,ESLint 团队经过 18 个月的修订和讨论,决定着手构建一个全新的配置系统 flat config,现在可以在 ESLint v8.21.0 中通过 API 使用。

https://eslint.org/blog/2022/08/new-config-system-part-1/

https://eslint.org/blog/2022/08/new-config-system-part-2/

https://eslint.org/blog/2022/08/new-config-system-part-3/

📒 字节一面:TCP 和 UDP 可以使用同一个端口吗

· 9 min read
加菲猫

📒 将微前端做到极致-无界方案

📒 【第2701期】技术Leader如何创造业务价值

📒 Monorepo 下的模块包设计实践

📒 【TypeScript】never 和 unknown 的优雅之道

📒 Bundle-less 的思考和实践分享

📒 React Fiber架构原理剖析

📒 【万字】优化Webpack?肘,跟我进屋聊聊

📒 字节的前端监控 SDK 是怎样设计的

📒 别再乱打日志了,这份 Java 日志规范,应有尽有,建议收藏!

📒 explain | 索引优化的这把绝世好剑,你真的会用吗

📒 这11条接口性能优化技巧,利好每日睡眠

📒 顺丰快递:请签收MySQL灵魂十连

📒 Go Mod小知识:伪版本

📒 Go ORM 单元测试全流程讲解

📒 超全总结:Go语言如何操作文件

📒 React 服务端渲染遇到的问题

在服务端渲染场景下,不能使用 style-loader,需要用 isomorphic-style-loader 注入样式。

style-loader 内部使用了 dom API 把样式注入到 style 标签中,在 Node 环境下会报错

React 期望在服务端和客户端渲染的内容是相同的,客户端渲染会默认复用服务端渲染的 dom。如果需要在服务端和客户端上渲染不同的内容,可以设置一个 isClient 变量:

class MyComponent extends React.PureComponent {
state = {
isClient: false,
}

componentDidMount() {
// 这里在客户端 hydrate 的时候执行
this.setState({ isClient: true });
}

render() {
const { isClient } = this.state;

if (!isClient) {
// 当需要渲染的组件需要访问浏览器 API,在 Node 环境会报错
// 这里可以渲染 fallback 的内容
return ...
}

// 在客户端可以正常渲染组件
return ...
}
}
tip

当需要渲染的组件需要访问浏览器 API(例如 windowdocumentlocation),在 Node 环境会报错。在服务端渲染的时候,可以先渲染 fallback 内容,在客户端 hydrate 的时候,再渲染正常组件。

另外有一些 SDK,内部可能也访问了浏览器 API,这种情况下不能直接在构造器中初始化实例,可以在 componentDidMount 钩子中延迟初始化。

https://17.reactjs.org/docs/react-dom.html#hydrate

📒 vite 代码压缩遇到的问题

vite 默认使用 esbuild 压缩,esbuild 不仅会做常规的压缩,而且还会在 target 配置允许的范围内做一些语法转换,进一步减小 bundle 体积。

例如在业务中有下面一段代码:

try {
// ...
} catch (err) {
// 这边没有用到 err 参数
return false;
}

经过 esbuild 压缩之后,try...catch 后面的括号直接不见了(这实际上是 ES2019 中的 optional-catch-binding 语法),在一个老业务工程构建的时候,Babel 无法识别这种语法,直接报错了:

try {
// ...
} catch {
return false;
}

有两种解决方案:

  • 一种是使用 terser 压缩,设置 minify: "terser"
  • 另一种继续使用 esbuild,但是手动设置 target: "es2015"

https://vitejs.dev/config/build-options.html#build-target

tip

在一般前端项目中,target 配置是针对 Babel 的,即最终的产物兼容性由 Babel 决定。

但是在 Vite 中,Babel 只参与部分 esbuild 尚不支持的提案阶段的语法转换,并不决定最终产物兼容性,最终的兼容性由 esbuild 决定。注意 esbuild 默认的 target 值为 "esnest",即 esbuild 认为环境支持最新的 JS 语法特性。但是在 Vite 中,build.target 默认为一个特殊值 "modules"(即支持原生 ES Module、动态导入语法和 import.meta 语法,对应 Chrome >=87),最低可以支持 "es2015"

注意 Vite 默认只做语法转换,并不会引入 polyfill(适合第三方库开发,由业务工程 @babel/preset-env 配置 useBuiltIns: "entry" 统一引入 polyfill)。如果产物需要直接在浏览器中运行,则需要 @vitejs/plugin-legacy 插件。该插件会对最终 bundle 中每个 chunk,使用 @babel/preset-env 转换生成对应的 legacy chunk,同时根据 target 配置的目标浏览器兼容性和实际用到的 API,生成一个 polyfill chunk。

https://vitejs.dev/guide/build.html#browser-compatibility

📒 Golang 标准库 strings

重点看一下 strings.Builder 的用法。

Go 学习之 strings.Builder 篇

Go 字符串拼接6种,最快的方式 -- strings.builder

Go拼接字串的三种方法 Go1.10中的strings.Builder

https://pkg.go.dev/strings@go1.19

📒 你需要知道的TypeScript高级类型

📒 Go 的时间转换和时区校对总记不住?给你一份备忘单

📒 两个真实线上升级故障让你彻底搞懂package.json中的脱字符(^)

📒 【第2696期】React 状态管理的新浪潮

📒 Go 创始人教你如何处理错误

⭐️ 总监又来了,人狠话不多,这篇 gRPC,小弟佩服!

📒 React 性能优化, 从 500 毫秒到 1.7 毫秒

https://orizens.com/blog/500ms-to-1-7ms-in-react-a-journey-and-a%20checklist/

📒 UMD 的包如何导出 TS 类型

📒 为什么 React 的 Diff 算法不采用 Vue 的双端对比算法

📒 🚀Turborepo:发布当月就激增 3.8k Star,这款超神的新兴 Monorepo 方案,你不打算尝试下吗

📒 搞清楚 Go Mod的版本和伪版本,下次别乱用了

📒 掌握 TypeScript 中的映射类型

📒 React Re-render 指南

什么是 re-render?什么是必要的 re-render 和不必要的 re-render?如果你对这些问题还模模糊糊的,在这篇文章中可以找到答案。

有四个原因可以让一个组件重新渲染自己:状态变化、父级(或子级)重新渲染、上下文变化和 hooks 变化。

通过 composition(组合)可以避免 re-render。有几种方式,1)把状态下移,避免上层组件 re-render,让 re-render 保持在一个很小的范围之内,2)children as props,作为 props,子组件不会受 state 变更的影响,3)component as props,避免 component re-render。

通过 React.memo 可以避免 re-render,被 memo 的组件只在 props 变更时会 re-render。有几种方式,1)对于带 props 的 component,需对非原始值的 props 做 memo,2)components as props or children,对子组件做 memo 而不要对父组件做 memo。

什么时候应该用 useMemo/useCallback?1)React.memo 过的组件的 props 应该用,因为他们只有 props 变更时才会 re-render,所以反之非 React.memo 过的组件的 props 无需使用,因为都会 re-render,用了也白用,2)useEffect、useMemo、useCallback 中非原始值的依赖应该用,3)重消耗(比如生成渲染树)的部分应该用,反之轻消耗不要用,因为 useMemo/useCallback 本身也有消耗。

如何防止 Context 引起的 re-render?1)memo context value,避免父级组件 re-render 导致 value 变更从而让依赖 context 的地方全部 re-render,2)拆分 data 和 API(getter 和 setter),这样 getter 变更时依赖 setter 的部分不会 re-render,3)把数据拆小,4)使用 context selector 比如 use-context-selector。

https://www.developerway.com/posts/react-re-renders-guide

📒 明明加了唯一索引,为什么还是产生重复数据

📒 现代前端测试框架 Vitest 的一些落地实践感悟

📒 如何用 Project Reference 优化 tsc 编译性能