Skip to content

string 字符串工具

介绍

string 是字符串处理工具集,提供字符串判空、格式化、HTML 处理、URL 操作、路径处理等常用功能。该工具集涵盖了移动端开发中常见的字符串操作场景。

核心特性:

  • 判空处理 - 统一的空值判断和默认值处理
  • 格式化 - 首字母大写、截断、sprintf 格式化
  • HTML 处理 - HTML 转文本、富文本适配
  • URL 操作 - 查询参数解析和构建
  • 路径处理 - 路径规范化和匹配

判空处理

parseStrEmpty

将空值转换为空字符串:

typescript
import { parseStrEmpty } from '@/utils/string'

// 处理各种空值
console.log(parseStrEmpty(null))      // ''
console.log(parseStrEmpty(undefined)) // ''
console.log(parseStrEmpty(''))        // ''
console.log(parseStrEmpty('hello'))   // 'hello'
console.log(parseStrEmpty(123))       // '123'

// 实际应用
const displayName = parseStrEmpty(user.nickname) || '未设置昵称'

isEmpty / isNotEmpty

判断字符串是否为空:

typescript
import { isEmpty, isNotEmpty } from '@/utils/string'

// 判断空值
console.log(isEmpty(null))      // true
console.log(isEmpty(undefined)) // true
console.log(isEmpty(''))        // true
console.log(isEmpty('  '))      // true(空白字符也视为空)
console.log(isEmpty('hello'))   // false

// 判断非空
console.log(isNotEmpty('hello')) // true
console.log(isNotEmpty(''))      // false

// 表单验证
const validateForm = () => {
  if (isEmpty(form.username)) {
    showError('请输入用户名')
    return false
  }
  return true
}

格式化功能

capitalize

首字母大写:

typescript
import { capitalize } from '@/utils/string'

console.log(capitalize('hello'))  // 'Hello'
console.log(capitalize('HELLO'))  // 'Hello'(其余转小写)
console.log(capitalize(''))       // ''

// 格式化名称
const formatName = (name: string) => capitalize(name.toLowerCase())

truncate

字符串截断:

typescript
import { truncate } from '@/utils/string'

// 默认省略号
console.log(truncate('这是一段很长的文字', 10))
// '这是一段很长的文...'

// 自定义省略符
console.log(truncate('这是一段很长的文字', 10, '>>>'))
// '这是一段很长的>>>''

// 不需要截断时返回原字符串
console.log(truncate('短文本', 10))
// '短文本'

// 列表显示
const items = data.map(item => ({
  ...item,
  title: truncate(item.title, 20)
}))

sprintf

格式化字符串(类似 C 语言的 sprintf):

typescript
import { sprintf } from '@/utils/string'

// 基本用法
console.log(sprintf('Hello, %s!', 'World'))
// 'Hello, World!'

// 多个参数
console.log(sprintf('%s has %d apples', 'Tom', 5))
// 'Tom has 5 apples'

// 实际应用
const errorMessage = sprintf('第%d行第%d列数据错误', row, col)
const welcomeText = sprintf('欢迎回来,%s!', username)

HTML 处理

html2Text

HTML 转纯文本:

typescript
import { html2Text } from '@/utils/string'

// 移除 HTML 标签
const html = '<p>Hello <strong>World</strong></p>'
console.log(html2Text(html)) // 'Hello World'

// 处理富文本内容
const richContent = '<div><p>段落1</p><p>段落2</p></div>'
console.log(html2Text(richContent)) // '段落1段落2'

// 列表摘要显示
const summary = truncate(html2Text(article.content), 100)

adaptRichText

适配富文本内容(用于小程序 rich-text):

typescript
import { adaptRichText } from '@/utils/string'

// 适配图片宽度
const html = '<img src="xxx" style="width:800px">'
const adapted = adaptRichText(html)
// 图片宽度被调整为 100%

// 在 rich-text 组件中使用
<rich-text :nodes="adaptRichText(content)" />

URL 操作

getQueryObject

从 URL 解析查询参数:

typescript
import { getQueryObject } from '@/utils/string'

// 解析当前 URL
const params = getQueryObject()
console.log(params.id)    // URL 中的 id 参数
console.log(params.type)  // URL 中的 type 参数

