应使用 pinyin-pro 提取中文姓名和城市名首字母,因其纯函数式、无 dom 依赖、支持多音字与 fallback,而 js-pinyin 和 charat(0)/简单正则在 h5/app 端易报错且无法处理数字、符号、英文、藏维语等非汉字开头场景。

直接用 pinyin-pro 提取首字母,别碰 js-pinyin 或手写正则——前者在 H5/App 端常报 global is not defined,后者对“欧阳修”“阿Q”“360员工”完全失效。
为什么不能用 charAt(0) 或简单正则
中文姓名和城市名不是纯汉字:可能以数字、符号、英文、多音字开头。“张三”取 charAt(0) 是 “张”,但“360员工”会得 “3”,“阿Q”得 “阿”,无法归入索引栏;正则 /^[\u4e00-\u9fa5]/ 对非汉字开头直接返回 null,后续分组崩掉。真实数据里还存在藏语地名(如“拉萨”)、维语地名(如“乌鲁木齐”),pinyin-pro 默认返回空字符串,必须加 fallback。
实操建议:
- 安装:
npm install pinyin-pro --save - 引入(Vue 3 setup):
import { getPinyin } from 'pinyin-pro' - 提取逻辑必须兜底:
const initial = getPinyin(name || '', { style: 'first-letter' })?.toUpperCase() || '#' - 对 Unicode 非汉字开头做 fallback:
name.charCodeAt(0) > 128 ? 'A' : initial(覆盖藏/维语等场景)
pinyin-pro 和 js-pinyin 在 uni-app 里的关键差异
js-pinyin 依赖 document 或全局 RegExp 行为,在 App 端常报 Cannot read property 'match' of undefined;pinyin-pro 是纯函数式、无 DOM 依赖、支持 Tree Shaking,gzip 后仅约 12KB,且对多音字(如“重庆”的“重”)处理更稳。
常见错误现象:
- App 真机运行白屏或报错
global is not defined→ 用了已停更的pinyin或chinese-to-pinyin - H5 端点击索引跳转错位、滚动卡顿 → 没加节流,
@touchmove每秒触发几十次scroll-into-view - 列表里出现空的 “Z” 分组 → 分组后没过滤:
groups.filter(g => g.list.length > 0)必须加
如何封装成可复用的过滤器(Vue 3 + setup)
不要在 filters 目录下写全局过滤器(Vue 3 已废弃 Vue.filter),改用组合式函数或 computed 内联处理。
示例(utils/getInitial.js):
import { getPinyin } from 'pinyin-pro'
export function getInitial(name = '') {
if (!name) return '#'
const p = getPinyin(name, { style: 'first-letter' })
if (p && /^[a-zA-Z]$/.test(p)) return p.toUpperCase()
// fallback:非字母开头 → 判 Unicode
return name.charCodeAt(0) > 128 ? 'A' : '#'
}
在页面中使用:
import { getInitial } from '@/utils/getInitial'
// 处理原始列表
const grouped = computed(() => {
const map = new Map()
list.value.forEach(item => {
const key = getInitial(item.name)
if (!map.has(key)) map.set(key, [])
map.get(key).push(item)
})
return Array.from(map.entries())
.map(([letter, list]) => ({ letter, list }))
.sort((a, b) => {
if (a.letter === '#') return 1
if (b.letter === '#') return -1
return a.letter.localeCompare(b.letter)
})
})
排序和分组顺序不能颠倒
微信通讯录是先全局按拼音排序,再切片归组;如果先分组再各自排序,会导致“A”组内名字乱序(比如“阿里”排在“安琪”后面),用户一眼就能看出不对。
正确做法:
- 先排序:
list.sort((a, b) => a.name.localeCompare(b.name, 'zh-CN')) - 再分组:遍历已排序数组,用
getInitial()提取首字母,按 key 聚合 - 最后过滤空组:
.filter(g => g.list.length > 0)
索引数组(右侧 A-Z 栏)必须动态生成,不能硬写 ['A','B',...'Z']——真实数据可能只有 A、C、#,否则会出现点击无响应、高亮错位。











