本文带领大家开发一套通用的官网开发模板,名字叫 nuxt3-best,名字不重要,不是best也不重要,好用才最重要。
前言
pnpm dlx nuxi@latest init nuxt3-best
接着选择包管理器,有 npm pnpm yarn bun,我们选择 pnpm。
接着会问我们是否初始化 git 仓库,选择 yes 就行了。
刚创建出来的项目目录结构非常简单,可以说只有一个 App.vue,其他都是配置文件,如下图。
默认界面如下:
接下来开始开整项目,大概有以下几个步骤:
- 一、基本配置
prettier、src目录、.npmrc等 - 二、引入
nuxt-ui(可选) - 三、引入
tailwindcss/unocss - 四、整理布局
- 五、响应式布局
- 六、表单验证
- 七、路由和导航
- 八、数据获取
- 九、引入其他常用
moduls - 十、打包部署
鉴于
unibest太强的代码格式配置,招到很多程序员的不适应,这个nuxt3-best项目就温和一点了。另外,官网作为短时间就完成的项目(且大概率是一个人完成)来说,我们不做过多限制,只配个prettier就行了。
一、基本配置 prettier、src目录等
-
.prettierrc.cjs文件编写如下代码
js复制代码// @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'', }, }, ], }
-
src目录
因为 Nuxt3 项目默认的文件夹都在顶层,就像 HBuilderX 创建的 Uniapp 项目那种的,全部在顶层,感觉很乱,所以推荐把所有的源代码都放到 src 里面,并增加 1个 配置即可。
上图是全部在顶层的图,把这些统统放到 src 里面,包括 App.vue 也要放进去。接着,在 nuxt.config.ts 中加上 srcDir 的配置:
diff复制代码// https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ devtools: { enabled: true }, + // https://nuxt.com/docs/api/nuxt-config#srcdir + srcDir: ''src/'', })
- 3.
.npmrc文件编写如下代码
这样就可以使用 淘宝源 和使用 pnpm 安装依赖了。
text复制代码shamefully-hoist=true strict-peer-dependencies=false registry=https://registry.npmmirror.com/
引入 nuxt-ui ( 可选 )
大部分官网开发,不需要引入第三方 UI 库,页面内容基本都是自己手写,包括组件。但是耐不住时不时有下拉框、表单和表单验证等功能出现,这个时候自己手写就太花时间了,还是引入第三方 UI库 吧。
我司官网有一个页面,需要用户留下信息,需要表单,于是我就引入了 nuxt-ui,主要是用它的表单。为啥不选其他 UI 库,2个原因:1)够用就行,2)nuxt 官方维护的。
怎么引入呢?
在 nuxt.com/modules 有很多官方维护的 modules,可以非常方便地集成,如下图:
选好 ui 那个卡片,点击进去,进入 nuxt-ui 文档,如下图只需要 2步 就引入了:
经过我的实际操作,这里使用 npx 安装会有问题,所以不能执行 npx nuxi@latest module add ui,要改为 pnpm add @nuxt/ui。
sh复制代码# npx nuxi@latest module add ui 不可以,因为原来是 pnpm 安装的,与npx 不兼容。 pnpm add @nuxt/ui
接着把 @nuxt/ui 加到 modules,如下:
diff复制代码// 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 了,你看下面的效果:
三、引入 tailwindcss/unocss
如果引入了
nuxt-ui则本节可以跳过,nuxt-ui自带tailwindcss。
如果需要引入 原子化CSS,则我推荐 UnoCSS,modules 找到 UnoCSS,如下图:
引入方式如下:
对应代码如下:
pnpm add -D @unocss/nuxtnuxt.config.ts
ts复制代码// nuxt.config.ts export default defineNuxtConfig({ modules: [ ''@unocss/nuxt'', ], })
uno.config.ts
ts复制代码// uno.config.ts import { defineConfig } from ''unocss'' export default defineConfig({ // ...UnoCSS options })
四、整理布局
开发 layouts 和 pages ,并对 layouts/default.vue 进行布局,要的效果就是:
- 顶部栏固定在顶部
- 中间是内容区域
- footbar 一直在”底部“,当内容不足一屏的时候,固定在底部,超过一屏的时候,跟随内容在最底部。
layouts/default.vue 代码如下:
html复制代码<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>
五、响应式布局
-
- 内置的
.container
- 内置的
不管是古老的 BootStrap 还是现代的 tailwindcss/unocss,都对 .container 进行了响应式处理,官网开发经常用得到。
举个例子,官网经常在最上面来个左右通屏的图片,下面内容又是左右居中的内容区域,我通常会这样处理:
html复制代码<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>
效果如下:
-
- 自己写响应式,如
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 一下。
vue复制代码<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" @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" @click="form.clear()"> Clear </UButton> </UForm> </template>
效果如下:(提交不满足规范,会报红)
如果一行要放2个表单怎么办?
diff复制代码+<div > <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>
效果如下:
七、路由和导航
首先申明,
常用的API都是自动导入的,无需引入。
约定的几个文件夹里面的东西都是可以直接使用的,无需引入。
可以直接通过 const route = useRoute() 拿到 route 页面路由信息,通过它可以获取路由 query 和 params。
- 1)
http://localhost:3000?name=fg&age=30
const { name, age } = route.query 可以拿到 name 和 age 信息。
- 2)
http://localhost:3000/product/5
某个路由导航到这里,那么就可以命中 src/pages/product/[pId].vue 页面,页面里面可以通过如下代码获取到 pId:
const { pId } = route.params 可以拿到 pId 信息。
-
-
路由导航,有2种方式,如下
<NuxtLink to="/product/5" > 进入详情 </NuxtLink- 使用编程方式导航,
const router = useRouter()拿到路由示例,然后router.push("/product/5")
-
八、数据获取
这个直接看官网就行了,nuxt.com/docs/gettin…。
主要有三种方式:useFetch, useAsyncData and $fetch。
九、引入其他常用 moduls
大家可以在 nuxt.com/modules 找自己想要的
很多人官网不需要用户登录,所以 pinia 不是通用的,vueuse 倒是比较通用,还有 image 也不错,所以我就安装了这2个:
sh复制代码pnpm add @vueuse/nuxt @nuxt/image
然后加到 nuxt.config.ts 的 modules 配置里,到此已经有 3个 了: modules: [''@nuxt/ui'', ''@vueuse/nuxt'', ''@nuxt/image''],.
十、打包部署
执行 pnpm build 即可完成打包,然后在服务器上运行如下命令 node .output/server/index.mjs 即可,如下图:
为了保证程序运行更加稳健,我们通常会使用 PM2,nuxt.com/docs/gettin…。
需要配置 ecosystem.config.cjs,内容如下:
js复制代码module.exports = { apps: [ { name: ''NuxtAppName'', port: ''3000'', exec_mode: ''cluster'', instances: ''max'', script: ''./.output/server/index.mjs'' } ] }
在服务器上,安装 PM2: npm i -g pm2,
然后运行 PM2:pm2 start ecosystem.config.cjs
可以看到运行了 10 个 cluster,应用名为 NuxtAppName。
其他常用命令如:pm2 stop NuxtAppName,pm2 reload NuxtAppName。
至此,完结~
总结
总结一下本文,主要描述了如何生成一个 nuxt3 项目模板,有以下 十个 步骤:
- 一、基本配置
prettier、src目录、.npmrc等 - 二、引入
nuxt-ui(可选) - 三、引入
tailwindcss/unocss - 四、整理布局
- 五、响应式布局
- 六、表单验证
- 七、路由和导航
- 八、数据获取
- 九、引入其他常用
moduls - 十、打包部署
全文完~