Nuxt3 中文课程 《实战全栈开发简书》.

样式化

学习如何为你的 Nuxt 应用程序添加样式。

在样式化方面,Nuxt 非常灵活。你可以编写自己的样式,或者引用本地和外部样式表。 你可以使用 CSS 预处理器、CSS 框架、UI 库和 Nuxt 模块来为你的应用程序添加样式。

本地样式表

如果你正在编写本地样式表,将它们放在 assets/ 目录 是最自然的位置。

在组件中引入

你可以在页面、布局和组件中直接引入样式表。 你可以使用 JavaScript 的 import,或者使用 css 的 @import 语句

pages/index.vue
<script>
// 使用静态导入以实现服务器端兼容性
import '~/assets/css/first.css'

// 注意:动态导入不兼容服务器端
import('~/assets/css/first.css')
</script>

<style>
@import url("~/assets/css/second.css");
</style>
样式表将被内联到 Nuxt 渲染的 HTML 中。

CSS 属性

你还可以使用 Nuxt 配置中的 css 属性。 将你的样式表放在 assets/ 目录 是最自然的位置。然后你可以引用它的路径,Nuxt 将会将它包含在应用程序的所有页面中。

nuxt.config.ts
export default defineNuxtConfig({
  css: ['~/assets/css/main.css']
})
样式表将被内联到 Nuxt 渲染的 HTML 中,全局注入并存在于所有页面中。

使用字体

将本地字体文件放在 ~/public/ 目录中,例如 ~/public/fonts。然后可以在样式表中使用 url() 引用它们。

assets/css/main.css
@font-face {
  font-family: 'FarAwayGalaxy';
  src: url('/fonts/FarAwayGalaxy.woff') format('woff');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

然后在样式表、页面或组件中按名称引用你的字体:

<style>
h1 {
  font-family: 'FarAwayGalaxy', sans-serif;
}
</style>

通过 NPM 分发样式表

你还可以引用通过 npm 分发的样式表。让我们以流行的 animate.css 库为例。

Terminal
npm install animate.css

然后你可以直接在页面、布局和组件中引用它:

app.vue
<script>
import 'animate.css'
</script>

<style>
@import url("animate.css");
</style>

你还可以将该包作为字符串引用到 Nuxt 配置的 css 属性中。

nuxt.config.ts
export default defineNuxtConfig({
  css: ['animate.css']
})

外部样式表

你可以通过在 nuxt.config 文件的 head 部分添加一个 link 元素来在应用程序中包含外部样式表。你可以使用不同的方法来实现这个目标。注意,本地样式表也可以以这种方式包含。

你可以通过 Nuxt 配置的 app.head 属性来修改 head:

nuxt.config.ts
export default defineNuxtConfig({
  app: {
    head: {
      link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }]
    }
}})

动态添加样式表

你可以使用 useHead 组合式函数在代码中动态设置 head 中的值。

Read more in Docs > API > Composables > Use Head.
useHead({
  link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }]
})

Nuxt 在内部使用 unhead,你可以在这里找到其完整文档here

使用 Nitro 插件修改渲染的 head

如果你需要更高级的控制,你可以使用 hook 拦截渲染的 html,并以编程方式修改 head。

创建一个插件,放在 ~/server/plugins/my-plugin.ts 中,像这样:

server/plugins/my-plugin.ts
export default defineNitroPlugin((nitro) => {
  nitro.hooks.hook('render:html', (html) => {
    html.head.push('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">')
  })
})

外部样式表是渲染阻塞资源:它们必须在浏览器渲染页面之前加载和处理。包含过大样式的网页渲染时间更长。你可以在 web.dev 上阅读更多相关信息。

使用预处理器

要使用 SCSS、Sass、Less 或 Stylus 等预处理器,首先要安装它。

npm install sass

编写样式表的自然位置是 assets 目录。 然后,你可以使用你的预处理器的语法在你的 app.vue(或布局文件)中导入你的源文件。

pages/app.vue
<style lang="scss">
@use "~/assets/scss/main.scss";
</style>

或者,你可以使用 Nuxt 配置的 css 属性。

nuxt.config.ts
export default defineNuxtConfig({
  css: ['~/assets/scss/main.scss']
})
在这两种情况下,Nuxt 渲染的 HTML 中将内联编译后的样式表。

如果你需要在预处理文件中注入代码,比如包含颜色变量的 sass 部分,你可以使用 vite 的 preprocessors 选项 来实现。

assets 目录中创建一些部分文件:

$primary: #49240F;
$secondary: #E4A79D;

然后在 nuxt.config 中:

export default defineNuxtConfig({
  vite: {
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: '@use "@/assets/_colors.scss" as *;'
        }
      }
    }
  }
})

Nuxt 默认使用 Vite。如果你想使用 webpack,参考每个预处理器 loader 的 文档

单文件组件(SFC)样式化

Vue 和 SFC 的最大优点之一就是在处理样式方面非常好用。你可以直接在组件文件的样式块中编写 CSS 或预处理器代码,因此你可以拥有出色的开发体验,而无需使用 CSS-in-JS 等工具。不过,如果你想使用 CSS-in-JS,你可以找到支持它的第三方库和模块,比如 pinceau

关于在 SFC 中为组件添加样式的详细资料,请参考 Vue 文档

类和样式绑定

