从头开始搭建一个Nuxt3模板项目

3,307 阅读10分钟

Hi~,我是一碗周,如果写的文章有幸可以得到你的青睐,万分有幸~

🍍 写在前面

Nuxt3在今年的4月分发布RC版本,到现在也已经大半年了,估计用不了多久正式版也就该发布了,也该是时候体验一下Nuxt3了。

这个项目模板已经包含了以下的内容

  • 🎨 使用Tailwind作为CSS组件库,并安装daisyUI插件,关于daisyUI可以参考:daisyUI快速上手
  • 💪 ESlint和Stylelint代码检验;
  • 🐶 创建Git commit message规范校验;
  • 🎉 天然支持Vue3、Typescript、Vite等;
  • 🍍 集成Pinia作为状态管理;
  • 🥤 集成vueuse作为Hooks库;
  • 🎊文件式路由、componentsAPI自动导入、组件自动导入等;

这里我使用pnpm作为包管理工具,其版本如下:

  • Node:v16.15.0
  • pnpm:7.14.2

🍓 安装Nuxt3

由于国内的网络环境访问Github经常挂,这里准备了三种安装方式,准确的说还是一种,只不过由自动变成了手动。

🍕 脚手架方式安装

通过脚手架方式安装也是官网中提供的,命令比较简单,如下所示:

pnpm dlx nuxi init nuxt3-template

这种方式经常会失败,失败后可以多次尝试,如果还不行可以选择下一种;

🧁 直接下载压缩包

第二种方式比较简单粗暴,直接下载脚手架中使用的模板,下载地址【codeload.github.com/nuxt/starte…】,然后解压就可以了,与第一种方式得到的最终效果是一致的。

🍬 复制粘贴

如果第二种也不能下载的话,可以尝试第三种,就是复制粘贴模板中的代码,这个更无脑,直接依次在根目录下创建下面这些文件并粘贴即可:

package.json

{
  "private": true,
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  },
  "devDependencies": {
    "nuxt": "3.0.0-rc.13"
  }
}

.gitignore

node_modules
*.log*
.nuxt
.nitro
.cache
.output
.env
dist

app.vue

<template>
  <div>
    <NuxtWelcome />
  </div>
</template>

nuxt.config.ts

// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({

})

tsconfig.json

{
  // https://v3.nuxtjs.org/concepts/typescript
  "extends": "./.nuxt/tsconfig.json"
}

🍗 安装依赖

选择自己的包管理工具安装依赖

# yarn
yarn install

# npm
npm install

# pnpm
pnpm install --shamefully-hoist

然后执行pnpm dev即可看到下面这个页面:

image.png

到这我们的第一步就已经完成了。

🍣 规范规范规范

在一个项目中代码检查、统一代码风格肯定是不能少的,我们依次来看:

🥯 ESlint

ESlint是什么我就不多说了,这里我代码格式化也是使用的ESlint,用的是Anthony Fu大佬的ESlint配置,为什么不用Prettier,看看大佬怎么说:

Why I don't use Prettier (antfu.me)

大佬的配置使用给了非常详细的步骤,这里我就在搬一下:

安装依赖:

pnpm i -D eslint @antfu/eslint-config typescript

创建.eslintrc配置文件,并写入下面这段内容:

{
  "extends": "@antfu"
}

创建.vscode/settings.json配置文件,并写入下面这段内容

{
  "prettier.enable": false,
  "editor.formatOnSave": false,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  }
}

简单几步就配置完成了,比自己安装各种包要省心的多,也不需要过多的考虑,而且可以轻松的覆盖各种规则。

🌴 Stylelint

这里配置一下Stylelint,用于检测CSS是否规范,虽然这个项目中会安装TailwindCSS但是也不可能一行CSS不写,所以还是需要简单配置一下的。

首先我们安装一下依赖:

pnpm i -D stylelint stylelint-config-standard postcss-html stylelint-config-html stylelint-config-recess-order 
  • stylelint:核心库
  • stylelint-config-standard:CSS规范
  • postcss-htmlstylelint-config-html中依赖这个包
  • stylelint-config-html:可以格式化HTML、Vue中的style标签中的CSS
  • stylelint-config-recess-order:调整属性的顺序

安装完依赖后我们需要创建一个style.config.js文件并写入下面这些内容:

