找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

150

积分

0

好友

18

主题
发表于 4 天前 | 查看: 9| 回复: 0

做数据前端开发,我们很快会形成一个共识:如何将枯燥的数字以恰当方式呈现,是我们的首要任务,但这仅仅是起点。

如果说规范的数字排版是中后台系统的"地基",确保了信息的准确传递;那么可视化图表就是地基之上的"建筑"。地基稳固,建筑才能充分发挥作用——让用户从微观数据阅读中解放出来,更快识别趋势、定位异常,真正从数据中获取价值。

本文重点讨论的并非那座"建筑",而是经常被忽视却决定系统专业度的"地基"——数字格式化。

观察得物各业务线的日常场景:

  • 商品详情页:券后价、折扣、分期单价等;
  • 智珠(得物社区运营平台)、得数(自助取数平台)、智能运营(得物交易运营平台)中的GMV、转化率、留存率、PVCTR等;
  • 社区帖子阅读量、点赞数、粉丝数。

这些数字表面只是数值,一旦显示在屏幕上,就自动成为版面组成部分:对齐方式、位数长短、小数位数、是否使用"万/亿"、单位放置——都会影响页面节奏和专业感。

排版本质是在管理信息与秩序:层级、节奏、对齐、留白。数字不是排版的"附属品",恰恰是这些原则最密集的载体。

本文专注"数字格式化"主题:

  1. 什么是数字格式化?背后有哪些文化差异和技术细节?
  2. 缺乏统一方案时,失控数字会给产品决策、UI排版和工程维护带来什么问题?
  3. 得物数据前端如何在得数/智珠/智能运营中将此事从"工具函数"升级为"基础设施"?
  4. 基于Intl.NumberFormat和@dfx/number-format的方案架构如何,带来哪些实际收益?

什么是"数字格式化"?

不只是toFixed(2)

提到数字格式化,第一反应常是:toFixed(2)、拼接%、添加¥符号,或通过正则添加千分位。但在真实场景中,"同一数值如何呈现"远比想象复杂。

数字写法本身就是"文化差异"

小数分隔符演变

据Florian Cajori 1928年著作《数学符号史》记载,小数分隔符演变经历了漫长标准化过程。

早期数学文献中,不同地区对小数的处理方式各异。以数值34.65为例,中世纪文献常在整数与小数间加横线或在个位数字上方加注标记。英国早期采用竖线"|"作为分隔,后在印刷中简化为逗号或点,这与当时阿拉伯数学家主要使用逗号的习惯相呼应。

"点"与"逗号"分流的历史成因

17世纪末至18世纪初是符号标准化的关键时期,也是英美体系与欧洲大陆体系产生分歧的起点:

  1. 欧洲大陆(莱布尼茨的影响):德国数学家莱布尼茨提议使用"点"作为乘法符号。经由克里斯蒂安·沃尔夫等学者推广,在欧洲大陆教科书中广泛普及。为避免符号冲突,欧洲大陆数学家普遍采用"逗号"作为小数分隔符。
  2. 英国(牛顿体系的延续):英国数学界未采纳莱布尼茨的乘法符号,而是沿用"X"表示乘法。因此"点"在英国未被乘法占用,得以继续作为小数分隔符使用。

标准的确立

尽管比利时和意大利等国曾长期坚持使用点作为小数分隔符,但最终均向欧洲大陆主流标准靠拢,改用逗号。至此,英美使用"小数点"、欧洲大陆使用"小数逗号"的格局基本定型。

  • 英语系/中国常见写法:1,234,567.89
  • 很多欧洲国家:1.234.567,89(点是千分位,逗号是小数)
  • 瑞士习惯:1'234.56或1'234,56,用撇号'做分组。

这些规则已被整理进Unicode CLDR和ICU数据库,现代浏览器的Intl.NumberFormat建立在这套数据之上,能根据locale自适应这些写法。

因此,简单的1,000:

  • 在美国人或中国人眼中是"one thousand/一千";
  • 在某些欧洲语境下可能被读作"保留三位小数的一点零"。

