介紹
Material Design 是 Google 提供的一套設計標準, Material-UI 是參考其標準設計為 React 開發者使用的套件目前在 V1.0 BETA 版 ,最主要差異是導入了 Css In Js (在v1.0版之前是使用 Sass 模式),目前 Css In Js 有很多套而 Material-UI 是使用 Jss 來當作主要的 核心
V1.0 Next.js 上的設定
先安裝
npm install Material-UI @next --save
在安裝 如果要使用ICON作者也很貼心的加入到套件之中
npm install material-ui-icons
如果要單獨使用套件直接掛入就可以了,不過這樣是沒有 Theme 通常要再補一個 Provider提供佈景
import React from 'react';
import { render } from 'react-dom';
import Button from 'material-ui/Button';
function App() {
return (
<Button raised color="primary">
Hello World
</Button>
);
}
render(<App />, document.querySelector('#app'));
Jss 因為要在 Document 本文中先設定JssProvider ,在 Next.js 要覆寫 Document 本文就要在 pages 目錄底下設定 _document.js 這隻檔案
import React from 'react';
import Document, { Head, Main, NextScript } from 'next/document';
import JssProvider from 'react-jss/lib/JssProvider';
import getContext from '../styles/getContext';
class MyDocument extends Document {
render() {
return (
<html lang="en" dir="ltr">
<Head>
<title>My page</title>
<meta charSet="utf-8" />
<meta
name="viewport"
content={
'user-scalable=0, initial-scale=1, ' +
'minimum-scale=1, width=device-width, height=device-height'
}
/>
<link rel="manifest" href="/static/manifest.json" />
<meta name="theme-color" content={this.props.stylesContext.theme.palette.primary[500]} />
<link rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"
/>
</Head>
<body> <Main /> <NextScript /> </body>
</html>
);
}
}
MyDocument.getInitialProps = ctx => {
const context = getContext();
const page = ctx.renderPage(Component => props => (
<JssProvider registry={context.sheetsRegistry} jss={context.jss}>
<Component {...props} />
</JssProvider>
));
return {
...page,
stylesContext: context,
styles: (
<style
id="jss-server-side"
dangerouslySetInnerHTML={{ __html: context.sheetsRegistry.toString() }}
/>
),
};
};
export default MyDocument;
這邊 主要是在 MyDocment.getInitialProps 完成Jss Provider 設定,順便傳入 Material 的佈景主題到 Meta 之中
<meta name="theme-color" content={this.props.stylesContext.theme.palette.primary[500]} />
context 也是設定主要佈景的地方,會跟 client共用
stylesContext: context,
context 內容主要是 Jss 跟 Material 的設定 Material 會要先設定 Theme
然後透過 Jss 的 createContext 去創建給 stylesContext 使用,這邊順便注入到 SSR 的 Gobal 與 Client SPA 共用
import { create, SheetsRegistry } from 'jss';
import preset from 'jss-preset-default';
import { createMuiTheme } from 'material-ui/styles';
import purple from 'material-ui/colors/purple';
import green from 'material-ui/colors/green';
import createGenerateClassName from 'material-ui/styles/createGenerateClassName';
const theme = createMuiTheme({
palette: {
primary: purple,
secondary: green,
},
});
// Configure JSS
const jss = create(preset());
jss.options.createGenerateClassName = createGenerateClassName;
function createContext() {
return {
jss,
theme,
sheetsRegistry: new SheetsRegistry(),
};
}
export default function getContext() {
if (!process.browser) {
return createContext();
}
// Reuse context on the client-side
if (!global.__INIT_MATERIAL_UI__) {
global.__INIT_MATERIAL_UI__ = createContext();
}
return global.__INIT_MATERIAL_UI__;
}
接下來設定 SPA , Material-UI提供了一個 withStyles HOC 讓 Css 掛到指定的元件,在 componentWillMount
這 getContext (import getContext from '../styles/getContext')
就 SPA 跟 SSR 都同一隻設定值(不用分開寫),在 componentDidMount 的時候因為SSR會有些衝突所以就把多餘的節點先移除,在給 SPA 的 MuiThemeProvider 需要的 Theme 以及 sheetsManager (都來自設定 getContext),
這樣就會有 Theme 主題了 ,接下來就可以在 Next.js 輕鬆的使用 Material-UI,其他元件的部分因為都是隨裝即用可以參考 https://material-ui-next.com/
主要理解這邊提到的三隻程式的關聯就可以輕鬆快樂上手了
/* eslint-disable flowtype/require-valid-file-annotation */
import React, { Component } from 'react';
import { withStyles, MuiThemeProvider } from 'material-ui/styles';
import wrapDisplayName from 'recompose/wrapDisplayName';
import getContext from '../styles/getContext';
// Apply some reset
const styles = theme => ({
'@global': {
html: {
background: theme.palette.background.default,
WebkitFontSmoothing: 'antialiased', // Antialiasing.
MozOsxFontSmoothing: 'grayscale', // Antialiasing.
},
body: {
margin: 0,
},
},
});
let AppWrapper = props => props.children;
AppWrapper = withStyles(styles)(AppWrapper);
function withRoot(BaseComponent) {
class WithRoot extends Component {
static getInitialProps(ctx) {
if (BaseComponent.getInitialProps) {
return BaseComponent.getInitialProps(ctx);
}
return {};
}
componentWillMount() {
this.styleContext = getContext();
}
componentDidMount() {
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles && jssStyles.parentNode) {
jssStyles.parentNode.removeChild(jssStyles);
}
}
render() {
return (
<MuiThemeProvider
theme={this.styleContext.theme}
sheetsManager={this.styleContext.sheetsManager}
>
<AppWrapper>
<BaseComponent {...this.props} />
</AppWrapper>
</MuiThemeProvider>
);
}
}
if (process.env.NODE_ENV !== 'production') {
WithRoot.displayName = wrapDisplayName(BaseComponent, 'withRoot');
}
return WithRoot;
}
export default withRoot;
另外 JSS 的寫作風格是 利用駝峰 CSS 語法像一些有"-"的都要拿掉例如 background-image在JSS 就要寫成 backgroundImage ,通常都會把這些 CSS跟 Components放在同一層在 import 近來後透過 mateial-UI 提供的 withStyle HOC綁定到物件 ,物件的class 都要寫成 className
總結
Material-UI 使用了 Jss 駝峰寫法 Css In Js 的好處之一就是可以省去 perBuild (Css的語法都支援),也省去開發階段reBuild 工作,除了Next.js 設定稍微複雜一些,稍微胖一些.不過個人滿喜歡 Material-UI 風格的,這個相對於 styled-components 都是屬於 Css In Js,也是常常被比較, styled-components 比較輕 day7 來介紹一下
官方提供 Next.js 範例
https://github.com/mui-org/material-ui/tree/v1-beta/examples/nextjs