跳到主要内容

git 起步

· 阅读需 1 分钟
1adybug
子虚伊人
  1. 设置用户名和邮箱

    git config --global user.name "Your Name"
    git config --global user.email "your_email@example.com"
  2. 生成 ssh 密钥

    ssh-keygen -t ed25519 -C "your_email@example.com"
  3. 设置代理

    git config --global http.proxy http://proxyusername:proxypassword@proxy.server.com:port
    git config --global https.proxy https://proxyusername:proxypassword@proxy.server.com:port

在 shell 中添加命令别名

· 阅读需 1 分钟
1adybug
子虚伊人
  1. 打开终端。

  2. 编辑你的 shell 配置文件。例如,如果你使用的是 bash,则可以编辑 ~/.bashrc 文件;如果你使用的是 zsh,则可以编辑 ~/.zshrc 文件。如果你使用的是 PowerShell,则可以编辑 $PROFILE 文件。你可以使用以下命令来打开文件:

    vi ~/.bashrc
    vi ~/.zshrc
    notepad $PROFILE
  3. 在文件的末尾添加以下行:

    alias p="pnpm"
    Set-Alias p pnpm
  4. 保存文件并退出编辑器。

  5. 你可以运行以下命令来重新加载你的 shell 配置文件使更改生效:

    source ~/.bashrc
    source ~/.zshrc
    . $PROFILE

现在,你可以在终端中使用 p 来代替 pnpm 运行命令。例如:

p install lodash

这样就会等同于运行 pnpm install lodash

当然在 PowerShell 中还可以使用函数来实现:

function p {
pnpm $args
}

获取 windows 在 wsl 中的 ip

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

有时我们需要在 wsl 中与 windows 进行通信,比如设置代理之类,可以通过以下方式获取到 windows 的 ip:

在终端中输入:

ipconfig

带有 vEthernet (WSL (Hyper-V firewall)) 字段的适配器便是 wsl 所在的网络,ip 地址便是 windows 的 ip。

当然,有时候我可能会发现在 wsl 中无法访问到 windows 的服务,大概率是防火墙的问题,可以尝试关闭防火墙,或者新增一个入站规则,允许 wsl 访问 windows 的端口。

  1. 打开 Windows Defender
  2. 点击 防火墙和网络保护
  3. 点击 高级设置
  4. 点击 入站规则
  5. 点击 新建规则
  6. 选择 端口
  7. 选择 特定本地端口
  8. 输入 windows 的 ip 和端口
  9. 点击 确定

这样就可以在 wsl 中访问到 windows 的 ip 了。

在 linux 中向 exec 传递 env

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

在 linux 中使用 exec 执行环境命令并传递 env 参数时时,发现很多命令都丢失了:

import { exec } from "child_process"

// 正常
exec("node -v")

// 报错,提示找不到 node 命令
exec("node -v", {
env: {
NODE_ENV: "production",
NAME: "Tom",
},
})

但是在 windows 中测试,发现正常。求助万能的 ChatGPT ,得知,在 linux 中,如果传递了 env 参数,所有的环境变量都会被覆盖,所以正确的做法是:

exec("node -v", {
env: {
...process.env,
NODE_ENV: "production",
NAME: "Tom",
},
})

正常,搞定。

Antd Design 的 Form 中自定义受控组件的使用

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

自定义或第三方的表单控件,也可以与 Form 组件一起使用。只要该组件遵循以下的约定:

  1. 提供受控属性 value 或其它与 valuePropName 的值同名的属性。

  2. 提供 onChange 事件或 trigger 的值同名的事件。

  3. 转发 ref 或者传递 id 属性到 dom 以支持 scrollToField 方法。

import { ChangeEvent, FC, forwardRef } from "react"

import { Button, Form, Input } from "antd"
import { useForm } from "antd/es/form/Form"
import FormItem from "antd/es/form/FormItem"
import { useInputState } from "soda-hooks"

type Info = {
name?: string
id?: string
}

type InfoItemProps = {
value?: Info
onChange?: (value: Info) => void
}

