web框架的发展,就会有越来越多的配置是可以约定俗成的,默认的,比如laravel就默认很多约定,既是你不用配置,它也可以执行,相当于默认值是true。
nuxt框架则更甚一步,尽可能多的不用手动配置,都把它当成约定的了,比如路由都约定了,不用配置,其他框架基本上都需要配置路由,或者简化配置路由。
nuxt的很多约定是基于文件夹的,当然是根目录的文件夹,或者路由文件夹,路由文件夹server相当于后端,其他的文件夹相当于前端。很多文件夹里的文件都是自动加载,无需配置引入,可以直接用。这点就相当神奇,ide一般都不能自动识别,显示错误。ide都要溯源,没有源头引用,它就报错。
.nuxt
测试开发执行的代码文件夹,就是pnpm dev执行的代码文件夹,development 开发模式代码
.output
生成的生产模式的代码文件夹,production 生产模式代码
.dist
其他模式生成的代码文件夹,比如nuxthub生成的 production 生产模式的代码
assets 与 public
两个都是放置静态资源的,比如css,图片等,nuxt框架里就不需要放js代码了吧。assets是不能直接通过网址访问,只能通过nuxt的框架代码来访问的资源,而public则可以通过网址访问的静态资源。
components 与 composables
都是组件库文件夹,components是vue组件库,而composables是js/ts组件库,js/ts组件库表现形式就是带有export输出的函数。两个组件库都是自动导入,使用时无需引用的,都是nuxt约定配置的文件夹。
middleware
middleware是路由中间件文件夹,但是他不是复数,不带s,有点奇怪,其他几个不带s的文件夹是content、public和server,content是nuxt自创的content模式静态md文档模式,public是公共文件夹不带s正常,server是服务器,不带s也正常,为什么middleware中间件不带s呢?是middleware复数模式就是middleware吗?middleware在server文件夹里,是export default defineEventHandler。
plugins 与 utils
plugins是插件文件夹,utils是工具箱文件夹,这两个文件夹感觉功能和composables是一样的,nuxt将他们分开,是因为它们代表了业务逻辑的不同用途吧。
这两个文件夹都可以放在根目录,就是“前端”的功能文件夹,也可以在server文件夹里,就成为了“后端”的功能文件夹了,前端是指业务逻辑,后端是指与数据库数据打交道。plugins放在前端,也就是根目录,就是 export default defineNuxtPlugin,放在后端,就是export default defineNitroPlugin了。
utils工具箱与composables,官方的解释是a semantic distinction,意思就是名字不同而已,utils放在server文件夹里,就是后端的js/ts组件库了,用来辅助后端,或者操作判断路由的,如判断路由是否以什么什么开头:
import type { H3Event } from 'h3';
export const isServerApi = (event: H3Event) => {
return event.path.startsWith('/api');
};
layouts 与 pages 与 content
layouts是模版文件夹, pages是vue页面文件夹,components是vue组件文件夹。content是md等静态页面文件夹,官方说有.md, .yml, .csv and .json。
schema 与 stores 与 types 与 modules
schema是数据库实体类文件夹,stores是状态文件夹,函数名有默认规则,例如export const useXXXStore,其中XXX就是某个状态的名称,使用的时候,就是直接用:
const store = useXXXStore();
types文件夹是非数据库实体类,就是除了数据库实体类之外的自添加的实体类,用于拓展某些数据类型或字段,比如创建一个全局可用的管理员账户:
import 'h3';
declare module 'h3' {
export interface H3EventContext {
user: {
name: string;
};
}
}
types文件夹里面的非数据库实体类的文件名是 xxx.d.ts,可以拓展非数据库实体类和方法。schema文件里面就是普通的.ts命名方法,通过操作后,直接修改的是数据库。
这几天看了b站的nuxt视频教程,搜索出来第一个就是:
这个视频讲的主要内容就是服务端执行一次和客户端执行一次,就是通常会执行2次,这个我还真是没注意呢,他这个视频主要将服务端客户端都执行的问题,nuxt框架或者说vue类似框架,响应式数据也是一个非常重要的点。响应式数据,就是vue等常讲的,双向绑定,某个地方修改,其他地方自动更新,这个双向绑定也是vue等新型框架最引人瞩目的吸引点,以前的js框架想要自动刷新,都需要axios等技术,才能实现局部刷新。
<template>
<div>
<UButton label="aaa添加" @click="aaa++,console.log(aaa)"/>
<UButton label="bbb添加" @click="bbb++"/>
</div>
</template>
<script setup lang="ts">
const aaa = 111
console.log(aaa)
const bbb = ref(222)
console.log(bbb.value)
</script>
上面的代码中,aaa就不是响应式数据,而bbb就是定义的响应式数据,
运行后,ssr就是服务端执行,下面的111,222就是客户端执行的,所以是执行了2次。
当然,如果在nuxt.config.ts配置 ssr: false,就只有客户端执行了:
为什么要开启ssr,服务端执行后就会生成全部代码,有利于网页的seo,主要就是为了seo。如果不需要搜索引擎的seo,可以关闭ssr功能,这样代码就是在客户端执行,减少一次服务端执行,减轻了服务器的压力。
当然也可以加一个判断,看是服务端还是客户端的,或者想要它在服务端执行,还是客户端执行:
if (import.meta.client) {
}
if (import.meta.server) {
}
从这个视频,还学到了全局css样式,不用写在app.vue框架入口文件上,而是放在assets文件夹里,建立.scss样式文件,安装sass: pnpm add sass, "sass": "^1.80.2",或者直接添加到package.json文件里,执行pnpm install。
$color1: green;
.color2 {
color: red;
}
.shantext {
animation: text-blink 1s infinite alternate;
}
@keyframes text-blink {
from {
opacity: 1;
color: #000;
}
to {
opacity: 0.5;
/* color: #000; */
}
}
这样就可以直接使用css样式了。还需要在nuxt.config.ts配置文件中配置一下,才能全局访问:
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ['@nuxt/ui', '@nuxthub/core'],
compatibilityDate: '2024-10-16',
// css: ['~/assets/css/base.scss'],
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "~/assets/css/base.scss" as *;',
},
},
},
},
runtimeConfig: {
// 服务端配置
count1: 1,
public: {
// 客户端配置
count2: 2,
appName: 'Andux',
},
},
ssr: false,
});
css: ['~/assets/css/base.scss'],这样引用,只能使用静态样式,在vite中引入样式,则可以使用变量$color1了。vite相当于把scss文件中的变量,引入到全局可以访问的了。
在pages目录中添加 [...404].vue文件,就创建了404默认页面了:
<template>
<div class="flex flex-col items-center justify-center py-28 min-h-0 w-full">
<p class="text-base font-semibold text-primary">404</p>
<h1
class="text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-5xl"
>
Page <span class="shantext text-lime-500">not</span> found
</h1>
<p class="mt-6 text-base/7 text-gray-500 dark:text-gray-400 text-center">
您要查找的页面不存在。
</p>
<UButton label="回到首页" to="/" class="mt-6 text-lg" />
</div>
</template>
<script setup lang="ts">
useHead({
title: '404',
});
</script>
not这个单词就使用了assets里面定义的全局样式shantext。
not就是一个动态的变化的字了。
nuxt很多时候发现点击不起作用,或者数据没有更新,那就是没有把相关的数据变成响应式的了。
例如在UI官网中介绍useColorMode时候用的样例:https://ui.nuxt.com/getting-started/theming
<script setup lang="ts">
const colorMode = useColorMode()
const isDark = computed({
get () {
return colorMode.value === 'dark'
},
set () {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
})
</script>
<template>
<ClientOnly>
<UButton
:icon="isDark ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
color="gray"
variant="ghost"
aria-label="Theme"
@click="isDark = !isDark"
/>
<template #fallback>
<div class="w-8 h-8" />
</template>
</ClientOnly>
</template>
就是把isDark变量,通过使用computed方法,变成了响应式数据了,这样在点击的时候,就会局部刷新按钮的图标。因为UButton本身就是nuxt框架提供的响应式按钮,所以图标会根据isDark的值而改变,但有些组件,例如导航栏,就需要使用computed还数进行改造。
默认的横向导航栏代码:https://ui.nuxt.com/components/horizontal-navigation
<script setup lang="ts">
const links = [{
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
},
badge: 100
}, {
label: 'Installation',
icon: 'i-heroicons-home',
to: '/getting-started/installation'
}, {
label: 'Horizontal Navigation',
icon: 'i-heroicons-chart-bar',
to: '/components/horizontal-navigation'
}, {
label: 'Command Palette',
icon: 'i-heroicons-command-line',
to: '/components/command-palette'
}]
</script>
<template>
<UHorizontalNavigation :links="links" class="border-b border-gray-200 dark:border-gray-800" />
</template>
改造后的代码:
<script setup lang="ts">
import type { HorizontalNavigationLink } from '#ui/types';
const links = computed<HorizontalNavigationLink[][]>(() => {
return [
[
label: 'Profile',
avatar: {
src: 'https://avatars.githubusercontent.com/u/739984?v=4'
},
badge: 100
}, {
label: 'Installation',
icon: 'i-heroicons-home',
to: '/getting-started/installation'
}, {
label: 'Horizontal Navigation',
icon: 'i-heroicons-chart-bar',
to: '/components/horizontal-navigation'
}, {
label: 'Command Palette',
icon: 'i-heroicons-command-line',
to: '/components/command-palette'
},
{
label: title.value,
icon: icona.value,
iconClass: iconClass.value,
click: () => {
isDark.value = !isDark.value;
},
},]
];
});
</script>
<template>
<UHorizontalNavigation :links="links" class="border-b border-gray-200 dark:border-gray-800" />
</template>
这样把标题和图标都定义成变量,放在isDark的set方法里,就可以将title和icona等变量变成响应式的了,就可以实现点击后局部刷新的了。