移动端适配

vw/vh + rem 结合动态计算根字体大小

1. rem 配合动态根字体大小

让 1rem 的大小,永远跟着屏幕宽度自动变化

1
2
3
4
5
6
7
8
html {
--screen-width: var(--fallback-screen-width, 100vw);
/* 根字体大小 = 视口宽度 / 3.75 */
font-size: calc(var(--safe-width) / 3.75);
}
body {
font-size: 0.16rem; /* 业务层使用 rem 作为单位 */
}

2. 现代视口单位兼容(dvw/dvh)

  • dvw/dvh(动态视口单位)规避移动端 “刘海屏 / 导航栏” 占用视口的问题;
  • 通过 @supports 做特性检测,仅在支持 dvh 的浏览器中替换为 dvw/dvh,向下兼容不支持的设备(仍用 vw/auto)。
1
2
3
4
5
6
@supports (height: 100dvh) {
html {
--screen-width: 100dvw;
--screen-height: 100dvh;
}
}
阅读全文 »

JSBridge是什么

JSBridge本质就是:Web端和客户端Native之间的通信桥梁,让混合开发模式中的Web端和Native端能够互相通信,实现双向调用。

Web端调用Native端

在Webview中注入JS API

通过WebView提供的接口,App将Native的相关接口注入到JS的Context(window)的对象中
Web端就可以直接在全局 window 下使用这个暴露的全局JS对象,进而调用原生端的方法

Android注入方法:

  • addJavascriptInterface配合@JavascriptInterface:Android 4.2+ 配合 @JavascriptInterface 才安全可用

IOS注入方法:

  • WKWebView + WKScriptMessageHandler 是 iOS 官方方案,推荐新项目优先使用,链路更清晰。
  • WebViewJavascriptBridge 是常见历史封装方案,接入快、兼容旧项目,但初始化握手和调试成本更高。
阅读全文 »

Nginx是什么

Nginx 是一个高性能的 Web 服务器和反向代理服务器
最常见的作用有这几个:

  • 静态资源服务器:直接返回 html/js/css/img
  • 反向代理:把请求转发给后端服务(Node/Java/Python)
  • 负载均衡:把流量分发到多台后端
  • 网关能力:HTTPS 终止、重定向、缓存、压缩(gzip/brotli)、跨域头等

Nginx的配置文件

在 nginx 中,其中比较重要的有以下几个文件,它们都是有层层关联的:

/etc/nginx/nginx.conf
/etc/nginx/conf.d/default.conf

/etc/nginx/nginx.conf

nginx 主配置文件,引用了 /etc/nginx/conf.d/ 目录下的所有配置文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
user  nginx;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;


events {
worker_connections 1024;
}


http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;

#gzip on;