从事电商、跨境、数据产品时,这种"写法"差异不再是小问题,而是直接影响决策和合规性的因素。

数字不只是"大小",还有语义和语气

在UI中,我们经常表达"数字的语气":

  • +12.3%:不是纯数学"加号",而是"上涨"信号,排版上常配合红/绿颜色;
  • 1.2M/120万:为节省空间和降低认知负担,用缩写表示量级;
  • < 0.01:极小数值,让用户知道"接近0",而非盯着0.000032的数字尾部;
  • —/N/A:告知用户"此处无数据/异常",而非"数值为0"。

这些属于"数字的表达"而非"运算结果"。仅使用toFixed和字符串拼接,很难在全系统内保持这些语气一致。

浏览器和Node已提供"引擎"

ECMAScript 402标准提供的Intl.NumberFormat,是专门处理本地化数字格式化的构造器。

它支持:

  • 根据locale切换小数点、分组规则、数字符号;
  • 货币格式:style: "currency" + currency: "CNY" | "JPY" | ...;
  • 百分比:style: "percent",自动乘100并加%;
  • 紧凑表示:notation: "compact",输出9.9M、9.9亿等缩写;
  • formatToParts():将数字拆解为整数、小数点、小数、货币符号等片段,便于精细排版。

因此,数字格式化的"引擎问题"已解决——真正难点在于:

  • 如何结合业务语义;
  • 如何结合排版规范;
  • 如何在多系统间保持一致;
  • 如何治理"不乱写"的工程实践。

这就回到我们自己的故事。

如果没有统一方案:三个数据产品的日常

以下场景在得物或类似电商/数据平台中常见。

想象典型后台页面:

  • 顶部是活动看板KPI卡片;
  • 中间是按品类/渠道拆分的表格;
  • 下方是创作者表现列表。

看板示例

表格示例

列表示例

价格:同一张订单,不同系统"长得不同"

价格差异

以前的做法如何?在没有统一规范的"蛮荒时代",面对数字的第一反应常是:"这还不简单?拼个字符串不就完事了?"

这种代码你一定写过,或在项目角落见过:

场景一:简单粗暴的字符串拼接

// "我管你什么场景,先拼上去再说"
function formatPrice(value: number) {
  // 隐患埋雷:这里是全角¥还是半角¥?null会变成"¥null"吗?
  return '¥' + value.toFixed(2);
}

场景二:为千分位手写正则

// "网上一搜一大把的正则,看着能跑就行"
export const formatNumberWithCommas = (number: number | string) => {
  const parts = number.toString().split('.');
  // 经典的正则替换,但这真的覆盖所有负数、极大值场景吗?
  parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  // 手动补零逻辑,不仅难维护,还容易在边界情况(如undefined)下报错
  if (parts.length > 1 && parts[1]?.length === 1) {
    parts[1] = `${parts[1]}0`;
  }
  return parts.join('.');
};

这看起来"能用",但当业务复杂度上升或需要国际化时,接手者只能感叹。

  1. 视觉上的"各自为政"

    • 符号打架:商品卡片用半角¥,详情页用全角¥,甚至混用CNY。
    • 精度随心:有的开发觉得toFixed(2)严谨,有的觉得.00多余直接砍掉,导致同一页面数字像锯齿般参差不齐。
  2. 排版上的"秩序崩塌"

    • 试想表格列:1299、1,299.00、1299.0混在一起。
    • 对齐基准线完全错乱,尤其在表格中如同"狗啃过",用户眼睛需在不同小数位间跳跃,阅读体验极差。
  3. 国际化上的"死胡同"

    • 这种硬编码逻辑完全堵死国际化道路。
    • 一旦业务需支持USD(美元)、JPY(日元,默认无小数)、EUR(欧元,部分国家用逗号做小数点)。
    • 如1,234,567.89(英美)和1.234.567,89(德意),这不是改符号能解决的,而是数字书写逻辑的根本差异。