module.exports = {
  /* 继承某些已有的规则 */
  extends: [
    'stylelint-config-standard', // 配置stylelint拓展插件
    'stylelint-config-html/vue', // 配置 vue 中 template 样式格式化
    'stylelint-config-recess-order', // 配置stylelint css属性书写顺序插件,
  ],
  overrides: [
    // 扫描 .vue/html 文件中的<style>标签内的样式
    {
      files: ['**/*.{vue,html}'],
      customSyntax: 'postcss-html',
    },
  ],
  rules: {
    'string-quotes': 'double', // 指定字符串使用单引号或双引号
    'color-hex-case': 'lower', // 16 进制颜色全小写
    'color-hex-length': 'long', // 禁止16禁止颜色小写
    'rule-empty-line-before': 'always', // 在规则之前的空行必须始终有一个空行
    'block-opening-brace-space-before': 'always', // 在块的开大括号之前必须有一个空格
    'selector-pseudo-class-no-unknown': [
      true,
      {
        ignorePseudoClasses: ['global', 'v-deep', 'deep'],
      },
    ],
  },
}

然后在.vscode/settings.json配置文件加入下面几行内容

{
  "prettier.enable": false,
  "editor.formatOnSave": false,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
    "source.fixAll.stylelint": true
  },
+  "stylelint.enable": true,
+  "stylelint.validate": [
+    "css",
+    "vue",
+    "html"
+  ]
}

到这Stylelint也配置完成了。

🥒 Git提交验证

接下来是Git commit message规范以及在提交前对代码进行检查的配置,首先安装依赖:

pnpm i -D husky

创建.husky文件夹

pnpm husky install  # 前提是已经初始化Git仓库

安装lint-staged依赖,用于检测本地暂存代码:

pnpm i -D lint-staged

package.json中加入下面这段内容:

{
  "scripts": {
    "lint-staged": "lint-staged",
  },
  "lint-staged": {
    "*.{js,ts,tsx,jsx}": [
      "eslint --fix --ext"
    ],
    "*.vue": [
      "eslint --fix --ext",
      "stylelint --cache --fix"
    ],
    "*.{css,html}": [
      "stylelint --cache --fix"
    ]
  }
}

创建git commit钩子,让其执行lint-staged脚本,执行命令如下:

pnpm husky add .husky/pre-commit "pnpm lint-staged"

最后配置校验commit信息,首先安装依赖:

pnpm i -D @commitlint/cli @commitlint/config-conventional commitizen cz-git
# @commitlint/cli @commitlint/config-conventional: commit规范以及校验工具
# commitizen: 用于生成message的包
# cz-git: commitizen 适配器

首先指定适配器,修改package.json添加config

{
  "scripts": {
  },
  "config": {
    "commitizen": {
      "path": "node_modules/cz-git"
    }
  }
}

然后添加commit msg钩子,并执行指定命令:

pnpm husky add .husky/commit-msg 'pnpm commitlint --edit'

最后添加我们提交的自定义配置,在根目录创建commitlint.config.js,并写入下面这段内容:

// @see: https://cz-git.qbenben.com/zh/guide
/** @type {import('cz-git').UserConfig} */