include /etc/nginx/conf.d/*.conf;
}

/etc/nginx/conf.d/default.conf

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

/usr/share/nginx/html

默认的静态资源目录,其目录下的 index.html 就是 nginx 的欢迎页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html {
color-scheme: light dark;
}
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>
If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.
</p>

<p>
For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br />
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.
</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

利用Docker启动Nginx镜像

1
docker run -it --rm -p 9000:80 nginx:alpine

root和index


location

location 用以匹配路由,配置语法如下

location [ = | ~ | * | ^ ] uri { … }

其中 uri 前可提供以下修饰符

  • = 精确匹配,优先级最高。
  • ^~ 前缀匹配,优先级其次。如果同样是前缀匹配,走最长路径。
  • ~ 正则匹配,优先级再次 (~* 只是不区分大小写,不单列)。如果同样是正则匹配,走第一个路径。
  • / 通用匹配,优先级再次。

location修饰符

location优先级


proxy_pass

proxy_pass 反向代理,也就是用来解决跨域的配置

当使用 proxy_pass 代理路径时,有两种情况

1. 转发时 保留 /api 路径

1
2
3
location /api/ {
proxy_pass http://localhost:3000;
}

结尾没有 / 斜杠
前端请求:/api/user –> 转发到后端:http://localhost:3000/api/user

2. 转发时 去掉 /api 路径

1
2
3
location /api/ {
proxy_pass http://localhost:3000/;
}

末尾加 / 斜杠
前端请求:/api/user –> 转发到后端:http://localhost:3000/user

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
server {
listen 80;
server_name localhost;

root /usr/share/nginx/html;
index index.html index.htm;

# 建议使用此种 proxy_pass 不加 URI 的写法,原样路径即可
# http://localhost:8300/api1/hello -> proxy:3000/api1/hello
location /api1 {
# 可通过查看响应头来判断是否成功返回
add_header X-Config A;
proxy_pass http://api:3000;
}

# http://localhost:8300/api2/hello -> proxy:3000/hello
location /api2/ {
add_header X-Config B;
proxy_pass http://api:3000/;
}

# http://localhost:8300/api3/hello -> proxy:3000/hello/hello
location /api3 {
add_header X-Config C;
proxy_pass http://api:3000/hello;
}

# http://localhost:8300/api4/hello -> proxy:3000//hello
location /api4 {
add_header X-Config D;
proxy_pass http://api:3000/;
}
}

add_header

控制响应头。

很多特性都是通过响应头控制,因此基于此指令可做很多事情,比如:

Cache
CORS
HSTS
CSP

Cache

1
2
3
location /static {
add_header Cache-Control max-age=31536000;
}

CORS

1
2
3
location /api {
add_header Access-Control-Allow-Origin *;
}

HSTS

HTTPS,SSL相关

1
2
3
4
5
location / {
listen 443 ssl;

add_header Strict-Transport-Security max-age=7200;
}

CSP

1
2
3
location / {
add_header Content-Security-Policy "default-src 'self';";
}

最佳实践

  • /index.html:304
  • /assets/*.[hash].(js|css|…):一年强缓存 + immutable
  • gzip/brotli 开启
  • SPA try_files 回退
  • redirect、rewrite配置
  • HTTPS + HTTP2 + 基础安全头
  • 发布前 nginx -t,发布后灰度验证

缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# HTML 走协商缓存(每次都向服务端校验)
location ~* \.html?$ {
expires -1; # 不走 Expires 强缓存
add_header Cache-Control "no-cache";
etag on; # 默认一般就是 on,显式写更清晰
if_modified_since exact; # 默认即可,显式写
try_files $uri =404;
}

# JS,CSS走强缓存
location /static/ {
expires 30d;
add_header Cache-Control "public, max-age=2592000, immutable";
try_files $uri =404;
}

接口转发解决跨域

Gzip压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 开启 gzip
gzip on;
gzip_vary on; # 响应头加 Vary: Accept-Encoding,避免代理缓存错配
gzip_proxied any; # 被代理请求也允许压缩
gzip_comp_level 6; # 压缩等级 1-9,6 是常用平衡点
gzip_min_length 1024; # 小于 1KB 不压缩
gzip_buffers 16 8k;
gzip_http_version 1.1;

# 压缩的 MIME 类型(注意 text/html 通常默认已启用)
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
application/rss+xml
image/svg+xml
font/ttf
font/otf
application/vnd.ms-fontobject
application/x-font-ttf
application/font-woff
application/font-woff2;

History路由

1
2
3
location / {
try_files $uri /index.html;
}

Redirect重定向

Redirect(重定向):告诉浏览器“去另一个 URL”,浏览器地址栏会变,状态码通常是 301/302/307/308

使用场景:

  • 域名跳转(a.com -> b.com)
  • 协议跳转(http -> https)
  • 旧链接永久迁移(SEO 友好,用 301/308)
1
2
3
location = /old-path {
return 301 /new-path;
}

Rewrite重写

Rewrite(重写):Nginx 在服务器内部改 URI 去匹配资源,通常不告诉浏览器,地址栏不变

使用场景:

  • SPA 回退(try_files $uri /index.html 本质类似内部重写思路)
  • 历史路径内部兼容映射
  • 隐藏后端真实路径结构
1
2
3
location /old/ {
rewrite ^/old/(.*)$ /new/$1 last;
}

限制访问source-map文件

1
2
3
4
5
6
7
# 禁止访问 sourcemap
location ~* \.map$ {
access_log off;
log_not_found off;
add_header Cache-Control "no-store" always;
return 404;
}

黑白名单配置

Docker是什么

Docker 是一套把应用连同运行环境一起打包、并在任何机器上几乎一致运行的容器技术。

docker

主要解决三类问题:

  1. 环境一致:利用容器技术,减少本地和真实机器运行环境不一致的问题
  2. 交付标准化:发布物从「一堆文件 + 安装步骤」变成「一个镜像 + 一条启动命令」
  3. 隔离与密度:多个docker容器能跑在同一台物理机上,彼此进程级隔离,相比虚拟机更轻量级
阅读全文 »

币圈参与者和生态

  • 长期资金:机构、投行、矿企
  • 风险投资:VC、加速器
  • 交易所与做市商:庄家、抽水、点差(插针,收割散户:以前存在,现在大所基本较少)
  • 量化基金:职业牌手、模型驱动
  • 开发者、项目方:发币者
  • 散户:韭菜

币圈基本构成

现货市场(Spot Market)
  • 一级市场:NFT铸造、土狗、盲盒、空投认购
    参与链上的初始发行环节(容易被套、被骗、资金盘)
  • 二级市场:交易所、DEX买卖
    中心化交易所买币或去中心化交易所链上买卖
衍生品市场(Derivatives Market):合约、期权
  • 合约
  • 期权
阅读全文 »

不要被学术界的思维限制了头脑,不要被程序员的思维限制了想象力。

比特币中的密码学

比特币中的哈希特性

Collision resistance (抗哈希碰撞)

没有高效的方法来人为的制造哈希碰撞

  • Collision resistance的定义:给定X,没有高效的方法找到Y,使得H(X) = H(Y)
  • Collision resistance的特性:无法用数学证明
  • MD5哈希函数:以前认为是Collision resistance,后来被鉴定为不安全的哈希函数,可通过人为的方式制造哈希碰撞
  • 比特币中使用的哈希函数:SHA-256(Secure Hash Algorithm)
Hiding

哈希的过程单向不可逆

  • Hiding的定义:输入值的空间够大,且分布均匀,取值可能性相同
    实际场景如何实现Hiding(保证分布均匀):输入值 + 随机数(输入X || nonce随机数)后经过Hash
Puzzle friendly

事先无法知道什么样的输入能得到一个什么样的哈希值,只能一个个尝试

比如挖矿,H(nonce + block header) <= target,没有捷径,只能去尝试多个nonce来找到解 => proof of work

比特币中的账户管理

非对称加密(asymmetric encryption algorithm):加密解密不用同一个密钥,加密用公钥,解密用私钥
去中心化,每个用户本地自己生成一组公钥和私钥,公钥相当于银行账号,私钥相当于账号密码
比特币交易过程中,为了能知道交易是由谁发起,需要用私钥将交易签名,公钥验证

两个人生成的公钥私钥相同怎么办(256位的值,产生两组相同公钥私钥的概率微乎其微)

message取hash->hash取签名

阅读全文 »

HTTP 响应体流

HTTP 响应体流是什么

单次 HTTP 请求对应一条响应;响应体暴露为 ReadableStream,浏览器用 fetch 拿到 res.body 后,通过 getReader() 循环 read() 按块消费,像边下载边读一样

HTTP 数据格式

正文多为连续 UTF-8 字节流(例如 Content-Type: text/plain),没有统一的应用层分帧规范;是否在分块边界截断、如何拼接成字符串由业务约定(下文示例把整个回答收成一段纯文本)。

代码实现

服务端代码实现

原始 HTTP 分块:正文为连续 UTF-8 文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
app.post("/api/chat", async (req, res) => {
try {
const parsed = chatBodySchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: parsed.error.flatten() });
return;
}

// 调用模型,返回流式响应数据
const stream = await invokeChatStream(parsed.data.message);
res.setHeader("Content-Type", "text/plain; charset=utf-8");
res.setHeader("Cache-Control", "no-cache, no-transform");
res.setHeader("X-Content-Type-Options", "nosniff");
res.flushHeaders();

for await (const chunk of stream) {
const text = getChunkText(chunk);
if (text) res.write(text);
}
res.end();
} catch (err) {
console.error(err);
if (!res.headersSent) {
res.status(500).json({ error: "internal_server_error" });
} else {
res.end();
}
}
});
阅读全文 »

MCP概述

MCP(Model Context Protocol)模型上下文协议

MCP用途

能让模型更好的使用各类工具(大模型本身只会问答)
image-2025-8-26

MCP Host

支持MCP协议的软件,Cursor,Claude Desktop,Cline

MCP Server

本质上是一个符合MCP协议的程序,不一定联网
image-2025-8-26-1

  • timeout: 60 (连接MCP Server的超时时间)

  • command: uv (程序)

  • transportType: stdio、sse(client和MCP Server沟通的方式)
    以一个 MCP Server 为例
    image-2025-8-26-2

  • MCP Server,MCP Host,用户,模型交互流程
    image-2025-8-26-3

  • 如何使用别人写的MCP Server

    • mcp.so
    • mcpmarket.com
    • smithery.ai

MCP Server一般用 Python 或 Node 编写,对应启动程序 -> uvx,npx

开发一个MCP Server

MCP底层协议

MCP的含义和地位

0%