声明环境变量
declare namespace NodeJS {
interface ProcessEnv {
TOKEN?: string
}
}
declare namespace NodeJS {
interface ProcessEnv {
TOKEN?: string
}
}
设置用户名和邮箱
git config --global user.name "Your Name"
git config --global user.email "your_email@example.com"
生成 ssh 密钥
ssh-keygen -t ed25519 -C "your_email@example.com"
设置代理
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 配置文件。例如,如果你使用的是 bash,则可以编辑 ~/.bashrc 文件;如果你使用的是 zsh,则可以编辑 ~/.zshrc 文件。如果你使用的是 PowerShell,则可以编辑 $PROFILE 文件。你可以使用以下命令来打开文件:
vi ~/.bashrc
vi ~/.zshrc
notepad $PROFILE
在文件的末尾添加以下行:
alias p="pnpm"
Set-Alias p pnpm
保存文件并退出编辑器。
你可以运行以下命令来重新加载你的 shell 配置文件使更改生效:
source ~/.bashrc
source ~/.zshrc
. $PROFILE
现在,你可以在终端中使用 p 来代替 pnpm 运行命令。例如:
p install lodash
这样就会等同于运行 pnpm install lodash。
当然在 PowerShell 中还可以使用函数来实现:
function p {
pnpm $args
}
有时我们需要在 wsl 中与 windows 进行通信,比如设置代理之类,可以通过以下方式获取到 windows 的 ip:
在终端中输入:
ipconfig
带有 vEthernet (WSL (Hyper-V firewall)) 字段的适配器便是 wsl 所在的网络,ip 地址便是 windows 的 ip。
当然,有时候我可能会发现在 wsl 中无法访问到 windows 的服务,大概率是防火墙的问题,可以尝试关闭防火墙,或者新增一个入站规则,允许 wsl 访问 windows 的端口。
Windows Defender防火墙和网络保护高级设置入站规则新建规则端口特定本地端口windows 的 ip 和端口确定这样就可以在 wsl 中访问到 windows 的 ip 了。
在 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",
},
})
正常,搞定。
自定义或第三方的表单控件,也可以与 Form 组件一起使用。只要该组件遵循以下的约定:
提供受控属性 value 或其它与 valuePropName 的值同名的属性。
提供 onChange 事件或 trigger 的值同名的事件。
转发 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
onChange 是会一直变化的,所以需要获取到最新值onChange 与 form.setFieldsValue 一样,都是同步的有时候在我们的表单中可能存在动态增删的情况,比如需要登记报名的人员,人员的数量是不确定的,每个人员都要登记姓名和身份证号,这便是动态表单。
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 字段是动态的,使用起来也非常简单,但是有以下注意点:
FormList 不接受泛型参数,直接给它传递动态的字段名即可,这里是 hobbies
FormList 的 initialValue 就是初始值,如果,你希望初始时就有一个输入框呈现,可以传入 [undefined]
initialValue 的每一项都会传递给 FormItem,继而传递给内部的表单元素。所以,必须传递相应类型的值,比如 Input 组件可以接受的初始值是 string | undefined,那 initialValue 可以接受的类型就是 (string | undefined)[],而 RangePicker 可以接受的初始值是 [Dayjs | null, Dayjs | null] | undefined,那 initialValue 可以接受的类型就是 ([Dayjs | null, Dayjs | null] | undefined)[]
FormList 接受一个函数作为 children。函数的有三个参数:fields、operation 和 meta
fields 是一个由 field 组成的数组,用于渲染动态表单的每一个子元素。
field 有两个属性 key 和 name,都是用于直接传递给 FormItem。注意不能直接使用 {...field} 的形式。因为 React 组件的 key 属性必须显式地声明,也就是必须是 key={key} 的形式。
opertion 有三个属性 add、remove 和 move 属性。
add 是一个用于添加表单数量的方法,接受两个参数 defaultValue 和 insertIndex。defaultValue 会传递给新增的 FormItem。与第 3 点类似,初始值的类型必须匹配。insertIndex 用于设置插入的位置。不能使用 <Button onClick={add}>Add</Button> 的形式,因为 onClick 是传递参数 MouseEvent 的,只能使用 <Button onClick={() => add()}>Add</Button>
remove 是一个用于删除某个表单的方法,接受一个参数 index,可以是 number 也可以是 number[],对应 field 中的 name
move 是一个用于移动表单的方法,接受两个参数 from 和 to,都对应 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
有时候,我们动态的表单可能不止一项属性,可能存在多个属性,属性中可能还有多个属性,这时便是嵌套的动态表单,这与简单的动态表单有以下几点不同:
name 属性无法直接使用,必须是使用 [name, "age"] 这种路径数组的形式
initialValue 也不再是 valueType[] 的形式,而是 { name: valueType }[] 的形式
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>
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;
}
}
}
之前一直都是使用 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 有四种可以传递的值:
true 默认值
false 不显示是否必填
"optional" 在可选表单项的 label 后面添加 (可选)
(label: ReactNode, { required }: { required: boolean }) => ReactNode 自定义渲染函数
当我们使用正则字符串替换字符串时,replace 函数的第二个表达式可以传入一个函数,函数的参数含义为:
/**
* 第一个参数为匹配到的字符串
* 从第二个参数开始为匹配到的所有分组
* 倒数第二个参数为匹配到的字符串的序列号
* 最后一个参数为原始字符串
*/
function replacer(match: string, arg1: string, arg2: string, ...args: string[], index: number, str: string): string {}