【2024年4月】nuxt3 项目模板,让你开发官网得心应手

发布于:2024-04-28 ⋅ 阅读:(38) ⋅ 点赞:(0)

本文带领大家开发一套通用的官网开发模板,名字叫 nuxt3-best,名字不重要,不是best也不重要,好用才最重要。

前言

pnpm dlx nuxi@latest init nuxt3-best

接着选择包管理器,有 npm pnpm yarn bun,我们选择 pnpm

接着会问我们是否初始化 git 仓库,选择 yes 就行了。

刚创建出来的项目目录结构非常简单,可以说只有一个 App.vue,其他都是配置文件,如下图。

image.png

默认界面如下:

image.png

接下来开始开整项目,大概有以下几个步骤:

  • 一、基本配置 prettiersrc目录.npmrc
  • 二、引入 nuxt-ui (可选)
  • 三、引入 tailwindcss/unocss
  • 四、整理布局
  • 五、响应式布局
  • 六、表单验证
  • 七、路由和导航
  • 八、数据获取
  • 九、引入其他常用 moduls
  • 十、打包部署

鉴于 unibest 太强的代码格式配置,招到很多程序员的不适应,这个 nuxt3-best 项目就温和一点了。另外,官网作为短时间就完成的项目(且大概率是一个人完成)来说,我们不做过多限制,只配个 prettier 就行了。

一、基本配置 prettiersrc目录

    1. .prettierrc.cjs 文件编写如下代码
// @see https://prettier.io/docs/en/options
module.exports = {
  singleQuote: true,
  tabWidth: 2,
  printWidth: 100,
  useTabs: false,
  semi: false,
  trailingComma: 'all',
  endOfLine: 'auto',
  htmlWhitespaceSensitivity: 'ignore',
  overrides: [
    {
      files: '*.json',
      options: {
        trailingComma: 'none',
      },
    },
  ],
}
    1. src目录

因为 Nuxt3 项目默认的文件夹都在顶层,就像 HBuilderX 创建的 Uniapp 项目那种的,全部在顶层,感觉很乱,所以推荐把所有的源代码都放到 src 里面,并增加 1个 配置即可。

image.png

上图是全部在顶层的图,把这些统统放到 src 里面,包括 App.vue 也要放进去。接着,在 nuxt.config.ts 中加上 srcDir 的配置:

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  devtools: { enabled: true },
+ // https://nuxt.com/docs/api/nuxt-config#srcdir
+ srcDir: 'src/',
})

image.png

  • 3. .npmrc 文件编写如下代码

这样就可以使用 淘宝源 和使用 pnpm 安装依赖了。

shamefully-hoist=true
strict-peer-dependencies=false
registry=https://registry.npmmirror.com/

引入 nuxt-ui ( 可选 )

大部分官网开发,不需要引入第三方 UI 库,页面内容基本都是自己手写,包括组件。但是耐不住时不时有下拉框、表单和表单验证等功能出现,这个时候自己手写就太花时间了,还是引入第三方 UI库 吧。

我司官网有一个页面,需要用户留下信息,需要表单,于是我就引入了 nuxt-ui,主要是用它的表单。为啥不选其他 UI 库,2个原因:1)够用就行,2)nuxt 官方维护的。

怎么引入呢?

在 有很多官方维护的 modules,可以非常方便地集成,如下图:

image.png

选好 ui 那个卡片,点击进去,进入 nuxt-ui 文档,如下图只需要 2步 就引入了:

image.png

经过我的实际操作,这里使用 npx 安装会有问题,所以不能执行 npx nuxi@latest module add ui,要改为 pnpm add @nuxt/ui

# npx nuxi@latest module add ui 不可以,因为原来是 pnpm 安装的,与npx 不兼容。
pnpm add @nuxt/ui

接着把 @nuxt/ui 加到 modules,如下:


// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  devtools: { enabled: true },
  // https://nuxt.com/docs/api/nuxt-config#srcdir
  serverDir: 'src/',
+ modules: ['@nuxt/ui'],
})

注意,nuxt-ui 内部已经引入了 tailwindcss,所以无需另外引入 原子化CSS 了,你看下面的效果:

image.png

三、引入 tailwindcss/unocss

如果引入了 nuxt-ui 则本节可以跳过,nuxt-ui 自带 tailwindcss