module.exports = {
  ignores: [commit => commit.includes('init')],
  extends: ['@commitlint/config-conventional'],
  rules: {
    // @see: https://commitlint.js.org/#/reference-rules
    'body-leading-blank': [2, 'always'],
    'footer-leading-blank': [1, 'always'],
    'header-max-length': [2, 'always', 108],
    'subject-empty': [2, 'never'],
    'type-empty': [2, 'never'],
    'subject-case': [0],
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'docs',
        'style',
        'refactor',
        'perf',
        'test',
        'build',
        'ci',
        'chore',
        'revert',
        'wip',
        'workflow',
        'types',
        'release',
      ],
    ],
  },
  prompt: {
    messages: {
      // type: 'Select the type of change that you're committing:',
      // scope: 'Denote the SCOPE of this change (optional):',
      // customScope: 'Denote the SCOPE of this change:',
      // subject: 'Write a SHORT, IMPERATIVE tense description of the change:\n',
      // body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n',
      // breaking: 'List any BREAKING CHANGES (optional). Use "|" to break new line:\n',
      // footerPrefixsSelect: 'Select the ISSUES type of changeList by this change (optional):',
      // customFooterPrefixs: 'Input ISSUES prefix:',
      // footer: 'List any ISSUES by this change. E.g.: #31, #34:\n',
      // confirmCommit: 'Are you sure you want to proceed with the commit above?',
      // 中文版
      type: '选择你要提交的类型 :',
      scope: '选择一个提交范围(可选):',
      customScope: '请输入自定义的提交范围 :',
      subject: '填写简短精炼的变更描述 :\n',
      body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
      breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
      footerPrefixsSelect: '选择关联issue前缀(可选):',
      customFooterPrefixs: '输入自定义issue前缀 :',
      footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
      confirmCommit: '是否提交或修改commit ?',
    },
    types: [
      // 英文
      // { value: 'feat', name: 'feat:     🚀  A new feature', emoji: '🚀' },
      // { value: 'fix', name: 'fix:      🧩  A bug fix', emoji: '🧩' },
      // { value: 'docs', name: 'docs:     📚  Documentation only changes', emoji: '📚' },
      // { value: 'style', name: 'style:    🎨  Changes that do not affect the meaning of the code', emoji: '🎨' },
      // { value: 'refactor', name: 'refactor: ♻️   A code change that neither fixes a bug nor adds a feature', emoji: '♻️' },
      // { value: 'perf', name: 'perf:     ⚡️  A code change that improves performance', emoji: '⚡️' },
      // { value: 'test', name: 'test:     ✅  Adding missing tests or correcting existing tests', emoji: '✅' },
      // { value: 'build', name: 'build:    📦️   Changes that affect the build system or external dependencies', emoji: '📦️' },
      // { value: 'ci', name: 'ci:       🎡  Changes to our CI configuration files and scripts', emoji: '🎡' },
      // { value: 'chore', name: 'chore:    🔨  Other changes that don't modify src or test files', emoji: '🔨' },
      // { value: 'revert', name: 'revert:   ⏪️  Reverts a previous commit', emoji: '⏪️' },
      // 中文版
      // { value: '特性', name: '特性:   🚀  新增功能', emoji: '🚀' },
      // { value: '修复', name: '修复:   🧩  修复缺陷', emoji: '🧩' },
      // { value: '文档', name: '文档:   📚  文档变更', emoji: '📚' },
      // { value: '格式', name: '格式:   🎨  代码格式(不影响功能,例如空格、分号等格式修正)', emoji: '🎨' },
      // { value: '重构', name: '重构:   ♻️  代码重构(不包括 bug 修复、功能新增)', emoji: '♻️' },
      // { value: '性能', name: '性能:   ⚡️  性能优化', emoji: '⚡️' },
      // { value: '测试', name: '测试:   ✅  添加疏漏测试或已有测试改动', emoji: '✅' },
      // { value: '构建', name: '构建:   📦️  构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)', emoji: '📦️' },
      // { value: '集成', name: '集成:   🎡  修改 CI 配置、脚本', emoji: '🎡' },
      // { value: '回退', name: '回退:   ⏪️  回滚 commit', emoji: '⏪️' },
      // { value: '其他', name: '其他:   🔨  对构建过程或辅助工具和库的更改(不影响源文件、测试用例)', emoji: '🔨' },
      // 中英混合版
      { value: 'feat', name: 'feat:       🚀  新增功能', emoji: '🚀' },
      { value: 'fix', name: 'fix:        🧩  修复缺陷', emoji: '🧩' },
      { value: 'docs', name: 'docs:       📚  文档变更', emoji: '📚' },
      { value: 'style', name: 'style:      🎨  代码格式(不影响功能,例如空格、分号等格式修正)', emoji: '🎨' },
      { value: 'refactor', name: 'refactor:   ♻️  代码重构(不包括 bug 修复、功能新增)', emoji: '♻️' },
      { value: 'perf', name: 'perf:      ⚡️  性能优化', emoji: '⚡️' },
      { value: 'test', name: 'test:       ✅  添加疏漏测试或已有测试改动', emoji: '✅' },
      { value: 'build', name: 'build:      📦️  构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)', emoji: '📦️' },
      { value: 'ci', name: 'ci:         🎡  修改 CI 配置、脚本', emoji: '🎡' },
      { value: 'revert', name: 'revert:     ⏪️  回滚 commit', emoji: '⏪️' },
      { value: 'chore', name: 'chore:      🔨  对构建过程或辅助工具和库的更改(不影响源文件、测试用例)', emoji: '🔨' },
    ],
    useEmoji: true,
    themeColorCode: '',
    scopes: [],
    allowCustomScopes: true,
    allowEmptyScopes: true,
    customScopesAlign: 'bottom',
    customScopesAlias: 'custom',
    emptyScopesAlias: 'empty',
    upperCaseSubject: false,
    allowBreakingChanges: ['feat', 'fix'],
    breaklineNumber: 100,
    breaklineChar: '|',
    skipQuestions: [],
    issuePrefixs: [{ value: 'closed', name: 'closed:   ISSUES has been processed' }],
    customIssuePrefixsAlign: 'top',
    emptyIssuePrefixsAlias: 'skip',
    customIssuePrefixsAlias: 'custom',
    allowCustomIssuePrefixs: true,
    allowEmptyIssuePrefixs: true,
    confirmColorize: true,
    maxHeaderLength: Infinity,
    maxSubjectLength: Infinity,
    minSubjectLength: 0,
    scopeOverrides: undefined,
    defaultBody: '',
    defaultIssues: '',
    defaultScope: '',
    defaultSubject: '',
  },
}

