跳到主要内容

在 jsdoc 中定义和复用对象类型

· 阅读需 2 分钟
1adybug
子虚伊人

我来介绍几种在 JSDoc 中定义和复用对象类型的方法:

  1. 使用 @typedef 定义类型:

    /**
    * @typedef {Object} User
    * @property {string} name - 用户名
    * @property {number} age - 年龄
    * @property {string} [email] - 可选的邮箱
    */

    /**
    * 创建新用户
    * @param {User} userData - 用户数据
    * @returns {User}
    */
    function createUser(userData) {
    // ...
    }
  2. 使用 @typedef 定义嵌套对象:

    /**
    * @typedef {Object} Address
    * @property {string} street
    * @property {string} city
    */

    /**
    * @typedef {Object} UserWithAddress
    * @property {string} name
    * @property {Address} address - 用户地址
    */

    /**
    * @param {UserWithAddress} user
    */
    function printUserAddress(user) {
    // ...
    }
  3. 使用 @callback 定义包含函数的对象类型:

    /**
    * @typedef {Object} ConfigOptions
    * @property {string} baseUrl
    * @property {function(Error): void} onError - 错误处理回调
    */

    /**
    * @param {ConfigOptions} config
    */
    function initialize(config) {
    // ...
    }
  4. 继承和扩展已有类型:

    /**
    * @typedef {Object} BaseConfig
    * @property {string} env - 环境变量
    */

    /**
    * @typedef {BaseConfig} ProductConfig
    * @property {string} productId - 产品ID
    */

    /**
    * @param {ProductConfig} config
    */
    function setupProduct(config) {
    // ...
    }
  5. 使用命名空间组织类型:

    /**
    * @namespace API
    */

    /**
    * @typedef {Object} API.RequestConfig
    * @property {string} url
    * @property {string} method
    */

    /**
    * @typedef {Object} API.Response
    * @property {number} status
    * @property {*} data
    */

    /**
    * @param {API.RequestConfig} config
    * @returns {Promise<API.Response>}
    */
    function request(config) {
    // ...
    }

这些类型定义可以在整个项目中重复使用。一些实用建议:

  • 将常用的类型定义放在单独的文件中,例如 types.js
  • 为复杂对象添加详细的属性描述
  • 使用 @property 的可选标记 [] 来表示可选属性
  • 用命名空间避免类型名称冲突
  • IDE(如 VSCode)会根据这些类型定义提供代码提示

这样可以让你的代码更容易维护,并提供更好的开发体验。

useQuery 中的状态

· 阅读需 1 分钟
1adybug
子虚伊人

@tanstack/react-queryuseQuery 分别提供了四种状态:

  • isPending:是否在等待数据,只要有数据存在,无论是缓存的,还是请求的,都不会是 isPending
  • isFetching:是否在请求数据
  • isRefetching:是否在重新请求数据
  • isLoading:是否在首次请求数据,等同于 isPending && isFetching
提示

同一个 queryKey 的这些状态是共享的

Docker 安装

· 阅读需 2 分钟
1adybug
子虚伊人

