nuxt框架的约定优于配置、响应数据、服务端客户端执行2遍

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等变量变成响应式的了,就可以实现点击后局部刷新的了。

猜你喜欢

转载自blog.csdn.net/andux/article/details/143138375