实现一个真的能跑的JS Bundler
前端的Bundler有很多,比如webpack、parcel、esbuild等,他们的实现非常复杂。但其实如果只看其中的核心:打包JS文件 这个功能来看,想要让他真的能跑起来,并不困难。
本文中实现的Bundler将能做到
打包 JS、TS
生成 web、node产物
正确处理package依赖
代码仓库:https://github.com/fun4wut/toy-bundler
整体流程JS文件 -> Transform -> 查找依赖 ->加入依赖图 -> 生成代码
定义Module其实依赖图的每个节点,将文件的基本信息记录下来。
这里的id为方便,直接用的递增方案
depDict是处理 require时的路径 -> id 的映射。
let globalIdCounter = 0;
export class ModuleNode {
id: number;
/** 绝对路径 */
filePath: string;
ext: string;
code: string;
deps: string[];
depDict:
Comlink分析
源代码地址:https://github.com/GoogleChromeLabs/comlink ;惊艳!
何为ComlinkComlink 是用来解决 web worker通信大量模板代码的一种方案,通过RPC调用的思路来对上层使用者屏蔽通信逻辑
如果不使用 comlink ,我们可能需要这么写。。
// main.js
const worer = new Worker('worker.js');
worker.addEventListener('message', e => {
// Do something...
});
worker.postMessage({
// ...
});
// worker.js
onMessage = (e) => {
if (matchCond1(e)) {
congsole.log('.....');
postMessage('...');
}
if (matchCond2()) {
semver,lockfile与pnpm
原定的公司内部分享,先拿出来也问题不大
Semver细节npm semantic version calculator (npmjs.com)
x.
y.
z.
m
1.
2.
0
-beta.3
Major
Minor
Patch
Prelease tag
版本通配符
Prerelease发新版本之前,给alpha/beta包打上tag
可用tag
alpha
beta
rc
If a version has a prerelease tag (for example, 1.2.3-alpha.3) then it will only be allowed to satisfy comparator sets if at least one comparator with the same [major, minor, patch] tuple also has a prerelease tag.
semver | npm Docs (npmjs.com)
lockfile机制生成与更新策略
若lock文件不存在,安装依赖并生存lock文件
若lock文件
JS中的互斥锁
作为一门单线程语言,JS有锁这种概念吗。诚然,因为JS单线程运行,而我们一般对变量的并发操作都是同步的,但如果是异步场景,那就两说了。
并发问题构造以最经典的 counter 问题为例子,来看下,每个worker都会从server拿取数据,并加到全局的counter上。同时启动100个worker,查看最终的counter值。
const delay = () => new Promise(resolve => setTimeout(resolve, 0))
const fetchFromServer = () => new Promise(resolve => setTimeout(() => resolve(10), Math.random() * 1000))
async function updateObj(patch) {
const cnt = globalObj.counter
await delay()
globalObj.counter = cnt + patch
}
const globalObj = {
count
TS函数柯里化
在公司疯狂摸鱼,忽然想念起前端来,所以写了点类型编程的玩具出来。今天先试着写写柯里化
何为柯里化先简单介绍下什么是柯里化(currying)。意思是将多参数的函数,转化成单一参数的函数。举个例子:
// 定义
const add = (a, b, c) => a + b + c
const curriedAdd = a => b => c => a + b + c
//调用
add(1, 2, 3) //6
curriedAdd(1)(2)(3) //6
可以看到,变为单一参数后,返回的是一个闭包,捕获了第一个参数的值,闭包的返回值也是一个单一参数的函数。
如何实现柯里化我们先不关心类型,来看看JS下的柯里化如何实现
function curry(fn) {
return (fn.length === 1
? fn
: (arg) => curry(fn.bind(null, arg)));
}
这里实现的很简单,使用 bind 创建一个已经预先接受第一个参数的函数,并进行递归即可,当函数参数个数为1时,即 p