我们本想省事写的小函数,最终变成阻碍系统演进的技术债务。

看似智能的"万/亿"缩写,其实是硬编码的陷阱

缩写问题

在创作者中心或内容社区,希望阅读量(PV)符合用户直觉认知:

  • 小数值:< 10,000时,显示完整数字,精确到个位;
  • 中量级:≥ 10,000时,缩写为X.X万;
  • 海量级:≥ 100,000,000时,缩写为X.X亿。

海外版需求又变为:1.2k/3.4M/5.6B。

于是写出经典formatPv:

function formatPv(count: number) {
  if (count < 10000) return String(count);
  if (count < 100000000) return (count / 10000).toFixed(1) + '万';
  return (count / 100000000).toFixed(1) + '亿';
}

这段代码逻辑清晰,看似解决问题。但在真实场景中,它是"Bug制造机":

1.临界点的"视觉突变"

  • 9999下一秒变成1.0万?产品会立即质疑:"这个数展示是不是不对,能不能9999后显示1.0w而非1.0万?"
  • 99950四舍五入变成10.0万,是否显示更直观的10万?这些细节需大量if-else修补。

2.维护上的"复制粘贴地狱"

  • 中文版写一套,英文版formatPvEn再写一套,业务拓展至世界各地时需要多少formatPv(xx)?
  • A项目拷一份,B项目拷一份。当产品要求"万"后不留小数,需去N个仓库找出代码修改。最终全站缩写策略处于"相似但不一致"状态。

3.文化适配上的"盲区"

  • 这套逻辑是典型"简体中文中心主义"。
  • 印度用户看到100k无感,他们习惯用Lakh(10万)和Crore(1000万);
  • 阿拉伯语区可能需要用东阿拉伯数字。

这不仅是将"万"翻译成"Wan"的问题,而是数字分级逻辑在不同文化中完全不同。我们在UI硬塞"只能在简体中文自洽"的规则,这在国际化产品中行不通。

被"抹平"的语义—0、空值与极小值

语义问题

在得数(自助取数平台)、智珠(得物社区运营平台)、智能运营(得物交易运营平台)类重度数据看板中,充满各种比率型指标

  • 风控类:鉴别通过率、拦截率;
  • 履约类:超时率、投诉成功率;
  • 增长类:活动转化率、复购率。

面对这些指标,以前最常见处理是写通用formatPercent:

function formatPercent(value: number | null | undefined) {
  // "没数据就当0算呗,省得报错"
  return ((value || 0) * 100).toFixed(2) + '%';
}

这行代码虽只有一句,却在业务层面犯三个严重错误

1.混淆"事实"与"缺失"

  • null代表数据未产出或链路异常(即无数),而0代表业务结果确实为零。
  • 代码粗暴将null转为0.00%,会让用户误以为"今天无人投诉"或"转化率为0",从而掩盖系统故障或数据延迟。

2."抹杀"长尾数据价值

  • 对AB或算法模型,0.000032是具备统计意义的概率值。
  • 被代码强行截断为0.00%后,业务同学困惑:"为什么P值还能是0?"这直接损害数据公信力,影响业务决策。

3.阻断精细化表达可能 当想将极小值优化为< 0.01%这种更科学表达时,通过vscode搜索代码,500+文件直接让人无从下手。

从排版设计角度,三者本应拥有完全不同视觉层级:

  • 空值(No Data):使用—或灰色占位符,表示"此处无信息",降低视觉干扰;
  • 异常值(Error):使用N/A或警示色,提示用户"数据有问题";
  • 极小值(Tiny Value):使用< 0.01%或≈0,保留数据存在感,同时传达"接近于零"的准确语义。

得数DataHub治理展示层时发现,大量"同一指标在不同页面长得不一样",根源在于这些各自为政、缺乏语义区分的格式化规则。

从"写工具函数"到"定义展示语义"

语义定义

