NodeJS系列(12)- Next.js 框架 (五) | 样式 (Styling) 、部署(Deploying)

发布时间 2023-08-18 21:28:07作者: 垄山小站

在 “NodeJS系列(8)- Next.js 框架 (一) | 安装配置、路由(Routing)、页面布局(Layout)” 里,我们简单介绍了 Next.js 的安装配置,创建了 nextjs-demo 项目,讲解和演示了 Next.js 项目的运行、路由(Routing)、页面布局(Layout)等内容。

在 “NodeJS系列(9)- Next.js 框架 (二) | 国际化 (i18n)、中间件 (Middleware)” 里,我们在 nextjs-demo 项目基础上,讲解和演示了 Next.js 项目的国际化 (i18n)、中间件 (Middleware) 等内容。

在 “NodeJS系列(10)- Next.js 框架 (三) | 渲染(Rendering)” 里,我们在 nextjs-demo 项目基础上,讲解和演示了渲染(Rendering)。

在 “NodeJS系列(11)- Next.js 框架 (四) | 数据获取(Data Fetching) ” 里,我们在 nextjs-demo 项目基础上,讲解和演示了数据获取(Data Fetching)。

本文继续在 nextjs-demo 项目(Pages Router)基础上,讲解和演示样式 (Styling) 、部署(Deploying)等内容。

NextJS: https://nextjs.org/
NextJS GitHub: https://github.com/vercel/next.js

 

1. 系统环境

    操作系统:CentOS 7.9 (x64)
    NodeJS: 16.20.0
    NPM: 8.19.4
    NVM: 0.39.2
    NextJS: 13.4.12