使用 apt 命令安装很有可能失败,参考官方教程 install-from-a-package

  1. 转至 https://download.docker.com/linux/ubuntu/dists/

  2. 在列表中选择 Ubuntu 版本

  3. 转到 pool/stable/ 并选择适用的架构(amd64armhfarm64s390x

  4. 下载 Docker EngineCLIcontainerdDocker Compose 软件包的以下 deb 文件:

    • containerd.io_<version>_<arch>.deb
    • docker-ce_<version>_<arch>.deb
    • docker-ce-cli_<version>_<arch>.deb
    • docker-buildx-plugin_<version>_<arch>.deb
    • docker-compose-plugin_<version>_<arch>.deb
  5. 安装 .deb 软件包。将以下示例中的路径更新为您下载 Docker 软件包的位置。

    sudo dpkg -i ./containerd.io_<version>_<arch>.deb \
    ./docker-ce_<version>_<arch>.deb \
    ./docker-ce-cli_<version>_<arch>.deb \
    ./docker-buildx-plugin_<version>_<arch>.deb \
    ./docker-compose-plugin_<version>_<arch>.deb

    Docker 守护进程自动启动。

  6. 通过运行 hello-world 镜像来验证安装是否成功:

    sudo service docker start
    sudo docker run hello-world

    此命令下载测试映像并在容器中运行。容器运行时,它会打印一条确认消息并退出。

现已成功安装并启动了 Docker Engine

修改 Docker 镜像

· 阅读需 1 分钟
1adybug
子虚伊人
  1. 创建目录 /etc/docker

    sudo mkdir -p /etc/docker
  2. 创建并编辑文件 /etc/docker/daemon.json

    sudo vim /etc/docker/daemon.json

    添加以下内容:

    {
    "registry-mirrors": ["https://docker.sunzishaokao.com", "https://hub.hxui.site", "https://docker.1ms.run"],
    "exec-opts": ["native.cgroupdriver=systemd"]
    }

    其中 registry-mirrors 为镜像地址,根据实际情况替换。

  3. 重启 Docker 服务:

    sudo systemctl daemon-reload
    sudo systemctl restart docker

修改 Ubuntu 镜像源

· 阅读需 1 分钟
1adybug
子虚伊人
  1. 首先确定 Ubuntu 版本代号:

    lsb_release -c

    常见的版本代号:

    • 16.04 xenial
    • 18.04 bionic
    • 20.04 focal
    • 22.04 jammy
    • 24.04 noble
  2. 备份原有的源文件:

    sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
  3. 修改源文件:

    sudo vim /etc/apt/sources.list

    将文件内容替换为以下内容:

    # 中科大镜像源
    deb https://mirrors.ustc.edu.cn/ubuntu/ noble main restricted universe multiverse
    deb https://mirrors.ustc.edu.cn/ubuntu/ noble-updates main restricted universe multiverse
    deb https://mirrors.ustc.edu.cn/ubuntu/ noble-backports main restricted universe multiverse
    deb https://mirrors.ustc.edu.cn/ubuntu/ noble-security main restricted universe multiverse

    其中 noble 为 Ubuntu 版本代号,根据实际情况替换。

  4. 更新源:

    sudo apt update

WSL 网络设置

· 阅读需 1 分钟
1adybug
子虚伊人

wsl 默认使用的是 NAT 模式的网络,无法直接访问外部网络,可以通过修改 wsl 的网络设置,将其设置为 mirrored,使其能够直接访问外部网络。

C:\Users\用户名 目录下创建 .wslconfig 文件,内容如下:

[wsl2]
networkingMode=mirrored

wsl 中查看网络配置:

ifconfig

如果 ip 地址已经和主机在同一个网段,那么网络设置已经生效。

重启 wsl 使设置生效:

wsl --shutdown

代理可能需要重新设置,或者重启代理软件或者主机

如果要在 NAT 模式下使用 clash 代理,可以参考 获取 Windows 在 wsl 中的 ip 这篇文章

vi ~/.bashrc

~/.bashrc 文件中添加以下内容:

export http_proxy=http://172.30.160.1:7890
export https_proxy=http://172.30.160.1:7890
export all_proxy=http://172.30.160.1:7890
export HTTP_PROXY=http://172.30.160.1:7890
export HTTPS_PROXY=http://172.30.160.1:7890
export ALL_PROXY=http://172.30.160.1:7890

重启终端,或者执行 source ~/.bashrc 使设置生效。

发布后同步到 npm 镜像

· 阅读需 2 分钟
1adybug
子虚伊人

在发布 npm 包后,可以立即在 npm 上看到最新的版本,然而 npmmirror 等镜像站却有一定的延迟。如果想要立即同步到镜像站,可以使用以下方法:

  1. 在项目根目录创建 scripts 文件夹,创建 sync.mjs 文件

    // @ts-check

    import { readFile } from "fs/promises"

    /**
    * 将浏览器中直接复制的 headers 转换为对象
    * @param {string} str 复制的 headers
    * @returns {Headers} headers 对象
    */
    function getHeaders(str) {
    const reg = /^(.+?):$\n^(.+?)$/gm
    const reg2 = new RegExp(reg.source, "m")
    const headers = new Headers()
    const match = str.match(reg)
    if (!match) throw new Error("headers 格式错误")

    Array.from(match).forEach(item => {
    const match2 = item.match(reg2)
    headers.set(match2[1], match2[2])
    })

    return headers
    }

    const headers = getHeaders(`accept:
    */*
    accept-encoding:
    gzip, deflate, br, zstd
    accept-language:
    zh-CN,zh;q=0.9,en;q=0.8
    content-length:
    0
    dnt:
    1
    origin:
    https://npmmirror.com
    priority:
    u=1, i
    referer:
    https://npmmirror.com/
    sec-ch-ua:
    "Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"
    sec-ch-ua-mobile:
    ?0
    sec-ch-ua-platform:
    "Windows"
    sec-fetch-dest:
    empty
    sec-fetch-mode:
    cors
    sec-fetch-site:
    same-site
    user-agent:
    Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36`)

    /**
    * 同步包
    * @param {string} packageName 包名
    */
    function syncPackage(packageName) {
    return fetch(`https://registry-direct.npmmirror.com/-/package/${packageName}/syncs`, {
    headers,
    referrer: "https://npmmirror.com/",
    referrerPolicy: "strict-origin-when-cross-origin",
    method: "PUT",
    mode: "cors",
    credentials: "omit",
    })
    }

    async function main() {
    const packageJson = JSON.parse(await readFile("package.json", "utf-8"))
    await syncPackage(packageJson.name)
    }

    main()
  2. package.json 中添加 sync 脚本

    {
    "scripts": {
    "postpublish": "node scripts/sync.mjs"
    }
    }

useQuery 中的 gcTime 和 staleTime

· 阅读需 1 分钟
1adybug
子虚伊人

gcTime 表示所有缓存数据回收时间。默认为 5 分钟。如果设置为 Infinity, 则表示缓存数据永不过期。如果存在多个 gcTime 值,则取缓存周期中的最大值。如果所有引用这个 queryKeyhooks 都被销毁了,那么这个 queryKey 的缓存数据也会被销毁。如果在销毁之后,又有新的 hooks 使用了这个 queryKey,分为两种情况:

  1. 如果还在 gcTime 时间内,那么会直接使用缓存数据,且新的 gcTime 会加入到这个缓存周期中,即使这个 gcTime 比之前的 gcTime 小,也会取最大值
  2. 如果超过了 gcTime 时间,那么会重新请求数据,会生成一个新的缓存周期

staleTime 表示缓存数据新鲜时间。默认为 0。在该时间间隔内,认为数据是新鲜的,不会重新发请求。如果设置为 Infinity,则表示数据永远新鲜。

之前也分析过 ahooks 中的 useRequest(参考useRequest 中的 cacheTime 和 staleTime),规则复杂,心智负担严重,且在严格模式下数据不一致。@tanstack/query 中的 useQuery 明显更加优雅,且设计合理。

移除 Ant Design 中默认的 a 元素样式

· 阅读需 1 分钟
1adybug
子虚伊人

Ant Design 总是会给 a 元素设置默认样式,这样会导致在使用 a 元素时,样式不符合我们的预期。比如在 a 元素中设置了 colorred,但是在 Ant Design 中,a 元素的默认样式是 blue,这样就会导致我们的样式被覆盖。可以用以下方式去除默认样式:

a[href],
a[href]:active,
a[href]:hover {
color: inherit;
}

使用 void 操作符返回

· 阅读需 1 分钟
1adybug
子虚伊人

有的时候,某些函数类的参数类型是 () => (undefined | SomeType),如果我们使用箭头函数的话,就会出现返回类型不匹配的情况,比如:

import { useEffect } from "react"

useEffect(() => setTimeout(() => console.log("Hello"), 1000), [])

这时候,编辑器会给我们报错 不能将类型“Timeout”分配给类型“void | Destructor”,我们可以使用 {}console.log("Hello") 包裹起来:

import { useEffect } from "react"

useEffect(
() =>
setTimeout(() => {
console.log("Hello")
}, 1000),
[],
)

但是这样的话,代码会变得很臃肿,我们可以使用 void 操作符来解决这个问题:

import { useEffect } from "react"

useEffect(() => void setTimeout(() => console.log("Hello"), 1000), [])

大功告成!