回顾上述场景,透过混乱代码片段,可发现三个共性系统难题:

  1. 逻辑熵增:每个业务线、甚至每个页面都在重复造轮子。formatPrice、formatPercent遍地开花,前后端逻辑割裂,维护成本随业务扩张指数上升。
  2. 无法治理:想统一全站"万/亿"阈值?或将某种费率精度从两位改四位?这几乎不可能。
  3. 体验失控:设计规范虽写"空值用—",但落实到代码全看开发心情。结果用户在不同系统间切换时,看到"似是而非"的统一,严重影响产品专业感。

为解决这些问题,在数据域产品中,我们对"格式化"重新定义:

它不只是前端的UI渲染逻辑,而是指标定义的一部分。

我们不仅要定义"指标的计算口径",更要定义"指标的展示语义"。

在Galaxy(指标管理平台)定义好"数怎么算出来"后,得数DataHub承担起定义"数该怎么被看见"的职责。我们将这层逻辑抽象为"展示语义"(Visualization Semantics)

  • 定类型(Type):它是金额(Currency)、比率(Ratio),还是计数(Integer)?
  • 定单位(Unit):默认是元/万元,还是%、‰(千分比)或bp(基点)?
  • 定精度(Precision):小数位固定保留两位,还是根据数值大小动态截断?
  • 定状态(State):遇到Null(空值)、Error(异常)或Epsilon(极小值),展示层如何兜底?
  • 定场景(Context):在空间局促的KPI卡片里(追求简洁),和需要财务核对的明细表格里(追求精确),是否应用不同渲染策略?

这是架构上的升维:

这些关于"长什么样"的逻辑,不再散落业务代码的if-else里,而是被统一收拢到元数据系统中管理。

从此刻起,数字格式化不再是前端模板里的小工具,而是成为得数体系的一项基础设施——一层独立、可配置、可治理的数据领域服务

开始造轮子:站在Intl.NumberFormat肩膀上

着手开发前,首先确立原则:底层能力不造轮子,拥抱Web标准。

ECMAScript 402标准中的Intl.NumberFormat已提供极其强大的本地化格式化引擎。它的能力远超大多数手写正则替换:

const n = 123456.789;
// 德国:点号分组、逗号小数
new Intl.NumberFormat('de-DE').format(n);
// "123.456,789"
// 印度:lakh/crore分组
new Intl.NumberFormat('en-IN').format(n);
// "1,23,456.789"
// 日元货币:默认0位小数
new Intl.NumberFormat('ja-JP', {
  style: 'currency',
  currency: 'JPY',
}).format(n);
// "¥123,457"

它完美解决那些最让前端头疼的国际化底层问题

  • 文化差异:全球各地的千分位、小数点、数字符号习惯;
  • 货币规则:不同币种的标准小数位(如日元0位,美元2位)和符号位置;
  • 多态支持:内置百分比、紧凑缩写(Compact Notation)、科学计数法等模式;
  • 排版能力:通过formatToParts()将数字拆解为数组(整数部分、小数部分、符号等),为精细化排版提供可能,如在小数或百分比符号比整数小2个字号。

但是,它天然"不懂"业务:

  • 它不懂中文习惯:无法直接实现"兆/京/垓"这种中文超大数缩写逻辑(标准最多支持到亿);
  • 它不懂得数规范:不知道*_rate类型指标在空值时要显示"-",在极小值时要显示< 0.01%;
  • 它不懂业务上下文:不知道GMV在KPI卡片里要按"万元"展示,而在财务报表里必须精确到"厘"。

因此,我们的最终架构策略是:

以Intl.NumberFormat为底层的"渲染引擎";在其之上搭建"数字领域层"(Domain Layer);专门用于转译得物的业务规则和排版语义。

@dfx/number-format:构建数字的"领域层"

在得物数据前端的大仓中,我们将这层能力实现为独立包:@dfx/number-format。专门服务得数、智珠、智能运营等内部系统。

可将其理解为三件事的组合:

  • 一个统一封装Intl.NumberFormat的核心引擎(含缓存、解析);
  • 一套可配置的业务规则/预设(preset)和插件系统;
  • 一组面向React和Node环境的接口(组件+Hook+FP函数)。

