Skip to content

常见的问题及解决方案

前端下载

Javascript 将{ “name”: “download” }转为 download.json 文件并下载

  1. 利用 a 标签 + DataURI
const data = { name: "download" };
const a = document.createElement("a");
a.download = "download.json";
a.href = `data:plain/text;base64,${btoa(JSON.stringify(data))}`; // base64或者URL编码都行
const ev = document.createEvent("MouseEvents");
ev.initMouseEvent("click");
a.dispatchEvent(ev);
  1. 利用 a 标签 + ObjectURL
const data = { name: "download" };
const a = document.createElement("a");
a.download = "download.json";
a.innerHTML = "Download";
a.href = URL.createObjectURL(
new Blob([JSON.stringify(data)], { type: "plain/text" })
);
const ev = document.createEvent("MouseEvents");
ev.initMouseEvent("click");
a.dispatchEvent(ev);

PC、移动端公用域名,但不同代码时,避免错误缓存

  1. HTTP 响应头 Vary :Vary 为服务器返回的头部,表示在内容协商算法时,应该使用哪些头部信息
Vary: * // 所以请求视为唯一,但是最好使用的 Cache-Control: no-store
Vary: <header-name>, <header-name>, ...
Vary: user-agent // 可以用来区分移动端还是PC端

Office Excel 打开 *.csv 文件乱码

对于*.csv 文件,office excel 打开时,需要一个元信息来说明他的 BOM(Byte Order Mark),如果不存在这个元信息,office excel 会可能以非 unicode 编码打开,从而导致乱

**修复:**添加元信息 \uFEFF,标识为 unicode 小端序

BOM 百度百科

Typescript paths 在 *.js 文件中无法使用

tsc 命令在运行后,未对 paths 内的路径进行还原,导致在运行*.js 文件时,无法识别路径

修复:

  1. hack require:在*.js 文件加载时,去识别路径,例如使用 module-alias
/**
* 启动脚本 启动之前需要加载或者执行的程序
*/
import { addAliases } from "module-alias";
const cwd = process.cwd();
addAliases({
"@Src": `${cwd}/dist`,
"@App": `${cwd}/dist/app`,
"@Helper": `${cwd}/dist/helper`,
"@Common": `${cwd}/dist/common`,
"@Log": `${cwd}/dist/common/module/log`,
"@Config": `${cwd}/dist/common/module/config`,
"@Protocol": `${cwd}/protocol-buffers`,
"@Public": `${cwd}+/public`,
});
  1. hack path:在 tsc 时,标明完整路径,例如使用 tsconfig-paths
import tsConfigPaths = require("tsconfig-paths");
import { readFileSync } from "fs";
import { join } from "path";
const tsconfig = JSON.parse(
readFileSync(join(process.cwd(), "tsconfig.json")).toString()
);
const { paths, baseUrl } = tsconfig.compilerOptions;
const cleanup = tsConfigPaths.register({
baseUrl,
paths,
});
cleanup();

前端图片压缩

前端实现图片压缩非根据压缩算法,而是通过 Canvas API 合理的减少图片质量裁剪尺寸,以减少体积

  1. 利用 FileReader API 读取文图片
  2. 利用 Canvas API 绘制图片
  3. 再利用 toDataURL、toBlob 压缩图片

canvas 图片跨域

使用 canvas 时,如下情况

/*@types {HTMLCanvasElement}*/
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const img = new Image();
// img.crossOrigin = '*'; 解决跨域的问题
img.src = "https://t7.baidu.com/it/u=2880994680,1520946052&fm=193&f=GIF";
img.onload = function () {
ctx.drawImage(img, 0, 0);
canvas.toDataURL("image/png"); // 生成base64
};

在使用 toDataURL、getImageData 是会存在跨域问题,生成 URL 会报错,使用 img.crossOrigin = '*'** ** 解决跨域的问题

node.js GRPC 常见问题

  1. 客户端、服务端读取 metadata
  2. 客户端、服务端发送 metadata
  3. 拦截器
import { loadSync } from "@grpc/proto-loader";
import {
loadPackageDefinition,
credentials,
Metadata,
StatusObject,
InterceptingCall,
Listener,
} from "grpc";
const PB_ROOT_PATH = "";
const PB_PATH = "";
const pb = loadSync(PB_PATH, {
keepCase: true,
includeDirs: [PB_ROOT_PATH],
});
const Service = (loadPackageDefinition(pb).$PB_PACKAGE_NAME as any)
.$PB_SERVICE_NAME;
const client = new Service($SERVER_ADDRESS, credentials.createInsecure(), {
interceptors: [interceptor],
});
// 拦截器
function interceptor(options: any, next: any) {
return new InterceptingCall(next(options), {
start(metadata: Metadata, listener: Listener, next: Function) {
// 可以修改请求值的metadata统一拦截
metadata.add("Trace-Id", "12345");
// 可以对返回值进行拦截
next(metadata, {
onReceiveMetadata(metadata: Metadata, next: Function) {
console.log("receive metadata", metadata);
next(metadata);
},
onReceiveMessage(message: any, next: Function) {
console.log("receive message", message);
next(message);
},
onReceiveStatus(status: StatusObject, next: Function) {
console.log("receive status", status);
next(status);
},
});
},
});
}
const mt = new Metadata();
mt.add("trace-id", "123");
const res = client.TransText({ text: "岳智明" }, mt, (err: any, res: any) => {
console.log(err);
console.log(res);
});

node.js 获取在线人数

使用 web socket 实时记录

Server

const http = require("http");
const socketIo = require("socket.io");
let userCount = 0;
const server = http.createServer();
const io = socketIo(server, {
cors: {
origin: "*",
},
});
io.on("connection", (client) => {
userCount++;
console.log("人数增加了一个,总计:", userCount);
console.log(Object.keys(io.engine.clients).length); // 或者不采用计数,直接获取客户端的连接数
io.emit("user_count_change", userCount);
client.on("disconnect", () => {
userCount--;
console.log("人数减少了一个,总计:", userCount);
io.emit("user_count_change", userCount);
});
});
server.listen(3001);

Client

// 记得引入 socket.io
const socket = io("localhost:3001");
socket.on("connect", function () {
console.log("连接服务端,开始计数");
});
socket.on("user_count_change", (count) => {
//
});

使用数据库实时记录,例如 redis

redis 记录当前有效的 session-id,实时扫描即可获得在线人数

pm2 报错

Error: spawn E2BIG

pm2 以 cluster 模式启动时,如果环境过多,可能会导致 spawn 报错 E2BIG(表示参数过长),导致报错

解决方法

  1. 删除无用的环境变量
  2. 使用 fork 模式
  3. 不使用 pm2,直接对 node.js 进行启动