2024-12-26 08:00:00
index.html
写入打包时间index.html
中的时间在项目根目录添加一个 build
文件夹,存放 vite
相关的 .ts
文件。
在 build
文件夹添加 html.ts
,编写一个 vite
插件,作用是打包时候在 index.html
文件的 head
内插入当前打包的时间。
// 路径 /build/html.ts
import type { Plugin } from 'vite'
export function setupHtmlPlugin(buildTime: string) {
const plugin: Plugin = {
name: 'html-plugin',
apply: 'build',
transformIndexHtml(html) {
return html.replace('<head>', `<head>\n <meta name="buildTime" content="${buildTime}">`)
}
}
return plugin
}
在 build
文件夹添加 time.ts
,并且安装 dayjs
库,编写一个获取时间的函数。
// 路径 /build/time.ts
import dayjs from 'dayjs'
import timezone from 'dayjs/plugin/timezone'
import utc from 'dayjs/plugin/utc'
export function getBuildTime() {
dayjs.extend(utc)
dayjs.extend(timezone)
const buildTime = dayjs.tz(Date.now(), 'Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss')
return buildTime
}
在 vite.config.ts
文件中引用
import process from 'node:process'
import { fileURLToPath, URL } from 'node:url'
import { defineConfig, loadEnv } from 'vite'
import { setupHtmlPlugin } from './build/html'
import { getBuildTime } from './build/time'
export default defineConfig(() => {
// 当前打包时间
const buildTime = getBuildTime()
return {
base: '/',
plugins: [
vue(),
// 使用插件
setupHtmlPlugin(buildTime)
],
define: {
// 注入全局的 BUILD_TIME 变量
BUILD_TIME: JSON.stringify(buildTime)
},
}
})
添加 src/plugins
目录,添加 app.ts
文件,编写相关逻辑,这里使用 Vue3
和 Naive UI
作为参考
// 路径 src/plugin/app.ts
import type { App } from 'vue'
import { NButton } from 'naive-ui'
import { h } from 'vue'
const UPDATE_CHECK_INTERVAL = 3 * 60 * 1000
export function setupAppVersionNotification() {
let isShow = false
let updateInterval: ReturnType<typeof setInterval> | undefined
const shouldCheckForUpdates = [!isShow, document.visibilityState === 'visible', !import.meta.env.DEV].every(Boolean)
const checkForUpdates = async () => {
if (!shouldCheckForUpdates)
return
const buildTime = await getHtmlBuildTime()
if (buildTime === BUILD_TIME) {
return
}
isShow = true
const n = window.$notification?.create({
title: '系统版本更新通知',
content: '检测到系统有新版本发布,是否立即刷新页面?',
action() {
return h('div', { style: { display: 'flex', justifyContent: 'end', gap: '12px', width: '325px' } }, [
h(
NButton,
{
onClick() {
n?.destroy()
}
},
() => '稍后再说'
),
h(
NButton,
{
type: 'primary',
onClick() {
location.reload()
}
},
() => '立即刷新'
)
])
},
onClose() {
isShow = false
}
})
}
const startUpdateInterval = () => {
if (updateInterval) {
clearInterval(updateInterval)
}
updateInterval = setInterval(checkForUpdates, UPDATE_CHECK_INTERVAL)
}
if (shouldCheckForUpdates) {
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
checkForUpdates()
startUpdateInterval()
}
})
startUpdateInterval()
}
}
async function getHtmlBuildTime() {
const baseUrl = '/'
const res = await fetch(`${baseUrl}index.html?time=${Date.now()}`)
const html = await res.text()
const match = html.match(/<meta name="buildTime" content="(.*)">/)
const buildTime = match?.[1] || ''
return buildTime
}
在 src/main.ts
文件引入刚刚文件
import { createApp } from 'vue'
import App from './App.vue'
import { setupAppVersionNotification } from './plugins/app'
async function bootstrap() {
const app = createApp(App)
// 如果是 react,同理也是在 dom 加载前
setupAppVersionNotification()
app.mount('#app')
}
bootstrap()
使用 vite-plugin-version
插件在打包时读取 package.json
内的版本号并生成 version.json
文件,比对文件版本进行提示更新,原理其实一样。
代码部份来自 SoybeanAdmin,有改动
完
2024-12-17 08:00:00
原文来自 How to use Zustand's persist middleware in Next.js,此作翻译存档。
import { create } from "zustand";
import { persist } from "zustand/middleware";
// Custom types for theme
import { User } from "./types";
interface AuthState {
isAuthenticated: boolean;
user: null | User;
token: null | string;
login: (email: string, password: string) => Promise<void>;
register: (userInfo: FormData) => Promise<void>;
logout: () => void;
}
const useAuthStore = create<AuthState>()(
persist(
(set) => ({
isAuthenticated: false,
user: null,
token: null,
login: async (email, password) => {
// Login user code
},
register: async (userInfo) => {
// Registering user code
},
logout: () => {
// Logout user code
},
}),
{
name: "auth",
}
)
);
export default useAuthStore;
如果我们尝试在组件中直接访问上述存储,当用户已登录时,就会出现水合错误,因为这与存储的初始状态不匹配。
我们会出现水合错误,是因为 Zustand
包含了来自持久化中间件(如本地存储等)的数据,而此时水合过程尚未完成,并且服务器渲染的存储具有初始状态值,这就导致了存储的状态数据出现不匹配的情况。
我通过创建一个状态来解决这个问题,在 useEffect
钩子函数内将 Zustand
存储的状态值写入该状态中,然后使用这个状态来访问存储的值。
import { useState, useEffect } from 'react';
const useStore = <T, F>(
store: (callback: (state: T) => unknown) => unknown,
callback: (state: T) => F
) => {
const result = store(callback) as F;
const [data, setData] = useState<F>();
useEffect(() => {
setData(result);
}, [result]);
return data;
};
必须在组件中通过这个 hook
来访问存储。
const store = useStore(useAuthStore, (state) => state)
2024-09-29 08:00:00
原文来自 【配置优化】我拿到VPS服务器必做的那些事,备份记录
更新软件库
apt update -y && apt upgrade -y
更新、安装必备软件
apt install sudo curl wget nano
校正系统时间
sudo timedatectl set-timezone Asia/Shanghai
timedatectl
timedatectl list-timezones
系统参数调优
实现方法:bash <(wget -qO- https://raw.githubusercontent.com/jerry048/Tune/main/tune.sh) -t
BBR 是 Google 提出的一种新型拥塞控制算法(Bottleneck Bandwidth and RTT),全称为瓶颈带宽和往返传播时间。
在 Linux 系统中,BBR 主要有以下特点和作用:
因为我本人是 PT 玩家,所以接触到了一位大佬自己魔改的 BBR 版本,也就是 BBRx。该版本调整了类似 startup(启动阶段)、drain(排空阶段)、probe_bw(探测带宽阶段)、probe_rtt(探测往返时间阶段)等状态下的一些关键参数,如 pacing_gain(发送速率增益)、cwnd_gain(拥塞窗口增益)等,个人来说觉得比原版 BBR 的效果更好,如果不喜欢的话,可以选择原版 BBR 进行安装。
bash <(wget -qO- https://raw.githubusercontent.com/jerry048/Tune/main/tune.sh) -x
sudo reboot
lsmod | grep bbr
,此时应该返回这样的结果:tcp_bbrx
、tcp_bbr
。sudo reboot
,如果只有tcp_bbr
则再等几分钟reboot
。此时再进行查询:lsmod | grep bbr
,此时应该返回这样的结果:tcp_bbrx
。wget --no-check-certificate https://github.com/teddysun/across/raw/master/bbr.sh && chmod +x bbr.sh &&./bbr.sh
,也是重启 VPS 生效。在 Linux 系统中,SWAP(交换空间)是指一块磁盘空间,用于在物理内存(RAM)不足时,作为临时的扩展内存来使用。当系统的物理内存使用量接近饱和,Linux 内核会将一些不常使用的内存页交换到 SWAP 分区中,从而为当前运行的程序腾出更多的物理内存。当这些被交换出去的内存页再次被需要时,它们会被重新换回到物理内存中。SWAP 分区的存在可以在一定程度上避免由于物理内存不足导致系统性能严重下降或进程被强制终止的情况。
因此,SWAP 对于内存小的 VPS 非常有必要,可以提高我们的运行效率。
这里我们用脚本来添加。
wget -O swap.sh https://raw.githubusercontent.com/yuju520/Script/main/swap.sh && chmod +x swap.sh && clear &&./swap.sh
free -m
wget -qO- get.docker.com | bash
或 curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh
curl https://install.1panel.live/docker-install -o docker-install && sudo bash./docker-install && rm -f./docker-install
docker -v
sudo systemctl enable docker
sudo apt-get purge docker-ce docker-ce-cli containerd.io
、sudo apt-get remove docker docker-engine
、sudo rm -rf /var/lib/docker
、sudo rm -rf /var/lib/containerd
docker compose version
修改 SSH 端口通常有以下几个主要原因:
将默认的 22 端口修改为 55520(暗戳戳地表白)@wanwan:
sudo sed -i 's/^#\?Port 22.*/Port 55520/g' /etc/ssh/sshd_config
sudo systemctl restart sshd
一键生成你的密钥:
wget -O key.sh https://raw.githubusercontent.com/yuju520/Script/main/key.sh && chmod +x key.sh && clear &&./key.sh
注意:请牢记你生成的密钥,否则会有无法连接 SSH 的后果。
apt install fail2ban
/etc/fail2ban/
目录下,fail2ban 的.conf 配置文件都是可以被.local 覆盖,所以配置方式建议是添加.local 文件,不修改原来的配置文件。nano /etc/fail2ban/jail.local
,配置文件如下:[DEFAULT]
#忽略的 IP 列表,不受设置限制(白名单)
ignoreip = 127.0.0.1
#允许 ipv6
allowipv6 = auto
#日志修改检测机制(gamin、polling 和 auto 这三种)
backend = systemd
#针对各服务的检查配置,如设置 bantime、findtime、maxretry 和全局冲突,服务优先级大于全局设置
[sshd]
#是否激活此项(true/false)
enabled = true
#过滤规则 filter 的名字,对应 filter.d 目录下的 sshd.conf
filter = sshd
#ssh 端口
port = ssh
#动作的相关参数
action = iptables[name=SSH, port=ssh, protocol=tcp]
#检测的系统的登陆日志文件
logpath = /var/log/secure
#屏蔽时间,单位:秒
bantime = 86400
#这个时间段内超过规定次数会被 ban 掉
findtime = 86400
#最大尝试次数
maxretry = 3
sudo systemctl enable fail2ban
sudo systemctl restart fail2ban
sudo systemctl status fail2ban
fail2ban-client status
2024-09-14 08:00:00
1、查看当前的 iptables 规则
sudo iptables -L -n
2、添加允许端口的规则(以 38042 为例)
sudo iptables -A INPUT -p tcp --dport 38042 -j ACCEPT
3、 保存规则(如果需要永久生效,需要安装iptables-persistent)
安装iptables-persistent(如果未安装)
sudo apt-get install iptables-persistent
保存规则
sudo netfilter-persistent save
1、启用 ufw(如果尚未启用)
sudo ufw enable
2、允许 38042 端口
sudo ufw allow 38042/tcp
1、检查 firewalld 状态
sudo systemctl status firewalld
2、添加端口到 firewalld 区域(例如 public 区域)
sudo firewall-cmd --zone=public --add-port=38042/tcp --permanent
3、重新加载 firewalld 配置
sudo firewall-cmd --reload
2024-08-09 08:00:00
在Phone或Tablet上查看设置 > 系统中开发者模式是否存在,如果不存在,可在设置 > 关于手机/关于平板中,连续七次单击“版本号”,直到提示“开启开发者模式”,点击确认开启后输入PIN码(如果已设置),设备将自动重启,请等待设备完成重启。
在设备运行应用/服务需要根据为应用/服务进行签名章节,提前对应用/服务进行签名。
1、将 Phone/Tablet 和 PC 连接到同一 WLAN 网络。
2、在开发者模式中,打开“无线调试”开关,并获取 Phone/Tablet 端的 IP 地址和端口号。(如果未开启开发者模式,先多次点击系统版本号开启)
3、在PC中执行如下命令连接设备
hdc tconn 设备IP地址:端口号
4、选择无线设备调试
2024-08-05 08:00:00