声明式开发:把"数字长什么样"抽象成规则

在业务开发侧,最终期望目标是:让开发者只关心"这是什么指标",而不关心"它该怎么展示"。

场景一:基础格式化 可直接使用组件,以声明式方式调用:

import { NumberFormat } from '@dfx/number-format';
// 无论在哪个页面,价格都只需这样写
<NumberFormat
  value={price}
  options={{ style: 'currency', currency: 'CNY' }}
/>

场景二:基于语义的自动格式化 更进阶用法是,在系统层面定义"规则集",业务组件只需传入指标名称:

import { NumberFormatProvider, AutoMetricNumber } from '@dfx/number-format';
// 1. 定义规则:所有"price.cny"类型指标,都遵循人民币格式
const rules = [
  { name: 'price.cny', options: { style: 'currency', currency: 'CNY' } },
];
// 2. 注入上下文
<NumberFormatProvider options={{ rules }}>
  {/* 3. 业务使用:完全解耦,只传语义 */}
  <AutoMetricNumber name="price.cny" value={price} />
</NumberFormatProvider>

收益显而易见:

  • 自动化:CNY自动带两位小数,切到JPY自动变0位小数,逻辑完全由底层接管。
  • 一致性:全站所有price.cny的地方,千分位、符号位置严格统一,版面节奏自然对齐。

场景三:批量匹配比率指标 对于成百上千个转化率指标,我们不需要逐一定义,只需一条正则规则:

const rules = [
  // 匹配所有以_rate结尾的指标,自动转为百分比,保留2位小数
  { pattern: /_rate$/i, options: { style: 'percent', maximumFractionDigits: 2 } },
];
// 页面代码极其干净
<AutoMetricNumber name="conversion_rate" value={conversionRate} />

空值与异常值如何展示、极小值是否显示< 0.01%、两位小数从哪里来,全部在规则+插件里处理。

插件系统:收口那些"奇怪但常见"的需求

得物业务场景中,存在大量Intl标准无法直接覆盖的边缘需求。我们将这些需求统一建模为插件(Format Plugin),介入格式化生命周期:

  • 千分比/基点(bps):需在pre-process阶段将数值乘1000或10000;
  • 中文大写金额:会计与合同场景的特殊转换;
  • 极小值兜底:设定阈值,当数值小于0.0001时,post-process阶段输出< 0.01%;
  • 会计格式:负数使用括号(1,234.56)而非负号;
  • 动态精度策略:根据数值大小动态决定保留几位小数。

这套插件机制的意义在于"治理":

它让"在某个业务仓库里偷偷写一个特殊正则"成为过去式。任何新的格式化需求,都必须以插件形式接入,由数据前端统一评审、沉淀,最终复用到全站。

全链路打通:从Galaxy元数据到UI渲染

全链路

最后,将这套系统与得数的中台能力打通(也是我们正在做的),形成完整渲染链路。

在Galaxy和得数的元数据中,指标本身已有code、label、type等字段。我们只需再加一点约定:

{
  "metricCode": "gmv",
  "label": "GMV",
  "format": "currency", // 基础类型
  "meta": {
    "formatConfig": {"style": "currency", "maximumFractionDigits": 2},
    "category": ["交易指标"],
    "displayConfig": { "table": "precise", "card": "compact" }
  }
}

前端渲染时:

  • 配置下发:页面加载时,获取指标的Code及元信息,转换为前端的MetricFormatRule[]。
  • 上下文注入:在应用根节点通过NumberFormatProvider注入规则集。
  • 傻瓜式使用:表格、卡片、图表组件只需消费指标ID:<AutoMetricNumber name={metricId} value={value} />

这样实现真正的『数据驱动UI』:

  • "指标长什么样"只在得数元数据管理中定义一次。
  • 修改展示规则(如调整精度),只需改配置,无需批量修改前端代码,更无需重新发版;
  • 前台业务与中台报表看到的同一个指标,格式永远物理一致。