如果需要引入 原子化CSS,则我推荐 UnoCSSmodules 找到 UnoCSS,如下图:

image.png

引入方式如下:

image.png

对应代码如下:

  • pnpm add -D @unocss/nuxt
  • nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@unocss/nuxt',
  ],
})
  • uno.config.ts
// uno.config.ts
import { defineConfig } from 'unocss'

export default defineConfig({
  // ...UnoCSS options
})

四、整理布局

开发 layoutspages ,并对 layouts/default.vue 进行布局,要的效果就是:

  • 顶部栏固定在顶部
  • 中间是内容区域
  • footbar 一直在”底部“,当内容不足一屏的时候,固定在底部,超过一屏的时候,跟随内容在最底部。

layouts/default.vue 代码如下:

<template>
  <div class="flex flex-col h-screen">
    <div class="leading-10 bg-slate-500 fixed w-full z-20 h-10">topbar</div>
    <div class="flex-1 pt-10 bg-gray-100">
      <slot />
    </div>
    <div class="h-10 leading-10 bg-slate-500">footer</div>
  </div>
</template>

五、响应式布局

    1. 内置的 .container

不管是古老的 BootStrap 还是现代的 tailwindcss/unocss,都对 .container 进行了响应式处理,官网开发经常用得到。

举个例子,官网经常在最上面来个左右通屏的图片,下面内容又是左右居中的内容区域,我通常会这样处理:

<template>
  <div>
    <img class="w-full" src="~/assets/images/pretty-girl.png" />
    <div class="container m-auto bg-yellow-100 p-4">
      <fg-content :line="40" />
    </div>
  </div>
</template>

效果如下:

container.gif

    1. 自己写响应式,如 grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 gap-4,这个比较直观,就不演示了。

六、表单验证

talk is cheap, show me the code

下面用到了 zod,记得 pnpm add zod 一下。

<script setup lang="ts">
import { z } from 'zod'
import type { FormSubmitEvent } from '#ui/types'

const options = [
  { label: 'Option 1', value: 'option-1' },
  { label: 'Option 2', value: 'option-2' },
  { label: 'Option 3', value: 'option-3' },
]

const state = reactive({
  input: undefined,
  inputMenu: undefined,
  textarea: undefined,
  select: undefined,
  selectMenu: undefined,
  checkbox: undefined,
  toggle: undefined,
  radio: undefined,
  radioGroup: undefined,
  switch: undefined,
  range: undefined,
})

const schema = z.object({
  input: z.string().min(10),
  inputMenu: z.any().refine((option) => option?.value === 'option-2', {
    message: 'Select Option 2',
  }),
  textarea: z.string().min(10),
  select: z.string().refine((value) => value === 'option-2', {
    message: 'Select Option 2',
  }),
  selectMenu: z.any().refine((option) => option?.value === 'option-2', {
    message: 'Select Option 2',
  }),
  toggle: z.boolean().refine((value) => value === true, {
    message: 'Toggle me',
  }),
  checkbox: z.boolean().refine((value) => value === true, {
    message: 'Check me',
  }),
  radio: z.string().refine((value) => value === 'option-2', {
    message: 'Select Option 2',
  }),
  radioGroup: z.string().refine((value) => value === 'option-2', {
    message: 'Select Option 2',
  }),
  range: z.number().max(20, { message: 'Must be less than 20' }),
})

type Schema = z.infer<typeof schema>

const form = ref()

async function onSubmit(event: FormSubmitEvent<Schema>) {
  // Do something with event.data
  console.log(event.data)
}
</script>