到这为止关于代码规范部分就已经全部完成了。

👹 依赖的安装与配置

🎨 TailwindCSS

我们先在项目中安装一下TailwindCSS,这里使用的是Nuxt版本,安装命令如下:

pnpm i -D @nuxtjs/tailwindcss

然后在nuxt.config.ts中加入一行内容:

export default defineNuxtConfig({
+  modules: ['@nuxtjs/tailwindcss'],
})

其实到这就已经可以在项目中使用了,@nuxtjs/tailwindcss中为很多配置都增加了默认值,也就省去我们自己去配置了。

当然,自己配置也可以,在nuxt.config.ts中添加一个tailwindcss配置项,然后可以自己配置,具体参考Options | Nuxt Tailwind (nuxtjs.org)

🍵 daisyUI

这里我们使用daisyUI作为我们的组件库,安装比较简单,命令如下:

如果你对daisyUI不了解,可以看我另一篇文章daisyUI快速上手,解决TailwindCSS疯狂堆砌class的问题

pnpm i daisyui

然后,在根目录创建tailwind.config.js并写入下面这段内容:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [],
  theme: {
    extend: {},
  },
  plugins: [require('daisyui')], // 以插件的方式引入
}

都设置完成后我们随便写点东西来看一下效果:

app.vue

<template>
  <div class="min-h-screen hero" :style="{ backgroundImage: 'url(https://placeimg.com/1000/800/arch)' }">
    <div class="bg-opacity-60 hero-overlay" />
    <div class="text-center hero-content text-neutral-content">
      <div class="max-w-md">
        <h1 class="mb-5 text-5xl font-bold">
          Hello there
        </h1>
        <p class="mb-5">
          Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi exercitationem quasi. In deleniti eaque aut repudiandae et a id nisi.
        </p>
        <button class="btn btn-primary">
          Get Started
        </button>
      </div>
    </div>
  </div>
</template>

现在运行我们的项目

pnpm dev

打开浏览器即可看到下面这个页面:

🥨 与ESlint配合

使用TailwindCSS存在一个问题,就是类名的顺序不固定,导致后期维护起来有些许困难,我们可以安装一个ESlint的插件eslint-plugin-tailwindcs来解决这个问题,这个插件的主要作用是调整类名的顺序,使用步骤如下:

首先安装依赖:

pnpm i -D eslint-plugin-tailwindcss

然后在.eslintrc中加入下面这几行:

{
  "plugins": [
    "tailwindcss"
  ],
  "extends": [
    "plugin:tailwindcss/recommended",
    "@antfu"
  ]
}

然后在保存时就可以自动格式化代码了。

这里需要注意一下,我的tailwindcss的版本是3.2,在写这篇文章的时候使用eslint-plugin-tailwindcss@3.6.2无法格式化,我安装的是tailwindcss@3.5.2,可以正常格式化。

🍍 Pinia

现在我们安装Pinia,命令如下:

pnpm install @pinia/nuxt

然后在nuxt.config.ts中增加配置配置文件:

export default defineNuxtConfig({
  modules: [
    '@nuxtjs/tailwindcss',
+   '@pinia/nuxt',
  ],
})

到这就配置完了,就可以在项目中直接使用了。

🐰 vueuse

vueuse作为Vue的Hooks库,库中封装了很多好用的函数,这里就不展开了介绍了。

Nuxt3中安装比较简单,仅需两步:

首先安装依赖

npm i -D @vueuse/nuxt @vueuse/core

然后在nuxt.config.ts中添加配置:

// nuxt.config.ts
export default {
  modules: [
    '@vueuse/nuxt',
  ],
}
export default defineNuxtConfig({
  modules: [
    '@nuxtjs/tailwindcss',
    '@pinia/nuxt',
+   '@vueuse/nuxt',
  ],
})

现在就配置完成了,如果想要测试是否安装成功可以在app.vue中写入下面这段代码:

<script setup>
useHead({
  title: 'nuxt3-template',
  link: [
    {
      rel: 'icon', type: 'image/png', href: '/nuxt.png',
    },
  ],
})
</script>

然后就可以看到我们的网站的标题以及icon都发生了变化。

🍇 写在最后

本篇文章到这就结束了,在这篇文章中介绍了Nuxt3项目的搭建过程,Github地址:ywanzhou/nuxt3-template: Nuxt3的一个开箱即用的模板 (github.com),喜欢可以点一个star支持一下。