如何在React Native中设计主题机制
发布网友
发布时间:2022-05-10 22:11
我来回答
共1个回答
热心网友
时间:2022-04-20 07:46
1:样式变量
web上,我们从css到scss/sass/less,然后我们才有变量可以用。然而在RN里,我们的样式本来就是JS化的,我们可以自由的使用JavaScript的变量以及其它特性来完成样式化:
// theme.js
var globalTextColor = '#000000',
mole.exports = {
backgroundColor: '#FFFFFF',
title: {
size: 32,
color: globalTextColor
},
content: {
size: 16,
color: globalTextColor
}
};
// styles.js
var React = require("react-native");
var {StyleSheet} = React;
var theme = require("./theme")
mole.exports = StyleSheet.create({
titleText : {
fontWeight: 'bold',
...theme.title
},
container1: {
background: theme.backgroundColor
},
container2: {
background: theme.backgroundColor
},
content: {
...theme.content
}
})
这样做,我们已经可以把一些需要经常修改的样式(如颜色、字体、尺寸等)和一些结构性的样式(譬如flex、position)等拆分开来,并且可以定义一些可以影响到多个样式的变量(如上文的globalTextColor)。对于日常的样式迭代,这已经可以满足一部分需求了,我们还可以根据自己的需要进行一些定制的预计算。而且这应当是最易理解的方式。不过这并不能实现用户切换主题的需求。
2:在组件上使用多个样式
类似ReactJS中我们使用classnames插件,ReactNative天然就支持使用数组提供多个样式的方式:
<View style={[styles.base, styles.background]} />
所以我们也可以在组件里分开引用默认样式和主题化的样式:注意这里theme返回的不是上文中的一个纯对象,而是另一个stylesheet
var React = require('React');
var styles = require('./styles');
var theme = require('../theme');
class Article extends React.Component {
render(){
return (
<View style={[style.container, theme.container]}>
<Text style={[styles.title, theme.title]}>
{this.props.title}
</Text>
<Text style={[styles.content, theme.content]}>
{this.props.content}
</Text>
</View>
);
}
};
3. 附加样式
首先,当我们实现一个通用组件的时候,应当让它可以在默认的style的基础上允许附加style(也即:允许外部进一步指定style属性):
var styleSheet = StyleSheet.create({
container: {
flex: 1
}
})
class Article extends React.Component{
render(){
var {
title, content,
style, titleStyle, contentStyle,
...others
} = this.props;
if (Array.isArray(style)){
style = [styleSheet.container, ...style];
} else {
style = [styleSheet.container, style];
}
//或者合并成这样一句: style=[styleSheet.container].concat(style);
return (
<View style={style} {...others}>
<Title style={titleStyle}>
{title}
</Title>
<Content style={contentStyle}>
{content}
</Content>
</View>
)
}
}
// Now you can use in this way:
<Article style={theme.article} title="Title" content="Foo" />
如果所有组件代码都支持附加样式,我们就比较方便通过props来传递额外的样式,这样,我们就可以引入一个新的黑科技:
4. 叠加默认属性黑魔法: 使用增强组件(EnhancedComponent)模式
如果我们可以给一个组件叠加一个默认样式,就可以让我们的许多工作简单的多(譬如上文的Title和Content,可以在Text的基础上直接叠加默认属性和样式得到)
export var Title = enhanceComponent(Text, {
style: styleSheet
});
export var Content = enhanceComponent(Text, {
style: {
fontSize: 16,
}
});
实际上,这个enhanceComponent很容易实现:
function enhanceComponent(Component, props){
var {style, ...others} = props;
return class extends React.Component {
render(){
var newProps = {...props};
for (var k : this.props){
if (/[sS]tyle$/.test(k) && newProps[k]) {
// merge style
newProps[k] = [].concat(newProps[k], this.props[k]);
} else if (k != 'children') {
// assign normal prop.
newProps[k] = this.props[k];
}
}
return (
<Component {...newProps}>
{this.props.children}
</Component>
)
}
};
}
所有名字以style或Style结尾的的属性被当做样式处理(处理类似titleStyle的情况)
5. 主题化组件
在3和4的基础上,结合ES6的一部分语法,我们应当可以以一种反向的方法来提供主题:提供一套主题化的组件。在此之前,我们的Article还需要做一些修改来适应:
var styleSheet = StyleSheet.create({
container: {
flex: 1
}
})
class Article extends React.Component{
render(){
var {
title, content, style,
createTitle, createContent
...others
} = this.props;
if (Array.isArray(style)){
style = [styleSheet.container, ...style];
} else {
style = [styleSheet.container, style];
}
//或者合并成这样一句: style=[styleSheet.container].concat(style);
return (
<View style={style} {...others}>
{createTitle ? createTitle(title) : <Title>
{title}
</Title>}
{createContent ? createContent(content) : <Title>
{content}
</Title>}
</View>
)
}
}
// Now you can use in this way:
<Article title="Title" content="Foo" createTitle={title=><Title style={theme.title}>{title}</Title>}/>
上面这个修改增加了createTitle和createContent函数,而取消了titleStyle和contentStyle(不够通用),实际上这也接近大部分内置库或第三方库所提供自定义化组件的方式(譬如为ListView提供ScrollBar)。
然后,我们可以编写这样一个themedComponents.js
// themedComponents.js
var themes = require("./themes");
import Article, {Title, Content} from './components/Article';
export var ThemedTitle = enhanceComponent(Title, {
style: themes.title
});
export var ThemedContent = enhanceComponent(Text, {
style: themes.content
});
export var ThemedArticle = enhanceComponent(Article, {
style: themes.article,
createTitle: title=>(<ThemedTitle>title</ThemedTitle>),
createContent: content=>(<ThemedContent>title</ThemedContent>),
});