<template>
  <UForm
    ref="form"
    :schema="schema"
    :state="state"
    class="space-y-4"
    @submit="onSubmit"
  >
    <UFormGroup name="input" label="Input">
      <UInput v-model="state.input" />
    </UFormGroup>

    <UFormGroup name="inputMenu" label="Input Menu">
      <UInputMenu v-model="state.inputMenu" :options="options" />
    </UFormGroup>

    <UFormGroup name="textarea" label="Textarea">
      <UTextarea v-model="state.textarea" />
    </UFormGroup>

    <UFormGroup name="select" label="Select">
      <USelect
        v-model="state.select"
        placeholder="Select..."
        :options="options"
      />
    </UFormGroup>

    <UFormGroup name="selectMenu" label="Select Menu">
      <USelectMenu
        v-model="state.selectMenu"
        placeholder="Select..."
        :options="options"
      />
    </UFormGroup>

    <UFormGroup name="toggle" label="Toggle">
      <UToggle v-model="state.toggle" />
    </UFormGroup>

    <UFormGroup name="checkbox" label="Checkbox">
      <UCheckbox v-model="state.checkbox" label="Check me" />
    </UFormGroup>

    <UFormGroup name="radioGroup" label="Radio Group">
      <URadioGroup v-model="state.radioGroup" :options="options" />
    </UFormGroup>

    <UFormGroup name="radio" label="Radio">
      <URadio
        v-for="option in options"
        :key="option.value"
        v-model="state.radio"
        v-bind="option"
      >
        {{ option.label }}
      </URadio>
    </UFormGroup>

    <UFormGroup name="range" label="Range">
      <URange v-model="state.range" />
    </UFormGroup>

    <UButton type="submit">Submit</UButton>

    <UButton variant="outline" class="ml-2" @click="form.clear()">
      Clear
    </UButton>
  </UForm>
</template>

效果如下:(提交不满足规范,会报红)

image.png

如果一行要放2个表单怎么办?

+<div class="grid grid-cols-2 gap-4">
    <UFormGroup name="input" label="Input">
        <UInput v-model="state.input" />
    </UFormGroup>

    <UFormGroup name="inputMenu" label="Input Menu">
        <UInputMenu v-model="state.inputMenu" :options="options" />
    </UFormGroup>
+</div>

效果如下:

image.png

七、路由和导航

首先申明,

  1. 常用的API都是自动导入的,无需引入。

  2. 约定的几个文件夹里面的东西都是可以直接使用的,无需引入。

可以直接通过 const route = useRoute() 拿到 route 页面路由信息,通过它可以获取路由 queryparams

  • 1) http://localhost:3000?name=fg&age=30

const { name, age } = route.query 可以拿到 nameage 信息。

  • 2) http://localhost:3000/product/5

某个路由导航到这里,那么就可以命中 src/pages/product/[pId].vue 页面,页面里面可以通过如下代码获取到 pId

const { pId } = route.params 可以拿到 pId 信息。

    1. 路由导航,有2种方式,如下

    • <NuxtLink to="/product/5" > 进入详情 </NuxtLink
    • 使用编程方式导航,const router = useRouter() 拿到路由示例,然后 router.push("/product/5")

八、数据获取

这个直接看官网就行了,。

主要有三种方式:useFetchuseAsyncData and $fetch

九、引入其他常用 moduls

大家可以在 找自己想要的

image.png

很多人官网不需要用户登录,所以 pinia 不是通用的,vueuse 倒是比较通用,还有 image 也不错,所以我就安装了这2个

pnpm add @vueuse/nuxt @nuxt/image

然后加到 nuxt.config.tsmodules 配置里,到此已经有 3个 了: modules: ['@nuxt/ui', '@vueuse/nuxt', '@nuxt/image'],.

十、打包部署

执行 pnpm build 即可完成打包,然后在服务器上运行如下命令 node .output/server/index.mjs 即可,如下图:

image.png

为了保证程序运行更加稳健,我们通常会使用 PM2,。

需要配置 ecosystem.confit.cjs,内容如下:

module.exports = {
  apps: [
    {
      name: 'NuxtAppName',
      port: '3000',
      exec_mode: 'cluster',
      instances: 'max',
      script: './.output/server/index.mjs'
    }
  ]
}

在服务器上,安装 PM2npm i -g pm2

然后运行 PM2pm2 start ecosystem.config.cjs

image.png

其他常用命令如:pm2 stop|delete <pm2-app-name>

image.png

image.png


至此,完结~

总结

总结一下本文,主要描述了如何生成一个 nuxt3 项目模板,有以下 十个 步骤:

  • 一、基本配置 prettiersrc目录.npmrc
  • 二、引入 nuxt-ui (可选)
  • 三、引入 tailwindcss/unocss
  • 四、整理布局
  • 五、响应式布局
  • 六、表单验证
  • 七、路由和导航
  • 八、数据获取
  • 九、引入其他常用 moduls
  • 十、打包部署

模板地址:

全文完~


网站公告

今日签到

点亮在社区的每一天
去签到