2. 样式 (Styling)

    Next.js 支持如下的应用程序样式设置方式:
        
        (1) 全局 CSS:对于那些有传统 CSS 经验的人来说,使用起来很简单,也很熟悉,但随着应用程序的发展,可能会导致更大的 CSS 捆绑包和管理样式的困难。
        (2) CSS 模块:创建本地作用域的 CSS 类,以避免命名冲突并提高可维护性。
        (3) JS 中的 CSS:直接在 JavaScript 组件中嵌入 CSS,实现动态和范围化的样式。
        (4) Sass:一个流行的 CSS 预处理器,它通过变量、嵌套规则和 mixin 等功能扩展 CSS。
        (5) Tailwind CSS:一个实用程序优先的 CSS 框架,允许通过组成实用程序类来快速定制设计。

    1) 全局 CSS

        在 nextjs-demo 项目里有一个 src/styles/globals.css 文件,这是由 create-next-app nextjs-demo 命令自动创建的,它就是 nextjs-demo 项目的一个全局 CSS 文件。内容如下:

            ...

            html,
            body {
                max-width: 100vw;
                overflow-x: hidden;
            }

            ...


        全局 CSS,顾名思义,就要将该文件里 CSS 能应用于 nextjs-demo 项目里的全部页面和组件。 由于这种样式表的全局性,为了避免冲突,只能在 src/pages/_app.js 中导入它们。

        修改 src/pages/_app.js 文件,内容如下:

            import '../styles/globals.css'

            export default ({ Component, pageProps }) => {
                return (
                    <Component {...pageProps} />
                )
            }


    2) CSS 模块

        Next.js 内置支持使用 *.module.CSS 扩展名的 CSS 模块。
    
        CSS 模块通过自动创建一个唯一的类名来对 CSS 进行本地作用域。这允许您在不同的文件中使用相同的类名,而不用担心冲突。这种行为使 CSS 模块成为包含组件级 CSS 的理想方式。

        示例,一个可重用按钮组件,创建 components/Button.module.css 文件,内容如下:

            /*
            You do not need to worry about .error {} colliding with any other `.css` or
            `.module.css` files!
            */
            .error {
                color: white;
                background-color: red;
            }


        创建 components/Button.js 文件,内容如下:

            import styles from './Button.module.css'
 
            export function Button() {
                return (
                    <button
                        type="button"
                        // Note how the "error" class is accessed as a property on the imported
                        // `styles` object.
                        className={styles.error}
                    >
                        Destroy
                    </button>
                )
            }       


    3) JS 中的 CSS

        JS 解决方案中可以使用任何现有的 CSS 。最简单的是内联样式:

            function Test() {
                return <p style={{ color: 'red' }}>Test Page</p>
            }
 
            export default Test


        我们捆绑样式的 jsx,以提供对独立作用域 CSS 的支持。其目的是支持类似于 Web 组件的“shadow CSS”,不幸的是,Web 组件不支持服务器渲染,仅支持JS。

            function HelloWorld() {
                return (
                    <div>
                        Hello world
                        <p>scoped!</p>
                        <style jsx>{`
                            p {
                                color: blue;
                            }
                            div {
                                background: red;
                            }
                            @media (max-width: 600px) {
                                div {
                                    background: blue;
                                }
                            }
                        `} </style>
                        <style global jsx>{`
                            body {
                                background: black;
                            }
                        `}</style>
                    </div>
                )
            }
            
            export default HelloWorld


    4) Sass

        Next.js 内置了对 Sass 的支持,同时使用 .scs 和 .sas 扩展。可以通过 CSS 模块 和 .module.scssor.module.sess 扩展名使用组件级 Sass。

        需要先安装 sass,命令如下:

            $ npm install --save-dev sass     

        Sass 支持两种不同的语法,每种语法都有自己的扩展名。.sscs 扩展要求使用 scss 语法,而 .sass 扩展则要求使用缩进语法(“sass”)。 如果不确定该选择哪一个,可以从 .scs 扩展开始,它是 CSS 的超集,不需要学习缩进语法(“Sass”)。

        (1) 自定义 Sass 选项

            如果要配置 Sass 编译器,请使用 next.config.js 中的 sassOptions。格式如下:

                import path from 'path'
                
                module.exports = {
                    sassOptions: {
                        includePaths: [path.join(__dirname, 'styles')],
                    },
                }

               
        (2) Sass 变量

            Next.js 支持从 CSS 模块文件导出的 Sass 变量。例如,使用导出的 primaryColor Sass 变量。

            src/styles/variables.module.scss 文件,内容如下:

                $primary-color: #64ff00;
                
                :export {
                    primaryColor: $primary-color;
                }            

 

            src/pages/_app.js 文件,内容如下:

                import variables from '../styles/variables.module.scss'
                
                export default function MyApp({ Component, pageProps }) {
                    return (
                        <Layout color={variables.primaryColor}>
                        <Component {...pageProps} />
                        </Layout>
                    )
                }           


    5) Tailwind CSS

        Tailwind CSS 是一个实用程序优先的 CSS 框架,与 Next.js 配合得非常好。

        需要先安装 Tailwind CSS,命令如下:

            $ npm install -D tailwindcss postcss autoprefixer
            $ npx tailwindcss init -p

        (1) 配置 Tailwind

            在 tailwind.config.js 中,为将使用 Tailwind CSS 类名的文件添加路径:

                /** @type {import('tailwindcss').Config} */
                module.exports = {
                    content: [
                        './app/**/*.{js,ts,jsx,tsx,mdx}', // Note the addition of the `app` directory.
                        './pages/**/*.{js,ts,jsx,tsx,mdx}',
                        './components/**/*.{js,ts,jsx,tsx,mdx}',
                    
                        // Or if using `src` directory:
                        './src/**/*.{js,ts,jsx,tsx,mdx}',
                    ],
                    theme: {
                        extend: {},
                    },
                    plugins: [],
                }

            不需要修改 postss.config.js。

        (2) 导入样式

            将 Tailwind CSS 指令添加到应用程序中的全局样式表中,修改 src/styles/globals.css 文件,添加如下内容:

                @tailwind base;
                @tailwind components;
                @tailwind utilities;

            在 src/pages/_app.js中,导入 globals.css 样式表,将样式应用于应用程序中的每个页面和组件。

        (3) 使用类

            在安装了 Tailwind CSS 并添加了全局样式后,可以在应用程序中使用 Tailwind 的实用程序类。示例:

                export default () => {
                    return <h1 className="text-3xl font-bold underline">Hello, Next.js!</h1>
                }

 

