ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

styled-components React Native 的 rem 单位支持:根字体锚定、calc() 集成与跨平台实现

styled-components React Native 的 rem 单位支持:根字体锚定、calc() 集成与跨平台实现 styled-components React Native 的 rem 单位支持根字体锚定、calc() 集成与跨平台实现【免费下载链接】styled-componentsFast, expressive styling for React. Server components, client components, streaming SSR, React Native—one API.项目地址: https://gitcode.com/gh_mirrors/st/styled-componentsrem是 CSS 中锚定根元素字体大小的相对长度单位。在 styled-components 的 React Native 渲染路径中rem如今被一等公民地支持它按应用的根字体大小换算默认根字体为16因此width: 1rem渲染为16、width: 2rem渲染为32。本文基于 native-rem-unit.md 变更说明结合packages/styled-components/src下的源码与测试完整讲解rem在 RN 中的用法、默认值与换算规则、calc()内的支持情况以及 react-native-web 上的行为差异。读完你将掌握在 styled-components 中跨 Web 与 React Native 统一书写相对长度样式的方法。rem 是什么锚定根字体大小的相对长度与em相对父元素字号不同remroot em始终相对文档根元素的字体大小解析。在浏览器里根元素通常是html其默认字号通常由浏览器设置决定桌面浏览器常见为 16px。RN 没有 DOM 级联也没有全局html元素因此 styled-components 的 native 实现需要自己定义根字体大小这一概念——这就是 NativeStyleContext.ts 中NativeCascadeValues.rootFontSize字段的来源export interface NativeCascadeValues { /** Parents resolved font-size in px. Anchors em resolution. */ fontSize: number; /** Parents resolved line-height in px. Anchors lh resolution. ... */ lineHeight: number; /** Root font size; anchors rem and rlh. */ rootFontSize: number; // ... } export const DEFAULT_CASCADE: NativeCascadeValues { fontSize: 16, lineHeight: 24, rootFontSize: 16, direction: ltr, };从源码可见rootFontSize的默认值就是16与 Web 浏览器通常的默认字号一致。DEFAULT_CASCADE作为NativeStyleContext的默认上下文值在组件树没有显式提供该值时生效这正是默认 16这一规则的实现落点。基本用法与换算规则在 styled-components 的 RN 组件中直接书写带rem的值即可无需任何额外配置import styled from styled-components/native; const Box styled.View width: 1rem; /* 渲染为 16 */ height: 2rem; /* 渲染为 32 */ margin: 0.5rem; /* 渲染为 8 */ ;换算规则简单直接数值 × 根字体大小。默认根字体为 16 时书写值换算结果width: 1rem16width: 2rem32width: 0.5rem8width: 1.5rem24若应用显式提供了非默认的根字体大小见下文源码说明则按实际值等比缩放。源码中的解析与换算rem的识别发生在编译期。在 resolvers.ts 中所有字体相对单位共享一个最长优先的正则// rem / rlh must come BEFORE em / lh in the alternation so // Font-relative units: cascade-anchored (em / lh, parent) or root-anchored // (rem / rlh, root). ... // longest-first so 12rem matches rem not 12r em. const REM_UNIT_RE /^(-?(?:\d(?:\.\d)?|\.\d))(rcap|rch|rex|ric|rem|rlh|cap|ch|em|ex|ic|lh)$/i;注意注释中强调的细节rem在备选分支中必须排在em之前且正则按最长优先排列确保12rem被识别为rem而不是12rem。识别到rem后buildResolver 会返回一个渲染期解析器const rem REM_UNIT_RE.exec(value); if (rem ! null) { return fontRelativeResolver(parseFloat(rem[1]), rem[2].toLowerCase()); }真正的乘法在fontRelativeResolver中完成resolvers.tsfunction fontRelativeResolver(n: number, unit: string): Resolver { switch (unit) { case rem: return env n * env.rootFontSize; case rlh: return env n * env.lineHeight; case em: return env n * env.fontSize; case lh: return env n * env.lineHeight; // ... } }rem与em的关键差异在源码注释中说得非常清楚resolvers.tsrem/rlh锚定根env.rootFontSizeem/lh锚定父元素env.fontSize/env.lineHeight。RN 没有 DOM 级联这些值由NativeStyleContext在渲染边界填充。这意味着在 RN 中rem是全树一致的固定基准不会因为某个祖先节点改了字号而波动——这正是rem相对em的核心优势。测试验证换算关系有据可查rem的换算行为有明确的单测覆盖见 resolvers.test.tsdescribe(rem (CSS Values 4 §6.1.1), () { it(..., () { expect(buildResolver(1rem)!(baseEnv)).toBe(16); expect(buildResolver(2rem)!(baseEnv)).toBe(32); expect(buildResolver(0.5rem)!(baseEnv)).toBe(8); }); it(..., () { // env 中显式传入 rootFontSize: 20 时 expect(buildResolver(1rem)!(env)).toBe(20); expect(buildResolver(1.5rem)!(env)).toBe(30); }); });测试同时验证了两种场景默认环境下1rem → 16、2rem → 32、0.5rem → 8以及在显式提供rootFontSize环境值时按比例换算1rem → 20、1.5rem → 30。后者说明如果应用的根字体大小可配置rem会自动跟随无需改写任何样式代码。此外rem也会在font-size、line-height等属性上以原始字符串形式保留给级联解析器处理见 polyfills.test.ts 与 L5341-5342 的rem passes through as raw for the cascade resolver用例说明解析流程对声明位置尺寸属性 vs 字体相关属性做了分层处理。在 calc() 中使用 rem变更说明明确写道rem既支持单独使用也支持出现在calc()内部。也就是说下面的写法是合法的const Card styled.View width: calc(100% - 2rem); /* 100% 减 32 */ padding: calc(1rem 4px); /* 16 4 20 */ ;从源码实现看calc(开头的值在 buildResolver 中会进入resolveMathFn分支由mathFnResolver在渲染期求值其中的动态臂包括rem这类依赖级联上下文的单位会被替换为具体数值后再计算。注释同时指出resolvers.tsWeb 端静态混合单位表达式如calc(33% - 5px)会原样交给浏览器 CSS 引擎因为浏览器能按真实包含块在绘制期解析%精度更高而 RN 端 Yoga 无法解析 calc 字符串因此mathFnResolver总是对%按最近的容器宽度env.container.width或视口宽度env.media.width求值。若需要像素级精确的%解析源码建议在布局根上设置container-type: inline-size。react-native-web交给浏览器处理变更说明的最后一点是在 react-native-web 上rem由浏览器原生处理styled-components 不做任何换算。这一点与源码中的平台分支完全吻合const vp VP_UNIT_RE.exec(value); if (vp ! null) { // The browser distinguishes dvh / svh / lvh against the real URL-bar // state; a JS resolver would collapse them to one number. Leave the // authored unit on the value so rn-web forwards it to CSS unchanged. if (__NATIVE_WEB__) return null; return viewportResolver(parseFloat(vp[1]), vp[2].toLowerCase()); }同理在 resolvers.ts 的注释中写着em/rem等字体相对单位的取值来自ResolveEnv由NativeStyleContext在渲染时填充rn-web passes them through to the browser unchangedrn-web 将它们原样传给浏览器。这一设计带来一个实用的跨平台特性React Native真机rem被编译期解析、渲染期换算为 dp 数值react-native-webrem原样进入浏览器的 CSS 层由浏览器按根元素字号解析。两种平台下你都可以写同一套rem样式行为各自由最合适的执行者保证。相关单位与注意事项rem并非唯一的字体相对单位。在 resolvers.ts 的fontRelativeResolver中还统一处理了em、lh、rlh以及ex、cap、ch、ic及其r-变体。其中ex/cap/ch/ic这类依赖字形度量的单位由于 RN 不暴露字形度量采用了固定近似系数EX_FRACTION 0.5、CAP_FRACTION 0.7等见 resolvers.ts。rem不依赖字形度量换算完全确定因此是 RN 场景下最可靠、最可预期的字体相对单位。在响应式媒体查询中rem同样被识别responsive.ts 的parseLength将em/rem按 16px 换算参与断点比较并有对应测试覆盖responsive.test.ts如(min-width: 20em)等价于 320 视口宽度。需要注意的是该媒体查询路径按固定 16 换算与NativeStyleContext中可配置的rootFontSize是两个独立的机制。使用建议在需要整树一致、不受父元素字号影响的相对尺寸时优先使用rem在希望尺寸跟随父元素字号如按钮内图标随文字缩放时使用em若项目需要自定义根字体基准从源码结构看关键锚点即NativeStyleContext中的rootFontSize默认 16见 NativeStyleContext.ts改动该值即可让全树rem按新基准等比缩放。总结styled-components 为 React Native 带来了完整的rem长度单位支持默认以根字体大小 16 为锚点1rem → 16、2rem → 32支持小数与负数既可直接书写也可嵌入calc()使用在 react-native-web 上则原样交由浏览器处理。其实现横跨 NativeStyleContext.ts根字体上下文、resolvers.ts编译期识别与渲染期换算、responsive.ts媒体查询换算三个模块并有 resolvers.test.ts 等测试锁定换算行为。对于希望在 Web 与 RN 之间复用同一套相对长度样式的项目rem提供了一个确定、可预期且跨平台一致的答案。【免费下载链接】styled-componentsFast, expressive styling for React. Server components, client components, streaming SSR, React Native—one API.项目地址: https://gitcode.com/gh_mirrors/st/styled-components创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表