Next.js 各种组合

Next.js 与各种技术栈的组合

Next.js + TailwindCSS + SB + zustand

注意: 不支持IE浏览器

1. VSCode 插件

  1. PostCSS Language Support

  2. Tailwind CSS IntelliSense

2. TailwindCSS

在 Next.js v10+ 项目中设置 Tailwind CSS

Step1. 创建项目
1
2

yarn create next-app --typescript
Step2. 安装Tailwind CSS

tailwindcss 通过 yarn 安装及其对等依赖项,然后运行 ​​init 命令生成 tailwind.config.jspostcss.config.js.

1
2
3
4

yarn add --dev tailwindcss postcss autoprefixer

npx tailwindcss init -p
Step3. 配置模板路径

在文件中添加所有模板文件的路径 tailwind.config.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22

module.exports = {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
// 或者(基于目录结构调整)
module.exports = {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx}",
    "./src/components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
Step4. 将 Tailwind 指令添加到 CSS

@tailwindTailwind 的每个层的指令添加到文件中。 ./styles/globals.css

1
2
3
4

@tailwind base;
@tailwind components;
@tailwind utilities;
Step5. 启动开发服务器
1
2

yarn dev
Step6. 开始在项目中使用 Tailwind

开始使用 Tailwind 的实用程序类来设计内容。

1
2
3
4
5
6
7
8

export default function Home() {
  return (
    <h1 className="text-3xl font-bold underline">
      Hello world!
    </h1>
  )
}

3. 主题切换

Step1. 安装 next-themes
1
2

yarn add next-themes
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13

// pages/_app.tsx
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { ThemeProvider } from 'next-themes';

function MyApp({ Component, pageProps }: AppProps) {
  return <ThemeProvider>
    <Component {...pageProps} />
  </ThemeProvider>
}

export default MyApp
Step2. 在 Next.js 创建一个 ThemeChanger.tsx 文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

// components/ThemeChanger.tsx
import { useEffect, useState } from 'react';
import { useTheme } from 'next-themes';

const themes = [{ name: 'Light' }, { name: 'Dark' }, { name: 'Emerald' }, { name: 'Pink' }];

const ThemeChanger = () => {
  const [mounted, setMounted] = useState(false);
  const { theme, setTheme } = useTheme();

  // 当安装在客户端时,现在可以显示 UI
  useEffect(() => setMounted(true), []);

  if (!mounted) return null;

  return (
    <div className="p-8 flex justify-between items-center font-bold text-xl bg-th-background-secondary text-th-primary-dark">
      <span>
        当前主题: <strong>{theme}</strong>
      </span>
      <div>
        <label htmlFor="theme-select" className="sr-only mr-2">
          选择主题:
        </label>
        <select
          name="theme"
          id="theme-select"
          className="bg-white text-gray-800 border-gray-800 border py-1 px-3"
          onChange={(e) => setTheme(e.currentTarget.value)}
          value={theme}
        >
          <option value="">选择主题</option>
          {themes.map((t) => (
            <option key={t.name.toLowerCase()} value={t.name.toLowerCase()}>
              {t.name}
            </option>
          ))}
        </select>
      </div>
    </div>
  );
};

export default ThemeChanger;
Step3. 启用
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

// pages/index.tsx
import type { NextPage } from 'next'
import Head from 'next/head'
import ThemeChanger from '../components/ThemeChanger';

const Home: NextPage = () => {
  return (
    <div>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <ThemeChanger />
      <main>
        <h1>
          Hello world!
        </h1>
      </main>

    </div>
  )
}

export default Home
Step4. 修改 globals.css 样式
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

// styles/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
html,
body {
  height: 100%;
  display: grid;
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}

#__next {
  height: 100%;
  grid-row: 1/-1;
  grid-column: 1/-1;
}

a {
  color: inherit;
  text-decoration: none;
}

* {
  box-sizing: border-box;
}

:root {
  --background: theme("colors.white");
  --background-secondary: theme("colors.gray.50");

  --primary-dark: theme("colors.gray.900");
  --primary-medium: theme("colors.gray.700");
  --primary-light: theme("colors.gray.500");
}

[data-theme="dark"] {
  --background: theme("colors.black");
  --background-secondary: theme("colors.gray.800");

  --accent-dark: theme("colors.fuchsia.900");
  --accent-medium: theme("colors.fuchsia.700");
  --accent-light: theme("colors.fuchsia.500");

  --primary-dark: theme("colors.gray.300");
  --primary-medium: theme("colors.gray.200");
  --primary-light: theme("colors.gray.100");
}

[data-theme="emerald"] {
  --background: theme("colors.white");

  --accent-dark: theme("colors.emerald.900");
  --accent-medium: theme("colors.emerald.700");
  --accent-light: theme("colors.emerald.500");
}

[data-theme="pink"] {
  --background: theme("colors.gray.900");
  --background-secondary: theme("colors.gray.800");

  --accent-dark: theme("colors.pink.900");
  --accent-medium: theme("colors.pink.700");
  --accent-light: theme("colors.pink.500");

  --primary-dark: theme("colors.gray.300");
  --primary-medium: theme("colors.gray.200");
  --primary-light: theme("colors.gray.100");
}

注意: 使用 yarn build前,需要把本地服务停掉,否则会构建失败。


4. 安装 Storybook

Step1. 安装 sb
1
2

npx sb init
Step2. 安装 postCSS 插件
1
2

yarn add -D @storybook/addon-postcss
Step3. 将插件添加到 .storybook/main.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22

// .storybook/main.js
module.exports = {
  ...
  addons: [
    ...
    {
      name: '@storybook/addon-postcss',
      options: {
        cssLoaderOptions: {
          // When you have splitted your css over multiple files
          // and use @import('./other-styles.css')
          importLoaders: 1,
        },
        postcssLoaderOptions: {
          // When using postCSS 8
          implementation: require('postcss'),
        },
      },
    },
  ],
};
Step4. 将 css 文件导入 .storybook/preview.js
1
2

import '../styles/globals.css'
Step5. 为 Storybook 配置 NextJS Images
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15

// .storybook/preview.js
+ import * as NextImage from "next/image";

+ const OriginalNextImage = NextImage.default;

+ Object.defineProperty(NextImage, "default", {
  configurable: true,
  value: (props) => (
    <OriginalNextImage
      {...props}
      unoptimized
    />
  ),
});
Step6. 为 Storybook 提供 Next.js 公共目录
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15

// package.json
{
  ...
  
  "scripts": {
    - "storybook": "start-storybook -p 6006",
    - "build-storybook": "build-storybook"
    + "storybook": "start-storybook -p 6006 -s ./public",
    + "build-storybook": "build-storybook -s public"
  },
  
 ...

}