项目常用校验正则常量:用户名、手机号、密码(强 / 弱两档)、邮箱、验证码、URL
import {
REG_USER_NAME,
REG_PHONE,
REG_PWD,
REG_PWD_STRONG,
REG_EMAIL,
REG_CODE_SIX,
REG_CODE_FOUR,
REG_URL
} from '@skyroc/utils';reg 模块导出 8 个预定义正则常量,覆盖表单验证中最常见的校验场景。所有正则均为非全局模式(无 g 标志),可安全地在 test() 中复用而不存在状态问题。
| 常量 | 规则摘要 | 典型合法值 | 典型非法值 |
|---|---|---|---|
REG_USER_NAME | 4-16 位:中文 / 英文 / 数字 / _ / - | alice_01、张三 | ab(太短)、alice@(含非法字符) |
REG_PHONE | 中国大陆 11 位手机号 | 13812345678 | 12345678901(非法号段) |
REG_PWD | 6-18 位:字母 / 数字 / _(弱策略) | pass_123 | abc(太短)、pass word(含空格) |
REG_PWD_STRONG | 8-32 位非空白,须同时含大小写字母与数字 | Passw0rd! | password1(无大写)、Passw0r(太短) |
REG_EMAIL | 标准邮箱格式 | user@example.com | user@、@example.com |
REG_CODE_SIX | 恰好 6 位纯数字 | 123456 | 12345(5 位)、1234567(7 位) |
REG_CODE_FOUR | 恰好 4 位纯数字 | 1234 | 123、12345 |
REG_URL | HTTP(S) / 协议相对 URL,协议可省 | https://example.com/path?q=1、//cdn.a.com/x.js | ftp://…、example(无点) |
/^[\u4E00-\u9FA5a-zA-Z0-9_-]{4,16}$/^ 和 $)REG_USER_NAME.test('alice'); // true
REG_USER_NAME.test('张三_01'); // true
REG_USER_NAME.test('ab'); // false(太短)
REG_USER_NAME.test('alice@gmail'); // false(@ 非法)
REG_USER_NAME.test('a'.repeat(17)); // false(太长)/^1[3-9]\d{9}$/1,第二位 3-9,共 11 位REG_PHONE.test('13812345678'); // true
REG_PHONE.test('19912345678'); // true
REG_PHONE.test('12345678901'); // false(第二位是 2)
REG_PHONE.test('1381234567'); // false(只有 10 位)
REG_PHONE.test('+8613812345678'); // false(含国际区号)这里不枚举号段白名单。运营商放新号段时,枚举式正则会立刻失效误杀真实用户; 放宽到 1[3-9]
的漏判成本远低于误杀。真正的号码有效性交给短信验证码。
如需校验带国际区号的手机号,需使用专门的国际号码校验库(如
libphonenumber-js)。
/^\w{6,18}$/\w = [a-zA-Z0-9_](字母 + 数字 + 下划线)@!#)REG_PWD.test('pass_123'); // true
REG_PWD.test('UPPER_case_01'); // true
REG_PWD.test('abc'); // false(太短)
REG_PWD.test('pass word'); // false(含空格)
REG_PWD.test('p@ssword'); // false(含 @)这条规则禁止符号,是一个偏弱的口令策略。仅在需要兼容存量账号体系时使用,新项目请用下面的 REG_PWD_STRONG。
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)\S{8,32}$/\S)REG_PWD_STRONG.test('Passw0rd'); // true
REG_PWD_STRONG.test('P@ssw0rd!'); // true(符号是允许的)
REG_PWD_STRONG.test('password1'); // false(缺大写)
REG_PWD_STRONG.test('PASSWORD1'); // false(缺小写)
REG_PWD_STRONG.test('Password'); // false(缺数字)
REG_PWD_STRONG.test('Passw0r'); // false(太短)
REG_PWD_STRONG.test('Passw 0rd'); // false(含空格)前三个 (?=...) 是零宽断言,只做「存在性检查」不消费字符,真正决定长度和字符集的是末尾的 \S{8,32}。
/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/\w+([-+.]\w+)*,支持 +、-、. 分隔\w+([-.]\w+)*\.\w+([-.]\w+)*,支持子域名和多级后缀REG_EMAIL.test('user@example.com'); // true
REG_EMAIL.test('user+filter@gmail.com'); // true
REG_EMAIL.test('user.name@sub.domain.io'); // true
REG_EMAIL.test('user@'); // false
REG_EMAIL.test('@example.com'); // false
REG_EMAIL.test('user@example'); // false(无顶级域名)/^\d{6}$/精确匹配 6 位连续数字,不多不少。
REG_CODE_SIX.test('123456'); // true
REG_CODE_SIX.test('12345'); // false(5 位)
REG_CODE_SIX.test('1234567'); // false(7 位)
REG_CODE_SIX.test('12345a'); // false(含字母)/^\d{4}$/精确匹配 4 位连续数字,常用于支付密码、短信验证码等场景。
REG_CODE_FOUR.test('1234'); // true
REG_CODE_FOUR.test('0000'); // true
REG_CODE_FOUR.test('123'); // false(3 位)
REG_CODE_FOUR.test('12345'); // false(5 位)/^(https?:\/\/|\/\/)?([\w-]+\.)+[a-zA-Z]{2,}(:\d{1,5})?(\/[^\s?#]*)?(\?[^\s#]*)?(#\S*)?$/http://、https://,或协议相对的 //REG_URL.test('https://example.com'); // true
REG_URL.test('http://example.com/path?q=1#hash'); // true
REG_URL.test('https://example.com:8080'); // true
REG_URL.test('//cdn.example.com/a.js'); // true(协议相对)
REG_URL.test('www.example.com'); // true
REG_URL.test('example.com'); // true(有点即可,协议可省)
REG_URL.test('ftp://example.com'); // false(非 http/https)
REG_URL.test('not a url'); // false这条正则各段字符集互不重叠、量词不嵌套,失配时不会产生指数级回溯。 早期版本把 \w 同时放进相邻两个字符类里,是典型的
ReDoS 形状——校验一条恶意长串就能把主线程挂住。
function validatePhone(value: string): string | null {
if (!REG_PHONE.test(value)) return '请输入正确的手机号';
return null;
}<Form.Item
name="phone"
rules={[
{ required: true, message: '请输入手机号' },
{ pattern: REG_PHONE, message: '手机号格式不正确' }
]}
>
<Input placeholder="请输入手机号" />
</Form.Item><input
{...register('email', {
required: '请输入邮箱',
pattern: {
value: REG_EMAIL,
message: '邮箱格式不正确',
},
})}
/>import { z } from 'zod';
import { REG_PHONE, REG_EMAIL, REG_PWD } from '@skyroc/utils';
const loginSchema = z.object({
phone: z.string().regex(REG_PHONE, '手机号格式不正确'),
password: z.string().regex(REG_PWD, '密码为 6-18 位字母、数字或下划线')
});
const registerSchema = z.object({
email: z.string().regex(REG_EMAIL, '邮箱格式不正确'),
username: z.string().regex(REG_USER_NAME, '用户名为 4-16 位,支持中文、英文、数字、_、-'),
verifyCode: z.string().regex(REG_CODE_SIX, '请输入 6 位验证码')
});g 标志这些正则不含 g(全局)标志,可以安全地在多次 test() 调用中复用,不会因 lastIndex 状态导致奇怪的行为:
// ✅ 安全,无 g 标志时 lastIndex 始终为 0
REG_PHONE.test('13812345678'); // true
REG_PHONE.test('13812345678'); // true(仍然正确)
// ⚠️ 如果有 g 标志(本模块不存在此问题,仅作示意)
const reg = /^\d+$/g;
reg.test('123'); // true
reg.test('123'); // false(lastIndex 被修改,第二次从 lastIndex 开始匹配)Last updated on