Skip to content

Latest commit

 

History

History
180 lines (153 loc) · 5.04 KB

File metadata and controls

180 lines (153 loc) · 5.04 KB

主题

styled-components提供<ThemeProvider>来支持主题模式,底层实现是用React的context API。只要是被<ThemeProvider>包起来的组件,不管多深,都可以通过props访问到theme。

// 使用props.theme获取到主题
const Button = styled.button`
	font-size: 1em;
	margin: 1em;
	padding: 0.25em 1em;
	border-radius: 3px;

	/* 根据theme.main来设置文本和边框的颜色
	color: ${props => props.theme.main};
	border: 2px solid ${props => props.theme.main};
`;

// 默认主题
Button.defaultProps = {
	theme: {
		main: 'palevioletred'
	}
}

// 设计新的主题
const theme = {
	main: 'mediumseagreen'
};

render(
	<div>
		<Button>Normal</Button>

		<ThemeProvider theme={theme}>
			<Button>Themed</Button>
		</ThemeProvider>
	</div>
);

Refs

用ref只能获取到一个StyledComponent,因为styled-component是在原来的组件上包装了一层。要想获取到底层的DOM(比如为了调用focus方法),需要使用innerRef。

const Input = styled.input`
	padding: 0.5em;
	margin: 0.5em;
	color: palevioletred;
	background: papayawhip;
	border: none;
	border-radius: 3px;
`;

const Form = () => (
	<Input
		placeholder="Hover here..."
		innerRef={x => this.input = x}
		onMouseEnter={() => this.input.focus()}
	/>
);

render(
	<Form />
);

安全性

由于styled-components能使用任意js作为插值,要小心用户的恶意输入,造成XSS。

// 用户输入的可能是恶意接口,比如撤销转账
const userInput = '/api/withdraw-funds';

const ArbitraryComponent = styled.div`
  background: url(${userInput});
  /* More styles here... */
`;

对普通组件做样式化

styled-components的实现原理是生成实际的带class的css,然后把class通过className绑定到具体的DOM节点上,然后在运行的时候把实际生成的css加到html文档的head节点后面。

所以,如果你用styled(MyComponent)准备渲染MyComponent,而MyComponent并不渲染className,那就什么样式都不会出现咯!为了避免这个问题,需要将传入的className渲染到DOM节点上去。

const MyComponent = (props) => <div className={props.className}>...</div>

如果你之前已经有className了, 那么可以把两者合并起来。

const MyComponent = (props) => <div className=`your-global-class {props.className}`>...</div>

如果把全局的class跟styled-component混起来用,很可能得到莫名其妙的结果。主要问题来自于css的优先级,如果你忘了什么是css的优先级,请看这里。如果两个优先级相同,那么后者胜出。看下面代码:

// MyComponent.js
const MyComponent = styled.div`background-color: green;`;

// my-component.css
.red-bg {
  background-color: red;
}

// For some reason this component still has a green background,
// even though you're trying to override it with the "red-bg" class!
<MyComponent className="red-bg" />

你可能会认为.red-bg会胜出所以显示红色,因为red-bg这个className在后面,结果却显示绿色,原因是styled-components是在运行的时候动态把生成的css注入到head后面的DOM节点,所以其实它在后面。

很难控制生成的css的插入位置的,所以还不如直接提升全局class的优先级,方法就是重复class名称。

/* my-component.css */
.red-bg.red-bg {
  background-color: red;
}

Media Templates

如果要开发响应式web页面,则media query是不可或缺的工具。方法也很简单,就直接用css的media query咯:

const Content = styled.div`
	background: red;
	height: 3em;
	width: 3em;

	@media (max-width: 700px) {
		background: green;
	}
`;

render(
	<Content />
);

media query一般长而重复,所以我们可以写个模板方法。由于js的强大功能,你很容易就可以在media query里面写自定义的tagged template literal。

const sizes = {
	desktop: 992,
	tablet: 768,
	phone: 376
}

// Iterate through the sizes and create a media template
const media = Object.keys(sizes).reduce((acc, label) => {
	acc[label] = (...args) => css`
		@media (max-width: ${sizes[label] / 16}em) {
			${css(...args)}
		}
	`
	return acc
}, {})
// 上面代码看不懂的,建议看看函数式编程和reduce的作用,上述代码等价于
const media = {};
for(let label of Object.keys(sizes)) {
	media[label] = (...args) => css`
		@media (max-width: ${sizes[label] / 16}em) {
			${css(...args)}
		}
	`
}

const Content = styled.div`
	height: 3em;
	width: 3em;
	background: papayawhip;

	/* Now we have our methods on media and can use them instead of raw queries */
	${media.desktop`background: dodgerblue;`}
	${media.tablet`background: mediumseagreen;`}
	${media.phone`background: palevioletred;`}
`;

render(
	<Content />
);

Tagged Template Literals

参见styled-components设计原理

服务端渲染

参见官网文档

Rerfers