const InfoItem = forwardRef<HTMLDivElement, InfoItemProps>((props, ref) => {
const { value, onChange } = props

// 推荐使用 soda-hooks 的 useInputState
const [name, setName] = useInputState(value?.name)

const [id, setId] = useInputState(value?.id)

function onNameChange(e: ChangeEvent<HTMLInputElement>) {
setName(e.target.value)
// 将变化之后的 value 传递给外部,优先级为 state < props < 最新的变化的 value
onChange?.({ id, ...value, name: e.target.value })
}

function onIdChange(e: ChangeEvent<HTMLInputElement>) {
setId(e.target.value)
onChange?.({ name, ...value, id: e.target.value })
}

return (
<div ref={ref}>
<Input value={name} onChange={onNameChange} />
<Input value={id} onChange={onIdChange} />
</div>
)
})

type FormData = {
info: Info
}

const App: FC = () => {
const [form] = useForm<FormData>()

return (
<Form<FormData> form={form} onFinish={console.dir}>
<FormItem<FormData> name="info">
<InfoItem />
</FormItem>
<FormItem<FormData>>
<Button onClick={() => form.setFieldsValue({ info: undefined })}>Reset</Button>
</FormItem>
<FormItem<FormData>>
<Button htmlType="submit">Submit</Button>
</FormItem>
</Form>
)
}

export default App
提示
  1. onChange 是会一直变化的,所以需要获取到最新值
  2. onChangeform.setFieldsValue 一样,都是同步的

Ant Design 中 FormList 的使用

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

有时候在我们的表单中可能存在动态增删的情况,比如需要登记报名的人员,人员的数量是不确定的,每个人员都要登记姓名和身份证号,这便是动态表单。

简单的动态表单

import { FC, Fragment } from "react"

import { Button, Form, Input } from "antd"
import FormItem from "antd/es/form/FormItem"
import FormList from "antd/es/form/FormList"

const App: FC = () => (
<Form onFinish={console.dir}>
<FormList name="hobbies" initialValue={["钓鱼"]}>
{(fields, { add }) => (
<Fragment>
{fields.map(({ key, name }) => (
<FormItem key={key} name={name}>
<Input />
</FormItem>
))}
<Button onClick={() => add()}>Add</Button>
</Fragment>
)}
</FormList>
<Button htmlType="submit">Submit</Button>
</Form>
)

export default App

在这个表单中,hobbies 字段是动态的,使用起来也非常简单,但是有以下注意点:

  1. FormList 不接受泛型参数,直接给它传递动态的字段名即可,这里是 hobbies

  2. FormListinitialValue 就是初始值,如果,你希望初始时就有一个输入框呈现,可以传入 [undefined]

  3. initialValue 的每一项都会传递给 FormItem,继而传递给内部的表单元素。所以,必须传递相应类型的值,比如 Input 组件可以接受的初始值是 string | undefined,那 initialValue 可以接受的类型就是 (string | undefined)[],而 RangePicker 可以接受的初始值是 [Dayjs | null, Dayjs | null] | undefined,那 initialValue 可以接受的类型就是 ([Dayjs | null, Dayjs | null] | undefined)[]

  4. FormList 接受一个函数作为 children。函数的有三个参数:fieldsoperationmeta

  5. fields 是一个由 field 组成的数组,用于渲染动态表单的每一个子元素。

  6. field 有两个属性 keyname,都是用于直接传递给 FormItem。注意不能直接使用 {...field} 的形式。因为 React 组件的 key 属性必须显式地声明,也就是必须是 key={key} 的形式。

  7. opertion 有三个属性 addremovemove 属性。

  8. add 是一个用于添加表单数量的方法,接受两个参数 defaultValueinsertIndexdefaultValue 会传递给新增的 FormItem。与第 3 点类似,初始值的类型必须匹配。insertIndex 用于设置插入的位置。不能使用 <Button onClick={add}>Add</Button> 的形式,因为 onClick 是传递参数 MouseEvent 的,只能使用 <Button onClick={() => add()}>Add</Button>

  9. remove 是一个用于删除某个表单的方法,接受一个参数 index,可以是 number 也可以是 number[],对应 field 中的 name

  10. move 是一个用于移动表单的方法,接受两个参数 fromto,都对应 field 中的 name

嵌套的动态表单

import { FC, Fragment } from "react"

import { Button, Form, Input } from "antd"
import FormItem from "antd/es/form/FormItem"
import FormList from "antd/es/form/FormList"

const App: FC = () => (
<Form onFinish={console.dir}>
<FormList name="persons" initialValue={[{ name: "Tom", age: "18" }]}>
{(fields, { add }) => (
<Fragment>
{fields.map(({ key, name }) => (
<Fragment key={key}>
<FormItem name={[name, "name"]} label="姓名">
<Input />
</FormItem>
<FormItem name={[name, "age"]} label="年龄">
<Input />
</FormItem>
</Fragment>
))}
<Button onClick={() => add()}>Add</Button>
</Fragment>
)}
</FormList>
<Button htmlType="submit">Submit</Button>
</Form>
)