3. 部署(Deploying)


    部署的含义是构建一个优化的可用于生产环境的 Web 应用程序版本,把这个版本部署实际的生成环境。这里的生产环境有如下几种:

        (1) 使用 Node.js 或 Docker 自托管 Next.js,并支持所有功能。
        (2) 静态导出 HTML ,部署到第三方 Web 服务器,比如 Nigix、Apache 等,这种方式有些功能不能用。
        (3) 将 Next.js 应用程序部署到 Vercel (https://vercel.com/new/clone)、AWS Copilot 等云服务商。


    1) 构建 (Next build)

        构建生产版本,运行如下命令:

            $ npm run build         
        
        标准输出包括:

            (1) 使用 getStaticProps 或自动静态优化的页面的 HTML 文件
            (2) 全局样式或单独作用域样式的 CSS 文件
            (3) 用于从 Next.js 服务器预呈现动态内容的 JavaScript
            (4) 通过 React 在客户端进行交互的 JavaScript

        输出生成的文件在 .next 文件夹中:

            .next/static/chunks/pages – 此文件夹中的每个 JavaScript 文件都与具有相同名称的路由相关。例如,.next/static/chunks/pages/about.js 将是在应用程序中查看 /about 路由时加载的 JavaScript 文件
            .next/static/media – 从 next/image 静态导入的图像被散列并复制到此处
            .next/static/css – 应用程序中所有页面的全局 css 文件
            .next/server/pages – 从服务器预渲染的 HTML 和 JavaScript 入口点。.nft.json 文件是在启用输出文件跟踪时创建的,包含依赖于给定页面的所有文件路径。
            .next/server/chunks – 在整个应用程序的多个位置使用的共享 JavaScript 块
            .next/cache–next.js - 服务器的构建缓存和缓存图像、响应和页面的输出。使用缓存有助于减少构建时间并提高加载图像的性能

        启动生产版本,运行如下命令:

            $ npm run start


    2) 部署生产版本到 Docker 环境

        (1) Docker 环境

            IP 地址(本地测试环境):192.168.0.10
            操作系统:Linux CentOS 7.9     
            Linux 用户: docker (非 root 用户,有 sudo 权限)

            Docker 版本: 20.10.7
            Docker Compose 版本: 2.6.1

                注:Docker Compose 相关介绍,请参考 “Docker基础知识 (4) - Docker Compose

            NodeJS 目录:/home/docker/nodejs

            将生产版本(运行过 npm run build 命令)的 nextjs-demo 目录整体复制到 /home/docker/nodejs 目录下,并修改 /home/docker/nodejs/nextjs-demo 目录及其子目录的读写权限,格式如下:

                $ chmod -R 777 /home/docker/nodejs/nextjs-demo

        (2) 创建 docker-compose.yml

            在 /home/docker/nodejs/nextjs-demo 目录下,创建 docker-compose.yml 文件,内容如下:

                version: "3.7"

                services:
                    service_nodejs:
                        image: nodejs:16.20
                        restart: always
                        container_name: nodejs_service
                        ports:
                            - 3000:3000
                        networks:
                            - service-network
                        volumes:
                            - /home/docker/nodejs:/home/docker/nodejs:rw
                        working_dir: /home/docker/nodejs/nextjs-demo  
                        entrypoint: ["npm", "start"]


        (3) 运行

            $ cd /home/docker/nodejs/nextjs-demo   # 进入 docker-compose.yml 所在目录

            # 在后台运行
            $ docker-compose up -d   

                [+] Running 2/2
                ⠿ Network nextjs-demo_default  Created                     0.0s
                ⠿ Container node_service       Started                     0.2s

            $ docker ps

                CONTAINER ID   IMAGE        COMMAND         ...  PORTS                         NAMES
                507db5566b60   node:16.20   "npm start"          0.0.0.0:3000->3000/tcp, ...   node_service            

            使用浏览器访问 http://192.168.0.10:3000, 显示 nextjs-demo 项目的 index 页面。