统一之后:重塑设计、工程与业务的价值

从我们的视角出发,这套统一数字格式方案落地,带来的收益远不止"代码整洁",它在三个层面产生深远影响。

对设计与排版:版面终于可控了

曾经,产品需在每个页面验收中反复纠结数字对齐和精度。现在,设计规范只需在文档中定义一次:

  • 金额类:统一保留两位小数,强制右对齐,货币符号半角化;
  • 比率类:空值兜底为—,异常值显示N/A,极小值转译为< 0.01%;
  • 缩写策略:全站统一遵循"中文万/亿、英文k/M/B"的梯度逻辑。

开发同学不再『古法手搓』,而是直接接入统一的Preset和插件。

无论看板、详情页,还是导出的Excel报表,数字风格保持像素级一致。从视觉角度,我们相当于给数字建立了一套Design Token:精度、单位、占位符都有标准索引,让整个平台呈现高度统一的专业感和节奏感

对工程质量与效率:从"搜索toFixed"到"改一处生效全站"

所有数字格式逻辑集中在一个领域层和配置中心。

一类指标的规则变更(精度、单位、空值策略)可以配置化调整。

规约可进入Lint(数字格式化限制)/Code Review:

  • 禁止直接在业务代码中new Intl.NumberFormat、toFixed拼字符串;
  • 鼓励所有数字展示全部走@dfx/number-format与DataHub规则。

这是工程治理层面的价值:它将"依赖开发者自觉"的软约束,变成"可配置、可维护、可迭代"的系统能力

对业务和数据信任:从"怀疑这数有问题"到"愿意用来决策"

统一数字格式还有一个最隐性却最核心的价值:重构数据信任。

  • 消除歧义:前台商品价格与中台报表金额完全一致,运营不再因"显示精度不同"质疑数据准确性;
  • 精准语义:空值(Null)和零(Zero)被清晰区分,避免因格式问题导致的错误决策。

在得物,严谨是需要刻在基因里的关键词

作为数据前端,我们深知:懂数据,并能用最专业方式呈现数据,是这一岗位的核心素养。当我们不仅能交付代码,还能通过精准数字展示消除歧义、传递信任时,技术与业务间就建立起深层默契。

这种对数字细节的极致追求,不仅是对用户体验的尊重,更是对得物"正品感"品牌心智在数字世界的延伸。

数字,是版面的『最后一公里』

回头看,得物数据前端在得数、智珠、智能运营这条线做的,其实有两件事:

  • 给予数字应有的"设计尊严"

我们不再将数字视为模板字符串里随时可替换的"黑洞",而是将其视作与字体、色彩、间距同等重要的排版元素,纳入统一的设计语言系统。

  • 为数字构建专属的"基础设施"

我们以Web标准Intl.NumberFormat为引擎,以@dfx/number-format为领域层,以得数Schema为配置中枢,将"数字如何展示"这一命题,从硬编码的泥潭中解放出来,转变为一种可配置、可治理、可演进的系统能力。

当你再回头看公司产品的各个页面——

  • 前台商品详情页的价格和券后价;
  • 社区里的阅读和点赞数字;
  • 智珠的策略看板;
  • 得数的自助分析报表。

你会发现它们拥有一个共同特征:

这些数字不再是各说各话的噪点,而是共同操着同一种精准、优雅的"排版语言"。

这就是我们做"数字格式化"的初衷:用技术的确定性,换取业务的专注力。

此时,作为前端开发,你可以坚定地说出:我这展示的没问题,是数出问题了~

走出数据域:从内部门户到全业务

前面的篇幅更多围绕得数、智珠、智能运营等中后台系统展开。在这些场景下,数字格式化的核心用户是运营、分析师和算法同学——帮助他们透过数据看清业务走势。

但这套能力并不应局限于"后台"。它完全具备走出数据域的潜力,成为得物全业务线共享的一层关键基础设施

跨业务线:从"运营看板"走向"交易链路"

