跳到主要内容

在 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 {}

在 TypeScript 中声明全局变量

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

有时我们需要声明一些全局变量或者模块,此时我们可以使用 declare 方法来实现:

.d.ts 或者 ts 文件中:

declare global {
var tip: string
}
注意
  1. .d.ts 文件中不能有 import 语句,否则它会变成模块
  2. ts 文件必须被引入,或者是入口文件

对于浏览器打包的项目,还可以添加 window 上的变量

declare global {
var tip: string

interface Window {
tip: string
}
}

window.tip = "This is a tip."
注意

必须使用 interface 来扩展声明

在 Remix 中使用 Ant Design

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

Remix.js 中使用 Ant Design 会出现首次渲染样式丢失的问题,参考 Ant Design 官方的解决方案 整体导出

npm i @ant-design/static-style-extract
npm i cross-env tsx -D
{
"scripts": {
"predev": "tsx ./scripts/genAntdCss.tsx",
"prebuild": "cross-env NODE_ENV=production tsx ./scripts/genAntdCss.tsx"
},
"dependencies": {
"@ant-design/static-style-extract": "^1.0.2"
},
"devDependencies": {
"cross-env": "^7.0.3",
"tsx": "^4.15.6"
}
}
import fs from "fs"

import { extractStyle } from "@ant-design/static-style-extract"
import { ConfigProvider } from "antd"

const outputPath = "./app/antd.min.css"

const css = extractStyle(node => <ConfigProvider theme={{ token: { colorPrimary: "red" } }}>{node}</ConfigProvider>)

fs.writeFileSync(outputPath, css)
import "./antd.min.css"

如果有自定义主题的需求,只需要传递给 ConfigProvider 相应的配置即可:

const css = extractStyle(node => <ConfigProvider theme={{ token: { colorPrimary: "red" } }}>{node}</ConfigProvider>)
注意

这种办法只能是妥协之计,打包出来的 css 文件很大。具体的优化还需要官方实现

设置滚动条样式

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

很多时候,我们希望设置滚动条的样式,但是设置的前提是,我们设置了滚动条的宽度:

/* 设置滚动条的整体样式 */
::-webkit-scrollbar {
/* 滚动条的宽度,纵向滚动时有效 */
width: 12px;
/* 滚动条的高度,横向滚动时有效 */
height: 12px;
}

/* 设置滚动条滑块的样式 */
::-webkit-scrollbar-thumb {
/* 滑块的颜色 */
background-color: rgba(0, 0, 0, 0.25);
/* 滑块的圆角 */
border-radius: 6px;
}

/* 当鼠标悬停在滑块上时,改变滑块的颜色 */
::-webkit-scrollbar-thumb:hover {
/* 滑块的颜色 */
background: rgba(0, 0, 0, 0.45);
}

/* 设置滚动条轨道的样式 */
::-webkit-scrollbar-track {
/* 滚动条轨道的颜色 */
background: rgba(0, 0, 0, 0.05);
/* 滚动条轨道的圆角 */
border-radius: 6px;
}

/* 滚动条角落的颜色 */
::-webkit-scrollbar-corner {
/* 滚动条角落的颜色 */
background: rgba(0, 0, 0, 0.05);
}

如何这些样式不生效,还有可能是元素设置了 scrollbar-colorscrollbar-width 属性,导致浏览器默认样式覆盖了我们的样式,所以需要将这些属性设置为 auto

闭包

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

在 JavaScript 中,很多时候我们总是认为:一个变量如果无法被访问到了,那么它就会被垃圾回收,然而事实却并非如此:

function bar() {
const a = 1
const b = 2

function log() {
console.log(a)
}

return log
}

const log = bar()
console.dir(log)
提示

使用 console.dir 可以避免控制台只打印变量的字符串形式

可以看到,闭包如下:

01

但是如果我们再创建一个函数 log2,并且引用了变量 b

function bar() {
const a = 1
const b = 2

function log() {
console.log(a)
}

function log2() {
console.log(b)
}

return log
}

const log = bar()
console.dir(log)

此时再查看闭包:

02

可以得出结论:只要产生了闭包,而上下文中的其他变量又被其他函数所引用了,即使引用的函数无法被访问,被引用的变量也不会被回收

HTML 元素 props

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