你可以利用 Vue SFC 的特性,使用 class 和 style 属性为组件添加样式。

<script setup lang="ts">
const isActive = ref(true)
const hasError = ref(false)
const classObject = reactive({
  active: true,
  'text-danger': false
})
</script>

<template>
  <div class="static" :class="{ active: isActive, 'text-danger': hasError }"></div>
  <div :class="classObject"></div>
</template>

更多信息,请参考 Vue 文档

使用 v-bind 动态样式

你可以在样式块中使用 v-bind 函数引用 JavaScript 变量和表达式。 绑定将是动态的,这意味着如果变量值发生变化,样式将会更新。

<script setup lang="ts">
const color = ref("red")
</script>

<template>
  <div class="text">hello</div>
</template>

<style>
.text {
  color: v-bind(color);
}
</style>

作用域样式

scoped 属性允许你将样式应用于组件内部。使用此属性声明的样式将只适用于该组件。

<template>
  <div class="example">hi</div>
</template>

<style scoped>
.example {
  color: red;
}
</style>

CSS 模块

你可以使用 module 属性和 CSS Modules。可以使用注入的 $style 变量访问它。

<template>
  <p :class="$style.red">This should be red</p>
</template>

<style module>
.red {
  color: red;
}
</style>

预处理器支持

SFC 样式块支持预处理器语法。Vite 内置支持 .scss、.sass、.less、.styl 和 .stylus 文件,无需配置。你只需要首先安装它们,然后在 SFC 中使用 lang 属性直接引用它们。

<style lang="scss">
  /* 在这里编写 scss */
</style>

你可以参考 Vite CSS 文档@vitejs/plugin-vue 文档。 对于 webpack 用户,请参考 vue-loader 文档

使用 PostCSS

Nuxt 内置了 postcss。你可以在 nuxt.config 文件中配置它。

nuxt.config.ts
export default defineNuxtConfig({
  postcss: {
    plugins: {
      'postcss-nested': {}
      "postcss-custom-media": {}
    }
  }
})

为了在 SFC 中实现正确的语法高亮,你可以使用 postcss lang 属性。

<style lang="postcss">
  /* 在这里编写 stylus */
</style>

默认情况下,Nuxt 已经预配置了以下插件:

利用布局实现多样式

如果你需要完全不同的样式来样式化应用程序的不同部分,你可以使用布局。 为不同的布局使用不同的样式。

<template>
  <div class="default-layout">
    <h1>默认布局</h1>
    <slot />
  </div>
</template>

<style>
.default-layout {
  color: red;
}
</style>
Read more in Docs > Guide > Directory Structure > Layouts.

第三方库和模块

Nuxt 在样式化方面没有固定的意见,提供了各种各样的选择。你可以使用任何你想要的样式工具,比如流行的库 UnoCSSTailwind CSS

社区和 Nuxt 团队开发了许多 Nuxt 模块,以便更轻松地集成。你可以在网站的 modules 部分 发现它们。 以下是一些帮助你入门的模块:

Nuxt 模块为你提供了良好的开发体验,但请记住,如果你喜欢的工具没有一个模块,这并不意味着你不能在 Nuxt 中使用它!你可以为自己的项目自行配置它。根据工具的不同,你可能需要使用 Nuxt 插件 和/或 制作自己的模块。如果你这样做了,请与社区分享!

轻松加载Web字体

你可以使用Nuxt Google Fonts模块来加载Google字体。

如果你正在使用UnoCSS,请注意它附带了一个web字体预设,方便地从常见提供商(包括Google字体等)加载字体。

进阶

过渡效果

Nuxt拥有与Vue相同的<Transition>元素,并且还支持实验性的View Transitions API

Read more in Docs > Getting Started > Transitions.

字体高级优化

我们建议使用Fontaine来减少你的CLS。如果你需要更高级的功能,可以考虑创建一个Nuxt模块来扩展构建过程或Nuxt运行时。

始终记得利用Web生态系统中可用的各种工具和技术,使你的应用程序的样式更加简单高效。无论你是使用原生CSS、预处理器、PostCSS、UI库还是模块,Nuxt都能满足你的需求。祝你样式编写愉快!

LCP高级优化

你可以采取以下措施来加快全局CSS文件的下载速度:

  • 使用CDN,让文件物理上更接近你的用户
  • 压缩你的资源,最好使用Brotli
  • 使用HTTP2/HTTP3进行传递
  • 将你的资源托管在同一个域名下(不要使用不同的子域名)

如果你正在使用Cloudflare、Netlify或Vercel等现代平台,大多数情况下这些事情应该会自动完成。 你可以在web.dev上找到一个LCP优化指南。

如果你的所有CSS都由Nuxt内联,你可以(实验性地)完全停止在渲染的HTML中引用外部CSS文件。 你可以通过一个钩子来实现,你可以将它放在一个模块中或者你的Nuxt配置文件中。

nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    'build:manifest': (manifest) => {
      // 找到应用程序入口的CSS列表
      const css = manifest['node_modules/nuxt/dist/app/entry.js']?.css
      if (css) {
        // 从数组的末尾开始,向前遍历
        for (let i = css.length - 1; i >= 0; i--) {
          // 如果以'entry'开头,从列表中删除它
          if (css[i].startsWith('entry')) css.splice(i, 1)
        }
      }
    },
  },
})