2019 年 3 月 28 日 星期四
使用 Styled JSX 美化 Next.js
发布者Styled JSX 是一个 CSS-in-JS 库,它允许你编写封装和作用域的 CSS 来样式化你的组件。你为一个组件引入的样式不会影响其他组件,这让你可以在不担心意外副作用的情况下添加、更改和删除样式。
开始使用
Next.js 默认包含 Styled JSX,因此入门非常简单,只需在现有 React 元素中添加一个 <style jsx> 标签并在其中编写 CSS 即可
function Home() {
return (
<div className="container">
<h1>Hello Next.js</h1>
<p>Let's explore different ways to style Next.js apps</p>
<style jsx>{`
.container {
margin: 50px;
}
p {
color: blue;
}
`}</style>
</div>
);
}
export default Home;在这个例子中,我们为组件的容器元素和段落添加了样式。尽管我们使用了通用选择器,但这些样式不会影响其他组件中带有 container 类名或 <p> 标签的元素。这是因为 Styled JSX 确保这些样式只作用于此组件(通过向样式元素应用额外的唯一类名)。
只需在 <style> 元素上添加一个 jsx 属性,你就可以编写标准的 CSS,这些 CSS 将自动添加前缀并自动作用于组件。<style jsx> 元素应放置在组件的根元素内。
添加全局样式
大多数项目都需要一些全局样式来样式化 body 元素或提供 CSS 重置。Styled JSX 允许我们使用 <style jsx global> 添加全局样式。例如:
function Home() {
return (
<div className="container">
<h1>Hello Next.js</h1>
<p>Let's explore different ways to style Next.js apps</p>
<style jsx>{`
.container {
margin: 50px;
}
p {
color: blue;
}
`}</style>
<style jsx global>{`
p {
font-size: 20px;
}
`}</style>
</div>
);
}
export default Home;这将对此特定页面中所有 <p> 标签应用 20px 的字体大小。
要将全局样式应用于我们应用程序中的所有页面,一个好的方法是首先创建一个包含全局样式的布局组件,然后用它包装所有页面。
使用布局组件提供了灵活性,可以将一组样式应用于某些页面,同时仍然允许其他页面使用不同的样式
function Layout(props) {
return (
<div className="page-layout">
{props.children}
<style jsx global>{`
body {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: 400;
line-height: 1.8;
color: #333;
font-family: sans-serif;
}
h1 {
font-weight: 700;
}
p {
margin-bottom: 10px;
}
`}</style>
</div>
);
}
export default Layout;在 Next.js 中,我们可以通过在 pages/_app.js 中创建一个自定义 App 组件,导入 Layout 组件,然后将其添加到渲染方法中,从而为所有页面加载一次布局,如下所示:
import React from 'react';
import App, { Container } from 'next/app';
import Layout from '../components/Layout';
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<Container>
<Layout>
<Component {...pageProps} />
</Layout>
</Container>
);
}
}
export default MyApp;在外部文件中编写样式
我们也可以在组件外部的外部文件中编写样式。
例如,我们可以将全局样式从 Layout 组件移动到单独的文件中,如下所示:
import css from 'styled-jsx/css';
export default css.global`
body {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: 400;
line-height: 1.8;
color: #333;
font-family: sans-serif;
}
h1 {
font-weight: 700;
}
p {
margin-bottom: 10px;
}
`;然后我们可以将样式重新导入到 Layout 组件中
import globalStyles from '../styles/global.js';
function Layout(props) {
return (
<div className="page-layout">
{props.children}
<style jsx global>
{globalStyles}
</style>
</div>
);
}
export default Layout;一次性全局选择器
我们使用 <style jsx> 添加到组件的样式只会影响该组件内的元素,而不会影响子组件。
有时,我们可能需要覆盖子组件的某个样式。为此,Styled JSX 提供了 :global(),允许访问“一次性全局选择器”。
例如,假设我们有一个 <Widget> 组件,其中包含一个带有 btn 类名的按钮。如果只想在主页上导入 widget 时更改此按钮的颜色,我们可以这样做:
import Widget from '../components/Widget';
function Home() {
return (
<div className="container">
<h1>Hello Next.js</h1>
<Widget />
<style jsx>{`
.container {
margin: 50px;
}
.container :global(.btn) {
background: #000;
color: #fff;
}
`}</style>
</div>
);
}
export default Home;动态样式
与其他解决方案相比,能够根据组件的 props 调整样式是 CSS-in-JS 库的一大优势。
使用 Styled JSX,我们可以这样做:
function Alert(props) {
return (
<div className="alert">
{props.children}
<style jsx>{`
.alert {
display: inline-block;
padding: 20px;
border-radius: 8px;
}
`}</style>
<style jsx>{`
.alert {
background: ${props.type == 'warning' ? '#fff3cd' : '#eee'};
}
`}</style>
</div>
);
}
export default Alert;如果 Alert 组件传递了一个值为 warning 的 type prop,例如
<Alert type="warning">This is an important message</Alert>该组件将有一个橙色背景。如果不指定 type prop,背景将回退到默认的灰色。
请注意,我们将动态样式放在单独的 <style jsx> 标签中。这不是必需的,但建议将静态样式和动态样式分开,这样在 props 更改时只有动态部分才会被重新计算。
根据 props 值调整样式的另一种方法是根据 prop 值应用不同的类名,如下所示:
function Alert(props) {
return (
<div className={props.type == 'warning' ? 'alert warning' : 'alert'}>
{props.children}
<style jsx>{`
.alert {
display: inline-block;
padding: 20px;
border-radius: 8px;
background: #eee;
}
.alert.warning {
background: #fff3cd;
}
`}</style>
</div>
);
}
export default Alert;创建网站主题
主题可以是一个简单的对象,其中包含我们应用程序中可能需要的最常用变量
const theme = {
fontFamily: {
sansSerif: '-apple-system, "Helvetica Neue", Arial, sans-serif',
mono: 'Menlo, Monaco, monospace',
},
colors: {
text: '#333',
background: '#fff',
link: '#1eaaf1',
linkHover: '#0d8ecf',
border: '#ddd',
warning: '#fff3cd',
success: '#d4edda',
},
};
export default theme;然后我们在组件中导入这个主题文件,并用变量替换硬编码的值
import theme from '../styles/theme';
function Layout(props) {
return (
<div className="page-wrapper">
{props.children}
<style jsx global>{`
body {
background: ${theme.colors.background};
color: ${theme.colors.text};
font-family: ${theme.fontFamily.sansSerif};
}
`}</style>
<style jsx global>{`
body {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: 400;
line-height: 1.8;
}
h1 {
font-weight: 700;
}
p {
margin-bottom: 10px;
}
`}</style>
</div>
);
}
export default Layout;import theme from '../styles/theme';
function Alert(props) {
return (
<div className="alert">
{props.children}
<style jsx>{`
.alert {
display: inline-block;
padding: 20px;
border-radius: 8px;
}
`}</style>
<style jsx>{`
.alert {
background: ${props.type == 'warning'
? theme.colors.warning
: theme.colors.light};
}
`}</style>
</div>
);
}
export default Alert;在这篇博文中,我们介绍了如何开始使用 Styled JSX。要了解更多附加功能,请务必在 GitHub 上查看。