// 解析指定 URL
const url = 'https://example.com/page?id=123&name=test'
const query = getQueryObject(url)
console.log(query) // { id: '123', name: 'test' }

objectToQuery

对象转查询字符串:

typescript
import { objectToQuery } from '@/utils/string'

// 基本用法
const params = { id: 123, name: 'test', type: 'user' }
console.log(objectToQuery(params))
// 'id=123&name=test&type=user'

// 构建 URL
const baseUrl = 'https://api.example.com/users'
const queryStr = objectToQuery({ page: 1, size: 10 })
const fullUrl = `${baseUrl}?${queryStr}`
// 'https://api.example.com/users?page=1&size=10'

// 过滤空值
const cleanParams = Object.fromEntries(
  Object.entries(params).filter(([_, v]) => v !== '' && v !== null)
)
const queryString = objectToQuery(cleanParams)

addQueryParams

向 URL 添加查询参数:

typescript
import { addQueryParams } from '@/utils/string'

// 添加单个参数
const url1 = addQueryParams('/api/users', { page: 1 })
// '/api/users?page=1'

// 添加多个参数
const url2 = addQueryParams('/api/users', { page: 1, size: 10 })
// '/api/users?page=1&size=10'

// 已有参数时追加
const url3 = addQueryParams('/api/users?type=admin', { page: 1 })
// '/api/users?type=admin&page=1'

路径处理

normalizePath

规范化路径:

typescript
import { normalizePath } from '@/utils/string'

// 处理多余斜杠
console.log(normalizePath('//pages//index//'))
// '/pages/index'

// 处理相对路径
console.log(normalizePath('./pages/index'))
// 'pages/index'

// 统一路径格式
const routes = [
  '/pages/index/',
  'pages/user',
  '//pages//detail//'
].map(normalizePath)
// ['/pages/index', 'pages/user', '/pages/detail']

isPathMatch

路径匹配判断:

typescript
import { isPathMatch } from '@/utils/string'

// 精确匹配
console.log(isPathMatch('/pages/index', '/pages/index'))
// true

// 通配符匹配
console.log(isPathMatch('/pages/user/*', '/pages/user/detail'))
// true

console.log(isPathMatch('/pages/**', '/pages/user/detail'))
// true

// 路由权限判断
const hasPermission = (path: string, allowedPaths: string[]) => {
  return allowedPaths.some(pattern => isPathMatch(pattern, path))
}

joinPath

路径拼接:

typescript
import { joinPath } from '@/utils/string'

// 基本拼接
console.log(joinPath('/api', 'users', '123'))
// '/api/users/123'

// 自动处理斜杠
console.log(joinPath('/api/', '/users/', '/123'))
// '/api/users/123'

// 构建 API 路径
const apiPath = joinPath(baseUrl, 'v1', 'users', userId)

格式转换

camelToKebab

驼峰转连字符:

typescript
import { camelToKebab } from '@/utils/string'

console.log(camelToKebab('backgroundColor'))
// 'background-color'

console.log(camelToKebab('fontSize'))
// 'font-size'

// 生成 CSS 类名
const className = camelToKebab(componentName)

kebabToCamel

连字符转驼峰:

typescript
import { kebabToCamel } from '@/utils/string'

console.log(kebabToCamel('background-color'))
// 'backgroundColor'

console.log(kebabToCamel('font-size'))
// 'fontSize'

// 解析 CSS 属性
const propName = kebabToCamel(cssProperty)

snakeToCamel

下划线转驼峰:

typescript
import { snakeToCamel } from '@/utils/string'

console.log(snakeToCamel('user_name'))
// 'userName'

console.log(snakeToCamel('created_at'))
// 'createdAt'

// API 响应数据转换
const formatResponse = (data: Record<string, any>) => {
  return Object.fromEntries(
    Object.entries(data).map(([key, value]) => [
      snakeToCamel(key),
      value
    ])
  )
}

实际应用场景

表单数据处理

typescript
import { isEmpty, parseStrEmpty, truncate } from '@/utils/string'

