Browser
浏览器相关的库
peerjs ---> 使用 WebRTC 的点对点通信
可以用来做非常多的事情,本站点用了它做了一个展示网站在线人数的功能,参考 commit
const conn = peer.connect("another-peers-id");
conn.on("open", () => {
conn.send("hi!");
});peer.on("connection", (conn) => {
conn.on("data", (data) => {
// Will print 'hi!'
console.log(data);
});
conn.on("open", () => {
conn.send("hello!");
});
});threads.js ---> WebWorker 库
使 web workers 和 worker threads 像函数调用一样简单
import { spawn, Thread, Worker } from "threads"
const auth = await spawn(new Worker("./workers/auth"))
const hashed = await auth.hashPassword("Super secret password", "1234")
console.log("Hashed password:", hashed)
await Thread.terminate(auth)import sha256 from "js-sha256"
import { expose } from "threads/worker"
expose({
hashPassword(password, salt) {
return sha256(password + salt)
}
})nativefier ---> Make App
通过 electron 将任何网站打包成桌面应用
$ npm install -g nativefier
$ nativefier https://twitter.comnijia-keys ---> 为网站添加快捷键
为网站添加快捷键
<template>
<ninja-keys
@selected="selected"
@change="change"
:data="hotkeys"
></ninja-keys>
</template>
<script setup>
import "ninja-keys";
// data etc..
</script>broz ---> 简易webview浏览器
原理是通过 electron 打开一个webview,然后载入对应的网页
$ npx broz antfu.me
Tone.js ---> 用于在浏览器中制作交互式音乐的 Web 音频框架
const synth = new Tone.Synth().toDestination();
const now = Tone.now()
synth.triggerAttackRelease("C4", "8n", now)
synth.triggerAttackRelease("E4", "8n", now + 0.5)
synth.triggerAttackRelease("G4", "8n", now + 1)sanitizi-html ---> 验证属于白名单的html元素
验证html元素或者属性的结构是否是预期的,或者转化为预期的结构
const dirty = 'some really tacky HTML';
const clean = sanitizeHtml(dirty, {
allowedTags: [ 'b', 'i', 'em', 'strong', 'a' ],
allowedAttributes: {
'a': [ 'href' ]
},
allowedIframeHostnames: ['www.youtube.com'],
allowedStyles: {
'*': {
// Match HEX and RGB
'color': [/^#(0x)?[0-9a-f]+$/i, /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/],
'text-align': [/^left$/, /^right$/, /^center$/],
// Match any number with px, em, or %
'font-size': [/^\d+(?:px|em|%)$/]
},
'p': {
'font-size': [/^\d+rem$/]
}
},
transformTags: {
'ol': sanitizeHtml.simpleTransform('ul', {class: 'foo'}),
}
});delegate-it ---> 轻量的事件代理库
假设有如下DOM元素:
<div id="app">
<p></p>
<p></p>
<p></p>
<!-- ... -->
<div>当我们需要在p上触发一个事件,显然由于p元素过多,性能不佳。因此可以将事件代理在div上:
delegate('#app', 'p', 'click', function(e) {
console.log(e.delegateTarget);
}, false);pica ---> 浏览器图像处理库
pica.resize(from, to)
.then(result => console.log('resize done!'));
// Resize & convert to blob
pica.resize(from, to)
.then(result => pica.toBlob(result, 'image/jpeg', 0.90))
.then(blob => console.log('resized to canvas & created blob!'));rrweb ---> 记录和回放
记录用户的操作,同时生成 canvas 回放,相较于录屏,有着更高的精度和灵活性
let events = [];
rrweb.record({
emit(event) {
// push event into the events array
events.push(event);
},
});
// this function will send events to the backend and reset the events array
function save() {
const body = JSON.stringify({ events });
events = [];
fetch('http://YOUR_BACKEND_API', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body,
});
}
// save events every 10 seconds
setInterval(save, 10 * 1000);memfs ---> 内存中虚拟 fs 文件系统
import { fs } from 'memfs';
fs.writeFileSync('/hello.txt', 'World!');
fs.readFileSync('/hello.txt', 'utf8'); // World!- 通过
Volume可以创建多个独立的卷 - 建议使用
memfs而不是过时的 webpack/memory-fs - 可以通过 streamich/unionfs 将多个虚拟 fs 联合在一起
import * as fs from 'fs';
import { Volume } from 'memfs';
import * as MemoryFileSystem from 'memory-fs';
import { ufs } from 'unionfs';
const vol1 = Volume.fromJSON({ '/memfs-1': '1' });
const vol2 = Volume.fromJSON({ '/memfs-2': '2' });
const memoryFs = new MemoryFileSystem();
memoryFs.writeFileSync('/memory-fs', '3');
ufs.use(fs).use(vol1).use(vol2).use(memoryFs);
console.log(ufs.readFileSync('/memfs-1', 'utf8')); // 1
console.log(ufs.readFileSync('/memfs-2', 'utf8')); // 2
console.log(ufs.readFileSync('/memory-fs', 'utf8')); // 3isomorphic-git ---> 浏览器环境的 git
<script src="https://unpkg.com/@isomorphic-git/lightning-fs"></script>
<script src="https://unpkg.com/isomorphic-git"></script>
<script type="module">
import http from 'https://unpkg.com/isomorphic-git@beta/http/web/index.js'
const fs = new LightningFS('fs')
const pfs = fs.promises
const dir = '/test-clone'
git.clone({ fs, http, dir, url: 'https://github.com/isomorphic-git/lightning-fs', corsProxy: 'https://cors.isomorphic-git.org' }).then(() => {
return pfs.readdir(dir)
}).then(console.log)
//=> ['.git', 'src', '.gitattributes', '.gitignore', '.prettierrc', '.releaserc', 'LICENSE', 'index.d.ts', 'README.md', 'azure-pipelines.yml', 'package.json', 'webpack.config.js', 'karma.conf.js', 'package-lock.json']
</script>browserfs ---> 浏览器环境模拟 fs
import { configure, BFSRequire } from 'browserfs';
// you can also add a callback as the last parameter instead of using promises
await configure({ fs: 'LocalStorage' });
const fs = BFSRequire('fs');
// Now, you can write code like this:
fs.writeFile('/test.txt', 'Cool, I can do this in the browser!', function(err) {
fs.readFile('/test.txt', function(err, contents) {
console.log(contents.toString());
});
});可以选择将数据存储不同的地方
import { configure, BFSRequire } from 'browserfs';
import Buffer from 'buffer';
const zipData = await (await fetch('mydata.zip')).arrayBuffer();
await configure({
fs: 'MountableFileSystem',
options: {
'/mnt/zip': {
fs: 'ZipFS',
options: {
zipData: Buffer.from(zipData)
}
},
'/tmp': { fs: 'InMemory' },
'/home': { fs: 'IndexedDB' }
}
};darkreader ---> Dark Reader for Browser
是一个 chrome 插件,可以让自动修改网页 CSS,支持暗黑模式,同时也提供了 API 用法:
import {
enable as enableDarkMode,
disable as disableDarkMode,
auto as followSystemColorScheme,
exportGeneratedCSS as collectCSS,
isEnabled as isDarkReaderEnabled
} from 'darkreader';
enableDarkMode({
brightness: 100,
contrast: 90,
sepia: 10,
});
disableDarkMode();
followSystemColorScheme();
const CSS = await collectCSS();
const isEnabled = isDarkReaderEnabled();效果很不错,有时间可以研究下是如何实现的
chii ---> 远程 debug 工具
调试远程的 DOM 元素和查看 console 输出
启动服务
$ npm i -g chii
$ chii start -p 8080插入脚本
<script src="//host-machine-ip:8080/target.js"></script>dom-chef ---> 使用JSX构建DOM
使用需要进行配置
import {h} from 'dom-chef';
const handleClick = e => {
// <button> was clicked
};
const el = (
<div className="header">
<button className="btn-link" onClick={handleClick}>
Download
</button>
</div>
);
document.body.appendChild(el);const plugins = [
[
'@babel/plugin-transform-react-jsx',
{
pragma: 'h',
pragmaFrag: 'DocumentFragment',
},
],
];{
"compilerOptions": {
"jsxFactory": "h",
"jsxFragmentFactory": "DocumentFragment"
}
}当然,也可以默认导出React的方式来配置
import React from 'dom-chef';dom-to-png ---> 从 DOM 生成图片
var node = document.getElementById('my-node');
domtoimage.toPng(node)
.then(function (dataUrl) {
var img = new Image();
img.src = dataUrl;
document.body.appendChild(img);
})
.catch(function (error) {
console.error('oops, something went wrong!', error);
});fingerprint ---> 精确的设备识别
在浏览器上运行,为每个访问设备确定唯一 ID。提高通过硬件设备识别用户的精确度是一直是难题
<script>
// Initialize the agent at application startup.
const fpPromise = import('https://fpcdn.io/v3/Se2pjBuE0r9EkhEsfaY7')
.then(FingerprintJS => FingerprintJS.load())
// Get the visitor identifier when you need it.
fpPromise
.then(fp => fp.get())
.then(result => {
// This is the visitor identifier:
const visitorId = result.visitorId
console.log(visitorId)
})
</script>barba ---> 页面过度动画
barba.init({
transitions: [{
name: 'opacity-transition',
leave(data) {
return gsap.to(data.current.container, {
opacity: 0
});
},
enter(data) {
return gsap.from(data.next.container, {
opacity: 0
});
}
}]
});partytown ---> Web Worker
将 CPU 密集型的工作转移到 web worker 中
- <script>...</script>
+ <script type="text/partytown">...</script>msw ---> API Mock 服务
用于浏览器和 NodeJs 的无缝 REST/GraphQL API 模拟库
- 生成 public 目录下生成
mockServiceWorker.js,确保服务能通过/mockServiceWorker.js访问到该资源
$ npx msw init public/
- 编写模拟接口的返回数据,将如下的脚本的
worker.start()尽早在前端页面内运行
// src/mocks.js
// 1. Import the library.
import { http, HttpResponse } from 'msw'
import { setupWorker } from 'msw/browser'
// 2. Describe network behavior with request handlers.
const worker = setupWorker(
http.get('https://github.com/octocat', ({ request, params, cookies }) => {
return HttpResponse.json(
{
message: 'Mocked response',
},
{
status: 202,
statusText: 'Mocked status',
},
)
}),
)
// 3. Start request interception by starting the Service Worker.
worker.start()