export default App

有时候,我们动态的表单可能不止一项属性,可能存在多个属性,属性中可能还有多个属性,这时便是嵌套的动态表单,这与简单的动态表单有以下几点不同:

  1. name 属性无法直接使用,必须是使用 [name, "age"] 这种路径数组的形式

  2. initialValue 也不再是 valueType[] 的形式,而是 { name: valueType }[] 的形式

  3. FormList 也可以再次嵌套 FormList

    <FormList name="persons">
    {fields =>
    fields.map(({ name }) => (
    <FormItem label="爱好">
    <FormList name={[name, "hobbies"]} initialValue={["钓鱼"]}>
    {(fields, { add }) => (
    <Fragment>
    {fields.map(({ key, name }) => (
    <FormItem key={key} name={name}>
    <Input />
    </FormItem>
    ))}
    <Button onClick={() => add()}>Add</Button>
    </Fragment>
    )}
    </FormList>
    </FormItem>
    ))
    }
    </FormList>

在 nginx 中使用 https

· 阅读需 1 分钟
1adybug
子虚伊人
http {
server {
# 网站端口
listen 443 ssl;
# 网站域名
server_name urdomain.com;

# 证书地址
ssl_certificate /etc/letsencrypt/live/urdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/urdomain.com/privkey.pem;

location / {

#本地服务地址
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}

Ant Design 的 Form 中 requiredMark 的使用

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

之前一直都是使用 css 来实现必填组件的 * 号的隐藏,没想到 Ant Design 官方提供了修改的方法:

import React, { FC, Fragment, useState } from "react"

import { InfoCircleOutlined } from "@ant-design/icons"
import { Button, Form, Input, Radio, Tag } from "antd"
import { useForm } from "antd/es/form/Form"
import FormItem from "antd/es/form/FormItem"

type RequiredMark = boolean | "optional" | "customize"

const customizeRequiredMark = (label: React.ReactNode, { required }: { required: boolean }) => (
<Fragment>
{required ? <Tag color="error">Required</Tag> : <Tag color="warning">optional</Tag>}
{label}
</Fragment>
)

const App: FC = () => {
const [form] = useForm()
const [requiredMark, setRequiredMarkType] = useState<RequiredMark>("optional")

const onRequiredTypeChange = ({ requiredMarkValue }: { requiredMarkValue: RequiredMark }) => {
setRequiredMarkType(requiredMarkValue)
}

return (
<Form
form={form}
layout="vertical"
initialValues={{ requiredMarkValue: requiredMark }}
onValuesChange={onRequiredTypeChange}
requiredMark={requiredMark === "customize" ? customizeRequiredMark : requiredMark}
>
<FormItem label="Required Mark" name="requiredMarkValue">
<Radio.Group>
<Radio.Button value>Default</Radio.Button>
<Radio.Button value="optional">Optional</Radio.Button>
<Radio.Button value={false}>Hidden</Radio.Button>
<Radio.Button value="customize">Customize</Radio.Button>
</Radio.Group>
</FormItem>
<FormItem label="Field A" required tooltip="This is a required field">
<Input placeholder="input placeholder" />
</FormItem>
<FormItem
label="Field B"
tooltip={{
title: "Tooltip with customize icon",
icon: <InfoCircleOutlined />,
}}
>
<Input placeholder="input placeholder" />
</FormItem>
<FormItem>
<Button type="primary">Submit</Button>
</FormItem>
</Form>
)
}

export default App

requiredMark 有四种可以传递的值:

  1. true 默认值

  2. false 不显示是否必填

  3. "optional" 在可选表单项的 label 后面添加 (可选)

  4. (label: ReactNode, { required }: { required: boolean }) => ReactNode 自定义渲染函数

使用正则表达式替换字符串

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

当我们使用正则字符串替换字符串时,replace 函数的第二个表达式可以传入一个函数,函数的参数含义为:

/**
* 第一个参数为匹配到的字符串
* 从第二个参数开始为匹配到的所有分组
* 倒数第二个参数为匹配到的字符串的序列号
* 最后一个参数为原始字符串
*/
function replacer(match: string, arg1: string, arg2: string, ...args: string[], index: number, str: string): string {}