// 表单验证
const validateForm = (form: FormData) => {
  const errors: string[] = []

  if (isEmpty(form.username)) {
    errors.push('用户名不能为空')
  }

  if (isEmpty(form.email)) {
    errors.push('邮箱不能为空')
  }

  return errors
}

// 数据提交前处理
const prepareSubmitData = (form: FormData) => {
  return {
    username: parseStrEmpty(form.username).trim(),
    nickname: parseStrEmpty(form.nickname).trim(),
    remark: truncate(parseStrEmpty(form.remark), 200)
  }
}

API 请求构建

typescript
import { objectToQuery, joinPath, addQueryParams } from '@/utils/string'

// 构建请求 URL
const buildApiUrl = (
  endpoint: string,
  params?: Record<string, any>
) => {
  const baseUrl = import.meta.env.VITE_API_BASE_URL
  const url = joinPath(baseUrl, endpoint)

  if (params && Object.keys(params).length > 0) {
    return addQueryParams(url, params)
  }

  return url
}

// 使用示例
const url = buildApiUrl('/users', { page: 1, size: 10, status: 'active' })
// 'https://api.example.com/users?page=1&size=10&status=active'

文章列表显示

typescript
import { html2Text, truncate } from '@/utils/string'

// 处理文章摘要
const formatArticle = (article: Article) => ({
  id: article.id,
  title: truncate(article.title, 30),
  summary: truncate(html2Text(article.content), 100),
  author: article.author,
  createTime: article.createTime
})

// 批量处理
const formattedList = articleList.map(formatArticle)

API

判空函数

函数说明参数返回值
parseStrEmpty空值转空字符串(value: any)string
isEmpty判断是否为空(value: any)boolean
isNotEmpty判断是否非空(value: any)boolean

格式化函数

函数说明参数返回值
capitalize首字母大写(str: string)string
truncate字符串截断(str: string, length: number, suffix?: string)string
sprintf格式化字符串(format: string, ...args: any[])string

HTML 处理函数

函数说明参数返回值
html2TextHTML 转文本(html: string)string
adaptRichText适配富文本(html: string)string

URL 操作函数

函数说明参数返回值
getQueryObject解析查询参数(url?: string)Record<string, string>
objectToQuery对象转查询串(obj: Record<string, any>)string
addQueryParams添加查询参数(url: string, params: Record<string, any>)string

路径处理函数

函数说明参数返回值
normalizePath规范化路径(path: string)string
isPathMatch路径匹配(pattern: string, path: string)boolean
joinPath路径拼接(...parts: string[])string

格式转换函数

函数说明参数返回值
camelToKebab驼峰转连字符(str: string)string
kebabToCamel连字符转驼峰(str: string)string
snakeToCamel下划线转驼峰(str: string)string

最佳实践

1. 统一空值处理

typescript
// 封装显示文本函数
const displayText = (value: any, defaultText = '-') => {
  return isNotEmpty(value) ? String(value) : defaultText
}

// 使用
<text>{{ displayText(user.phone) }}</text>
<text>{{ displayText(user.email, '未绑定') }}</text>

2. URL 参数处理

typescript
// 安全获取 URL 参数
const getUrlParam = (key: string, defaultValue = '') => {
  const params = getQueryObject()
  return params[key] || defaultValue
}

// 构建分页 URL
const buildPageUrl = (page: number, size: number) => {
  const currentParams = getQueryObject()
  return objectToQuery({
    ...currentParams,
    page,
    size
  })
}

3. 文本安全显示

typescript
// 防止 XSS 的文本显示
const safeText = (text: string) => {
  return html2Text(text)
}

// 截断并安全显示
const safeDisplay = (text: string, maxLength: number) => {
  return truncate(html2Text(text), maxLength)
}

常见问题

1. isEmpty 对空白字符串的处理?

isEmpty 会将只包含空白字符的字符串视为空:

typescript
isEmpty('   ')  // true
isEmpty('\n\t') // true

2. truncate 中文字符计算?

truncate 按字符数计算,中文和英文都算一个字符:

typescript
truncate('你好世界', 2) // '你好...'
truncate('hello', 2)   // 'he...'

3. URL 编码问题?

objectToQuery 会自动进行 URL 编码:

typescript
objectToQuery({ name: '张三' })
// 'name=%E5%BC%A0%E4%B8%89'