Skip to content
0

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.com

nijia-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!
  1. 通过 Volume 可以创建多个独立的卷
  2. 建议使用 memfs 而不是过时的 webpack/memory-fs
  3. 可以通过 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')); // 3

isomorphic-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 模拟库

  1. 生成 public 目录下生成 mockServiceWorker.js,确保服务能通过 /mockServiceWorker.js 访问到该资源

$ npx msw init public/

  1. 编写模拟接口的返回数据,将如下的脚本的 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()

Released under the MIT License.