目前@dfx/number-format虽然生长于数据土壤,但其解决的问题是通用的。未来,它可以自然渗透到更广阔的业务场景:

  • 交易域:统一商品详情页、结算页的价格、分期费率、税费展示逻辑;
  • 社区域:标准化社区详情页中的风险分、阈值、命中率精度;
  • 客服域:规范赔付金额、工单时长的展示口径。

这在工程上意味着一次"升维":

  • 依赖升级:将@dfx/number-format从数据域私有包提升为公司级的基础依赖;
  • 开发范式:新业务在处理数字展示时,默认动作不再是"手写一个format函数",而是查阅现有的Preset和插件;

最终带来的体验质变是:

用户无论在得物App的前台页面,还是内部的各类管理系统中,看到的数字都拥有同一套呼吸感和视觉习惯。这种跨端的一致性,是品牌专业感最直接的体现。

跨地区与汇率:构建独立的"全球化价格能力"

随着得物业务出海,多地区、多币种是绕不开的挑战。此时,数字领域层不仅要负责"长什么样",更要承担起"展示策略"的职责。

我们可以设想一个「全球化价格组件」

<MultiRegionPrice
  skuId="123456"
  basePriceCny={price}
  regions={[
    { locale: 'zh-CN', currency: 'CNY' },
    { locale: 'en-US', currency: 'USD' },
    { locale: 'ja-JP', currency: 'JPY' },
  ]}
/>

这个组件将负责两层逻辑的解耦:

  • 计算层:负责实时汇率换算、多币种价格计算。
  • 展示策略层:
    • 对照展示:是否显示"原币种+本地参考价"(如JP¥20,000(≈$135.00));
    • 文化适配:是否遵循当地的"心理价位"策略(如$199.99 vs$200);
    • 格式渲染:最终调用@dfx/number-format,确保日元无小数、美元有小数、分节号正确。

从工程视角看,这避免"汇率算对了、数字排版全乱了"的尴尬;从产品视角看,这正是得物沉淀"出海技术套件"的重要一步(如国际智能运营)。

时间与日期:用同样的思路,去做第二条"格式化主线"

数字是一类挑战,"时间"则是另一类。跨时区转换、相对时间(此刻)、不同地区的日期写法(DD/MM vs MM/DD),其复杂度丝毫不亚于数字。

既然浏览器提供Intl.DateTimeFormat,我们完全可以复刻数字领域层的成功路径:

  • 基础设施:构建@dfx/date-format,统一封装时间格式化与相对时间逻辑;
  • 预设管理:定义标准Preset(如"运营报表用YYYY-MM-DD"、"C端动态用今天12:30");
  • 组件化:提供<DateTimeFormat />和<AutoMetricDate />组件;
  • 元数据打通:在得数的元数据中,针对时间型的维度也同样进行配置。

这样,数据前端的展示层就拥有两条清晰主线:

  • 数值主线:Intl.NumberFormat → @dfx/number-format → 数字展示规范
  • 时间主线:Intl.DateTimeFormat → @dfx/date-format → 时间展示规范

未来,我们甚至可以抽象出更上层的@dfx/data-format,让"任何字段该怎么展示",完全由Schema配置+领域层规则共同决定。

最后:把"展示"这件事做到极致

如果只看代码,我们做的事情似乎很简单:把Intl标准用到极致,封装了一层领域库,并接通了元数据配置,写了个工具库。

但如果从产品体验和工程演进的角度看,我们其实完成了一次基础设施的升级

把前端开发中最琐碎、最容易被忽视的"数字与时间展示",从"到处粘贴的小工具",升级成了"有统一规范、有可观测性、有迭代空间的系统能力"。

现在,这套能力已让得数、智珠、智能运营的部分模块长出统一的"数字气质"。

未来,期望它能够走出数据平台,支撑得物更广泛的业务场景,让同一件商品,在不同地区、不同语言、不同终端上,既算得对,又看得顺。

您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区(YunPan.Plus) ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-1 13:29 , Processed in 0.079205 second(s), 38 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 CloudStack.

快速回复 返回顶部 返回列表