📒 React 18 RC 版本发布啦,生产环境用起来!
安装最新的 React 18 RC 版本(Release Candidate候选版本):
$ yarn add react@rc react-dom@rc
注意在 React 18 中新增了 concurrent Mode
模式,通过新增的 createRoot
API 开启:
import ReactDOM from 'react-dom'
// 通过 createRoot 创建 root
const root = ReactDOM.createRoot(document.getElementById('app'))
// 调用 root 的 render 方法
root.render(<App/>)
startTransition
特性依赖concurrent Mode
模式运行
如果使用传统 legacy 模式,会按 React 17 的方式运行:
import ReactDOM from 'react-dom'
// 通过 ReactDOM.render
ReactDOM.render(
<App />,
document.getElementById('app')
)
React 18 主要是对自动批处理进行优化。在 React 18 之前实际上已经有批处理机制,但是只针对同步代码,如果放在 Promise
、setTimeout
等异步回调中,自动批处理会失效。
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
// 自动批处理更新
// 注意此时 setState 是异步的
this.setState({val: this.state.val + 1});
console.log(this.state.val);
this.setState({val: this.state.val + 1});
console.log(this.state.val);
setTimeout(() => {
// 自动批处理失效
// 此时 setState 是同步的
this.setState({val: this.state.val + 1});
console.log(this.state.val);
this.setState({val: this.state.val + 1};
console.log(this.state.val);
}, 0);
}
};
在 React 18 版本之前,上面代码的打印顺序是 0、0、2、3
React 18 版本解决了这个问题,在异步回调中更新状态也能触发自动批处理,打印的顺序是 0、0、1、1
总结一下主要有以下几个新特性:
- 新的
ReactDOM.createRoot()
API(替换ReactDOM.render()
) - 新的
startTransition
API(用于非紧急状态更新) - 渲染的自动批处理优化(主要解决异步回调中无法批处理的问题)
- 支持
React.lazy
的 全新 SSR 架构(支持<Suspense>
组件)
📒 CSS TreeShaking 原理揭秘: 手写一个 PurgeCss
📒 「源码解析」一文吃透react-redux源码(useMemo经典源码级案例)
📒 WebSocket 基础与应用系列(一)—— 抓个 WebSocket 的包
HTTP 和 WebSocket 都属于应用层协议,都是基于 TCP 来传输数据的,可以理解为对 TCP 的封装,都要遵循 TCP 的三次握手和四次挥手,只是在连接之后发送的内容(报文格式)不同,或者是断开的时间不同。
如何使用 Wireshark 抓包:
- 在 Capture 中选择本机回环网络
- 在 filter 中写入过滤条件 tcp.port == 3000
WebSocket 基础与应用系列(一)—— 抓个 WebSocket 的包
📒 反向操作,用 Object.defineProperty 重写 @vue/reactivity
📒 antfu 大佬的 eslint 配置
📒 antfu 大佬的 vscode 配置
📒 使用 tsdoc 编写规范的注释
📒 npm 包发布工具
📒 使用 pnpm 作为包管理工具
基本用法:
pnpm add <pkg>
:安装依赖pnpm add -D <pkg>
:安装依赖到 devDependenciespnpm install
:安装所有依赖pnpm -r update
:递归更新每个包的依赖pnpm -r update typescript@latest
:将每个包的 typescript 更新为最新版本pnpm remove
:移除依赖
如何支持 monorepo 项目:https://pnpm.io/zh/workspaces
pnpm -r
带一个参数 -r
表示进行递归操作。
📒 推荐两个打包工具
📒 seedrandom:JS 种子随机数生成器
种子随机数生成器,生成是随机的,但是每次调用生成的值是固定的:
const seedrandom = require('seedrandom');
const rng = seedrandom('hello.');
console.log(rng()); // 第一次调用总是 0.9282578795792454
console.log(rng()); // 第二次调用总是 0.3752569768646784
📒 深入Node.js的模块加载机制,手写require函数
📒 589. N 叉树的前序遍历 :「递归」&「非递归」&「通用非递归」
📒 Million v1.5:一种快速虚拟 DOM 的实现
专注于性能和大小,压缩后小于 1KB,如果您想要一个抽象的 VDOM 实现,Million 是你构建自己的框架或库时理想的选择
📒 200 行代码使用 React 实现俄罗斯方块
📒 「React 进阶」 学好这些 React 设计模式,能让你的 React 项目飞起来🛫️
📒 「1.9W字总结」一份通俗易懂的 TS 教程,入门 + 实战!
📒 Oclif v2.5:Heroku 开源的 CLI 框架
一个用于构建 CLI 脚手架的成熟框架,无论是简单的参数解析还是很多功能指令都可以驾驭。
📒 使用 Rust 与 WebAssembly 重新实现了 Node 的 URL 解析器
https://www.yagiz.co/implementing-node-js-url-parser-in-webassembly-with-rust/
📒 PDF:从 JavaScript 到 Rust:新书免费发布
https://github.com/vinodotdev/node-to-rust/releases/download/v1/from-javascript-to-rust.pdf
📒 Red Hat 和 IBM 团队的 Node.js “架构参考”
📒 在 Node 环境下使用 execa
运行命令
https://blog.logrocket.com/running-commands-with-execa-in-node-js/
📒 万字长文详解从零搭建企业级 vue3 + vite2+ ts4 框架全过程
📒 10 React Antipatterns to Avoid - Code This, Not That!
📒 markdown 编辑器滚动如何实现联动
const ScrollTarget = {
NONE: "NONE",
EDITOR: "EDITOR",
RENDER: "RENDER",
};
let curTarget = ScrollTarget.NONE;
let timer = null;
const scrollManager = (handler) => (target) => {
if ((curTarget = ScrollTarget.NONE)) {
curTarget = target;
}
if (curTarget === target) {
handler(target);
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
curTarget = ScrollTarget.NONE;
}, 100);
}
};
const scrollFn = scrollManager(handleScroll);
📒 Webpack 的模块规范
Webpack 基于 CJS 和 ESM 规范实现了模块机制,但也不是完全基于,而是在这些模块规范基础上扩展了一套自己的 api,用于增强功能,例如:
require.context
- 使用
import
加载.json
模块
在 ESM 规范中
import
只能用于加载 JS 模块,只有require
可以加载 json 模块
📒 如何将对象序列化为查询字符串
const aaa = {a: 1, b: 2, c: "2333"}
第一种手动拼接,简单直接,一行代码搞定:
const _stringify =
(obj) => Object.entries(obj).map(([key, val]) => `${key}=${val}`).join("&");
还可以使用 URLSearchParams
对象进行拼接:
const _stringify = obj => Object.entries(obj).reduce((accu, [key, val]) => {
accu.append(key, val);
return accu;
}, new URLSearchParams)
📒 「深入浅出」主流前端框架更新批处理方式
浏览器环境下,宏任务的执行并不会影响到浏览器的渲染和响应,即宏任务优先级低于页面渲染。
function run(){
setTimeout(() => {
console.log('----宏任务执行----')
run()
}, 0)
}
// 通过递归调用 run 函数,让 setTimeout 宏任务反复执行
// 这种情况下 setTimeout 执行并不影响页面渲染和交互事件
run()
微任务会在当前 event loop 中执行完毕,会阻塞浏览器的渲染和响应,即微任务优先级高于页面渲染。
function run(){
Promise.resolve().then(() => {
run()
})
}
// 在这种情况下,页面直接卡死了,没有响应
run()
这里主要就是理解关键渲染路径,即浏览器渲染一帧会先执行脚本,再页面布局,绘制渲染。如果是宏任务,浏览器会把每一次事件回调放在下一帧渲染前执行,这样可以确保浏览器每一帧都能正常渲染。如果是微任务,浏览器在执行渲染之前会清空微任务队列,会导致一直停留在当前 event loop,相当于脚本一直在执行,如果长时间不把控制权交还给浏览器,就会影响下一帧的渲染,导致页面出现卡顿和事件响应不及时。