Skip to main content

代码片段

JavaScript / TypeScript

在 ShadowDOM 中渲染 React 应用

renderInShadowDOM.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import {StyleSheetManager} from "styled-components";

export function renderInShadowDOM(app: React.ReactNode, options: {
container: HTMLElement,
links?: Array<string>,
styles?: Array<string>
}) {
const container = document.createElement("div");
options.container.appendChild(container);

const shadow = container.attachShadow({mode: "open"});

const styleSlot = document.createElement("section");
shadow.appendChild(styleSlot);

const root = document.createElement("div");
shadow.appendChild(root);

(options.links || []).map(createCssLink).forEach(ele => shadow.appendChild(ele));
(options.styles || []).map(createCssStyle).forEach(ele => shadow.appendChild(ele));

ReactDOM.createRoot(root).render(<StyleSheetManager target={styleSlot}> {app} </StyleSheetManager>);
}

function createCssLink(style: string) {
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = style;
return link;
}

function createCssStyle(style: string) {
const link = document.createElement("style");
link.type = "text/css";
link.textContent = style;
return link;
}

以下是使用示例:

App.tsx
import style from "./style.css?inline";
import App from "./App";
import { renderInShadowDOM } from "./renderInShadowDOM";

renderInShadowDOM(<App />, {
container: container || document.body,
styles: [style.toString()]
});
style.css
@tailwind base;
@tailwind components;
@tailwind utilities;

参考文档: How to render react applications in Shadow DOM with SSR and Style Encapsulation, How to use Tailwind with shadow dom? #1935

获取函数返回类型

函数返回类型
export async function myFunction() {
return Promise.resolve([{hello: "world"}]);
}

export type MyFunctionResultType = ReturnType<typeof myFunction> extends Promise<Array<infer T>> ? T : never; // => {hello: string}

base64 处理

浏览器
/**
* 编码成base64字符串
* 参考文档 [Base64](https://developer.mozilla.org/en-US/docs/Glossary/Base64)
* @author wmm
* @date 2024-02-19
*/
export function encodeToBase64(str: string): string {
return window.btoa(String.fromCodePoint(...new TextEncoder().encode(str)));
}

/**
* 将base64解码
* 参考文档 [Base64](https://developer.mozilla.org/en-US/docs/Glossary/Base64)
* @author wmm
* @date 2024-02-19
*/
export function decodeFromBase64(base64: string): string {
const binString = window.atob(base64);
// @ts-ignore
const data = window.Uint8Array.from(binString, m => m.codePointAt(0));
return new TextDecoder().decode(data);
}

/**
* 用于将读取成base64
*/
export function readAsBase64(data: Blob): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(data);
reader.onload = () => resolve((reader.result as string).split(";base64,")[1]);
reader.onerror = err => reject(err);
});
}

React 表单快捷更新

export type OnChangeUpdate<T> = {
<K extends keyof T>(key: K, value: T[K]): void;
};
表单示例
function render() {
const [form, setForm] = useState({
firstName: "",
lastName: "",
age: 0
});

const update: OnChangeUpdate<typeof form> = (key, value) => {
setForm({...form, [key]: value});
};

return (
<input
type="text"
value={form.firstName}
onChange={e => update("firstName", e.target.value)}
/>
);
}

export type OnChangeUpdate<T> = {
<K extends keyof T>(key: K, value: T[K]): void;
};

解析 HTML/XML

浏览器端执行
export async function parseHtml(url: string, mimeType?: DOMParserSupportedType): Promise<Document> {
const req = await fetch(url);
const text = await response.text();
return new DOMParser().parseFromString(text, mimeType || "text/html");
}

HumanSize/HumanTimer

可读的Size字符串
function humanSize(bytes: number, decimals = 2) {
if (bytes === 0) return "0 Bytes";

const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];

const i = Math.floor(Math.log(bytes) / Math.log(k));

return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
}
将剩余毫秒数转成可读的时分秒格式
function humanMs(ms: number) {
let milliseconds = Math.abs(ms);

const oneSecond = 1000;
const oneMinute = oneSecond * 60;
const oneHour = oneMinute * 60;

const hours = Math.floor(milliseconds / oneHour);
milliseconds -= hours * oneHour;
const minutes = Math.floor(milliseconds / oneMinute);
milliseconds -= minutes * oneMinute;
const seconds = Math.floor(milliseconds / oneSecond);
milliseconds -= seconds * oneSecond;
const leftMs = milliseconds.toFixed(0);

const value = [hours, minutes, seconds].map(val => val.toString().padStart(2, "0")).join(":");

return `${value}.${leftMs.padStart(3, "0")}`;
}