@fluentui- react-native/use-styling (英语)
这个软件包提供了一个框架,用于构建一个能产生有观点的样式的钩子功能,既用于简单又用于更高顺序的组件. 这是本着以下原则建立的:
- 他们 - 使用 Styling 钩子允许根据主题对象中包含的信息构建样式。 这个包对主题系统的外形没有意见,而是在建钩时注入.
- 缓存 - 道具和样式的计算不应超过必要的次数。 结果将被缓存,以提高性能,并保持样式对象的一致对象引用.
- 重用 - 该系统的设计应尽量少地使用必要的密钥,尽量分享生成的样式。 这在为样式生成类名等情况下很重要.
一个极其简单的例子是:
// create the styling hook which can be used in the component, note that this gets created once and
// should not be done inside the component itself
const useSyling = buildUseStyling(myComponentOptions, themeHelper);
// component implementation, consuming the styling hook
const MyComponent = (props) => {
// get some default/styled props for the container and content child components
const { container, content } = useStyling(props);
// render as normal but mixin those values to pass the props to the sub-components
const { textValue, ...rest } = props;
const merged = { ...container, ...rest };
return (
<View {...merged}>
<Text {...content}>{textValue}</Text>
</View>
);
};
现在这个例子并不特别有用,而是展示了造型钩在HOC中可以起的基本作用.
快速引用
该系统的核心思想是: Tokens + Theme ==> Styles。这可以被认为是:
- 您的风格是通过一个食谱来制作的, 定义为在
slotProps - 代币是你的配方
- 其中一些信物来自常数
- 有些信物来自主题
- 有些指使甚至可能来自道具,但这不是默认,需要宣布为
tokensThatAreAlsoProps
该方法的总体特征如下:
const useStyling = buildUseStyling(
/* options that define the component */
{
/* Step 1: Build up a single Tokens object with constants for the component */
tokens?: [
/* object */
{ },
/* theme function */
(theme: Theme) => ({ /* created object */ }),
/* name to lookup */
'NameToLookup'
],
/* Step 2: Potentially apply states (if applicable) to the Tokens */
states?: ['hovered', 'pressed', 'disabled'],
/* Step 3: Copy some/none/all props to Tokens */
tokensThatAreAlsoProps?: 'all' /* all props are tokens */
| 'none' | undefined /* no props are tokens */
| ['token1', 'token2'] /* list of tokens which also appear in props */,
/* Step 4: Create props for child components from Tokens and Theme */
slotProps?: {
/* props as a constant object */
slot1: { },
/* props as a function of tokens and theme, with smart caching/filtering */
slot2: buildProps((tokens: Tokens, theme: Theme) => {
// function to generate props
return { style: { color: tokens.token1 }}
}, ['token1'])
/* props as a custom function of tokens, theme, cache */
slot3: (tokens: Tokens, theme: Theme, cache: GetMemoValue) => {
// function to generate props
}
}
},
/* theme helper plugin, global per theming system */
{
/* get the active theme, usually from the context */
useTheme: () => { return useContext(ThemeProvider) || getDefaultTheme() };
/* lookup a component in the theme to see if token overrides are available */
getComponentInfo: (theme: Theme, name: string) => lookupComponentInTheme(theme, name);)
}
);
键
一个符号仅仅是一个显示组件样式的设置. 这些可以被认为是框架自动所建的附加组件道具. 通常设定在组件创建时间或被一个主题所覆盖,但可以从多种来源获得:
- 对象 - 这是一个对象内包含的一组值。 假设这些值在物体的寿命期间保持不变.
- 主题函数 - 允许在一个主题中设定与值相关的值。 一个例子是,在主题中的彩色调色板中为特定条目设置颜色.
- 搜索名称 - 这将查询此组件的符号对象或主题函数的主题 .
示例
/** Tokens interface for this component */
interface Tokens {
backgroundColor?: ColorValue;
borderWidth?: number;
borderRadius?: AnimatableNumericValue | string;
}
/** Build the styling hook */
const useStyling = buildUseStyling({
tokens: [
/* fixed object */
{
borderWidth: 1,
borderRadius: 2,
},
/* theme function */
(theme: Theme) => {
backgroundColor: theme.palette.bodyBackground || 'white';
},
/* name lookup */
'MyComponentName',
],
...otherSettings,
});
层层
这些令牌会按照它们在数组中出现的顺序被深刻地合并.
const mergedTokens = merge(
...options.tokens.map(entry => {
// string gets converted to object or function (or empty object)
if (typeof entry === 'string') {
entry = themeHelper.getComponentInfo(entry) || {};
}
// return either the function call (which will produce an object) or the object
return typeof entry === 'function' ? entry(theme) : entry;
});
);
缓存
整个过程唯一的可变输入是主题. 因此,所有这一切的结果将基于:
- 使用Stypling 关闭, 每次关闭都会有自己独特的缓存值
- 主题对象引用,存储于 a
WeakMap
因此,这一计算将按每个主题、每个构成部分类型进行一次.
国家
一些组件可能具有额外的状态或模式,可以适用于它们. 举例来说,按钮组件可能有一个叫做徘徊或按住的状态。 在这种情况下,Tokens接口应如下:
interface ButtonTokens {
// base values
backgroundColor?: ColorValue;
color?: ColorValue;
borderColor?: ColorValue;
// states
hovered?: ButtonTokens;
pressed?: ButtonTokens;
}
在这种情况下 states 选项如下:
const useStyling = buildUseStyling({
tokens: [/* token settings */],
states: ['hovered', 'pressed'],
...otherSettings,
});
每一条目 states 数组,按其出现的顺序,将发生下列情况:
- 评估国家是否适用。 如果道具[状态]是真实的,或者在关闭时的通过会被评价为真实的,那么这是真实的. 更多关于这一点,下面.
- 如果状态是真实的,而令牌中包含一个与该名称相匹配的子对象,则子对象将被合并为母对象.
生成的 useStyling 钩子有一个可选的第二个参数,它是形式的一个函数:
type HasLayer = (state: string) => boolean;
这使得除道具外,还可以根据状态设定状态. 例如:
interface ButtonState {
hovered?: boolean;
pressed?: boolean;
}
const Button = (props: ButtonProps) => {
// get some state value that figures out whether the button is pressed and/or hovered
const buttonState: ButtonState = useButtonState(props);
// get styling values based on props and the current state
const { container, icon, label } = useStyling(props, (stateName) => buttonState[stateName]);
const buttonProps = useButtonMagic(props, container);
return (
<View {...buttonProps}>
<Image {...icon} source={props.iconSrc} />
<Text {...label}>{props.text}</Text>
</View>
);
};
托肯和道具
从缓存的角度来说,理想的情景是使道具和道具完全分开. 这使得可变性非常低,并减少了频繁进行样式重新计算的需要. 从使用组件的人的角度来看,这大大限制了灵活性。 因此,在部件上将一些道具浮出水面可能是有用的.
因为道具没有提供给最后的阶段 useStyling 工作流程,如果任何道具也是道具,就需要声明它们,以便它们能够覆盖来自主题或常数的道具值. 这是通过 tokensThatAreAlsoProps 属性。 这有三种行为:
| 数值 | 行为 |
|---|---|
'none' 或者说 undefined | 无地产为道具,道具将照旧. |
'all' | 所有属性都应视为符. 这相当于: tokens = { ...tokens, ...props } |
(keyof Tokens)[] | 提供从道具复制到符号的离散值列表( 如果有) |
槽道具
建设更高秩序部分,特别是可自定义部分的难度在于如何将一组道具映射成多组内部组件. 虽然标准是揭露 style 编辑 props 接口,此选项只能映射到一个内部组件.
通过直接瞄准子组件的样式来定制HOC是有问题的,因为它泄露了执行细节. 因此,长期维持或更新组件而不打碎客户变得具有挑战性.
B. 解决 useStyling 模块是允许将焦点定制到各种托肯值上,然后对图肯斯如何被翻译为每个儿童部分的道具进行定义的绘图。 在这个图书库的平面中,每个儿童部分被称作插槽.
每个槽直接或通过函数返回自己的道具对象.
直接对象
为一个槽提供道具的最简单方法是直接提供一个对象. 虽然适用范围有限,但看来如下:
const useStyling = buildUseStyling({
...precedingOptions,
slotProps: {
slot1: { style: { display: 'flex' } },
},
});
职能
功能假定有下列签字:
function propBuilder(tokens: Tokens, theme: Theme, cache: GetMemoValue<Props>): Props;
虽然上面已详细讨论过符号和主题,但缓存将是一个缓存实例,按着:
- 主题
- 主题上的标志
- 这个位置
如果没有也是道具的标志,即如果 tokensProps 功能可以是:
const slotFn = (tokens: Tokens, theme: Theme, cache: GetMemoValue<Props>) =>
cache(
() => {
return {
style: {
backgroundColor: tokens.backgroundColor,
// etc.
},
};
},
[/* no keys, just direct cache the function result*/],
)[0];
如果所有的道具都是道具,那么这些钥匙应该在缓存中加以考虑. 这样将变成:
const slotFn = (tokens: Tokens, theme: Theme, cache: GetMemoValue<Props>) => {
const { backgroundColor, color, borderRadius } = tokens;
return cache(
() => ({
style: { backgroundColor, color, borderRadius },
}),
[backgroundColor, color, borderRadius],
)[0];
};
构建Props 帮助器
图书馆提供名为buildProps的帮助器,使这个技术更能被人机化. 它为上面的全部缓存功能提供了一个实施,并将用于:
const slotFn = buildProps(
(tokens: Tokens, theme: Theme) => ({
style: {
backgroundColor: tokens.backgroundColor,
color: tokens.color,
borderRadius: tokens.borderRadius,
},
}),
['backgroundColor', 'color', 'borderRadius'],
);
这一职能提供的主要好处是: buildUseStyling 造起钩子时知道如何完善这些列表. 因此,缓存会根据其中的具体规定自动调整 tokenProps。 。 。 .