很多时候我们会有元素标签上显示的属性来获取元素的 props

import { HTMLAttributes } from "react"

type MyDivProps = HTMLAttributes<HTMLDivElement>

但其实,React 内置更为方便的泛型工具 ComponentProps

import { ComponentProps } from "react"

type MyDivProps = ComponentProps<"div">

HTMLAttributes<HTMLDivElement>ComponentProps<"div"> 的区别在于,后者包含了 refkey 属性

前端守则

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

技术栈

类型要求
编辑器Visual Studio Code
浏览器Chrome
语言TypeScript
UI 库React
组件库Ant Design / Deepsea Components
脚手架Rsbuild / Next
路由React Router
包管理器Yarn
时间处理Day.js
CSSTailwind / Emotion
hooksahooks
状态共享ahooks / Soda Hooks / React Soda
网络请求fetch
数据验证Zod
数据库管理Prisma
移动端Capacitor
移动端组件库Ionic
代码格式化Prettier

要求

  1. 所有 React 组件必须使用以下形式书写:

    const Component: FC = () => <div></div>

    export default Component

    // 有参数组件
    import { FC } from "react"

    export type ComponentProps = {}

    const Component: FC<ComponentProps> = props => {
    const {} = props

    return <div></div>
    }

    export default Component
  2. 全局共享状态优先使用 ahooks 实现,其次 React Soda

    import { useRequest } from "ahooks"

    export function useCount() {
    return useRequest(() => Promise.resolve(Math.random()), {
    cacheKey: "abc",
    })
    }
    注意

    ahooksStrictMode 下有 Bug,需要关闭严格模式

    import createStore from "react-soda"

    export const useCount = createStore(0)
  3. CSS 样式优先使用 Tailwind 实现,其次 Emotion 或者行内样式实现。

    注意
    • Tailwind 不支持动态属性,如 `bg-${color}`
    • 尽量不要在 Emotion 中书写动态样式,Emotion 生成的样式不会被自动清除。如果有动态需求,可以使用 CSS 变量 var(--name) 搭配行内样式实现
  4. 对于 Ant Design 的样式修改尽量通过修改主题来实现

  5. 对于表单查询的参数使用 Soda Hooks 中的 useQueryState 来存储和控制

    import { useQueryState } from "soda-hooks"

    const [query, setQuery] = useQueryState({
    keys: ["name", "unit"],
    parse: {
    age: value => (value ? Number(value) : undefined),
    },
    })
  6. 在项目根目录配置 Prettier

    // prettier.config.cjs
    module.exports = {
    // tailwind 格式化插件
    plugins: ["prettier-plugin-tailwindcss"],
    semi: false,
    tabWidth: 4,
    arrowParens: "avoid",
    printWidth: 800,
    trailingComma: "none",
    }
  7. 命令规范

在开发环境中使用 https

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

现在很多环境下的网络请求要求必须是 https 请求,但是在开发或者内网环境下,无法使用 CA 机构颁发的证书,这时候可以使用 OpenSSL 提供的自签证书功能:

安装

在 windows 系统下,可以使用 winget 来安装第三方分发版本:

winget search openssl
# 选择一个版本较新的分发版本
winget install FireDaemon.OpenSSL

重启终端

生成证书

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout your-key.pem -out your-cert.pem

使用

import { readFileSync } from "fs"
import { createServer } from "https"

import express from "express"

const app = express()

const server = createServer(
{
key: readFileSync("your-key.pem"),
cert: readFileSync("your-cert.pem"),
},
app,
)

server.listen(3000)

在 Node.js 中使用 fetch 请求接口可能会报错:

TypeError: fetch failed
at Object.fetch (node:internal/deps/undici/undici:11730:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
cause: Error: self-signed certificate
at TLSSocket.onConnectSecure (node:_tls_wrap:1674:34)
at TLSSocket.emit (node:events:514:28)
at TLSSocket._finishInit (node:_tls_wrap:1085:8)
at ssl.onhandshakedone (node:_tls_wrap:871:12) {
code: 'DEPTH_ZERO_SELF_SIGNED_CERT'
}
}

指定环境变量 NODE_TLS_REJECT_UNAUTHORIZED='0' 即可

npx cross-env NODE_TLS_REJECT_UNAUTHORIZED='0' node index.js