Nuxtjs3 备忘
2022-05-10 tech vue front-end nuxtjs 24 mins 10 图 8503 字
作为一个本职工作是基础设施开发的后端人员,我将js比作linux系统,将vue比作docker/containerd,而nuxt就相当于kubernetes了。当然这个类比并不是非常准确。前端比容器大战要柔和的多,感觉大家都在底层上重复建设。对比容器技术,swarm/k8s/mesos也就5年左右就分出了胜负。前端目前主流还是react/vue/Angular这三家,基于这三家之上又有各自的服务端框架(更底层typescript倒是已经统一了),导致前端学习门槛较高,框架概念、设计模式其实也走了一遍后端框架的老路,很多概念后端开发可以平移过来。我比较相信Taylor Otwell(Laravel的创造者),他说vue好,所以我就跟着他一起用了。
前两篇记录了点vue2和vue3的笔记,这一篇记录下nuxtjs3,这是官网,2022 年 4 月 20 日正式发布 nuxt3,代号为“Mount Hope”。
Nuxt3是基于 Vue3 发布的 SSR 框架:
- SPA应用:也就是单页应用,这些多是在客户端的应用,不能进行SEO优化(搜索引擎优化)。
- SSR应用:在服务端进行渲染,渲染完成后返回给客户端,每个页面有独立的URL,对SEO友好,对应企业网站、商品展示 、博客这类型的展示型网站。
这是官网显示的一些优势说明:
- 更轻量:以现代浏览器为基础的情况下,服务器部署和客户端产物最多减小75倍。
- 更快:用动态服务端代码来优化冷启动。
- Hybird:增量动态生成和其他高级模式现在都成为可能。
- Suspense: 导航前后可在任何组件中获取数据。
- Composition API : 使用Composition API 和 Nuxt3的composables 实现真正的可复用性。
- Nuxt CLI : 权限的零依赖体验,助你轻松搭建项目和集成模块。
- Nuxt Devtools :专属调试工具,更多的信息和快速修复,在浏览器中高效工作。
- Nuxt Kit :全新的基于 TypeScript 和跨版本兼容的模块开发。
- Webpack5 : 更快的构建速度和更小的构建包,并且零配置。
- Vite:用Vite作为你的打包器,体验轻量级的快速HMR。
- Vue3 : 完全支持Vue3语法,这一点特别关键。
- TypeScript:由原生TypeScript和ESM构成,没有额外配置步骤。
安装
npx
是npm从5.2版开始增加的命令
npx nuxi init nuxt3-test # 快速创建
npm install
npm run dev
生成文件如下:
这里要重点提的是 nuxt.config.js
文件,所有配置都在里面。
一些常用的目录如下:
- pages // 页面目录,Nuxt.js 框架读取该目录下所有的 .vue 文件并自动生成对应的路由配置。
- components // 组件目录
- assets // 静态资源目录
- public // 不需要 vue 处理的静态资源,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理。 服务器启动的时候,该目录下的文件会映射至应用的根路径 / 下。
- layouts // 项目布局目录
- common // 存储js文件
- plugins // nuxt扩展的js文件
- store // vuex的配置文件,Nuxt.js 框架集成了 Vuex 状态树 的相关功能配置,在 store 目录下创建一个 index.js 文件可激活这些配置。
配置参考链接:
删除根目录下的app.vue,然后pages目录下新建index.vue文件,就会自动关联vue-router,支持动态路由。
因为我是容器化环境开发的,所以改了一下运行端口到80端口,访问方便一些。如果发现配置不生效,可以尝试重启一卡容器(多个容器在同一个目录下启动npm run时感觉会冲突,就遇到有些配置不生效了)。
一些官网备忘
head
在setup
函数中,可以useHead
使用与元标记对应的键的元属性对象进行调用:title
, titleTemplate
, base
, script
, style
, meta
and link
, 以及htmlAttrs
and bodyAttrs
。还有两个速记属性charset
和viewport
。
data-fetching
Nuxt 提供useFetch
、useLazyFetch
和useAsyncData
来useLazyAsyncData
处理应用程序中的数据获取。
state-management
useState
是一个对 SSR 友好的ref
替代品。它的值将在服务器端渲染后(在客户端水化期间)被保留,并使用唯一键在所有组件之间共享。
server-routes
Nuxt 会自动扫描 、 和 目录中的文件~/server/api
,~/server/routes
以~/server/middleware
注册具有 HMR 支持的 API 和服务器处理程序。
每个文件都应该导出一个用defineEventHandler()
.
路由
Nuxt使用了自动加载路由的方案,他会根据你项目的目录来自动创建router对象;
一个简单的示例,pages下的目录示例及其对应的路由如下所示:
test:
a.vue -- 对应路径:/test/a
index.vue -- 对应路由:/test
index:
_id:
a.vue -- 对应路由:/{id}/a
index.vue -- 对应路由:/{id}
index.vue -- 对应路由:/,会被包含到上一级的index.vue的<nuxt-child>中
index.vue -- 对应路由:/,其中如果包含有nuxt-child元素,那么会将index目录下的index.vue加载到nuxt-child元素位置
- 在 Nuxt.js 里面定义带参数的动态路由,需要创建对应的以下划线作为前缀的 Vue 文件 或 目录。
- 如果你想将
_id
设置为必选的路由,需要在 users/_id 目录内创建一个 index.vue 文件。
路由参数校验
Nuxt.js 可以让你在动态路由组件中定义参数校验方法。
举个例子: pages/users/_id.vue
export default {
validate ({ params }) {
// 必须是number类型
return /^\d+$/.test(params.id)
}
}
如果校验方法返回的值不为 true或Promise中resolve 解析为false或抛出Error , Nuxt.js 将自动加载显示 404 错误页面或 500 错误页面。
未知嵌套深度的动态嵌套路由
如果您不知道URL结构的深度,您可以使用_.vue动态匹配嵌套路径。这将处理与更具体请求不匹配的情况。
文件结构:
pages/
--| people/
-----| _id.vue
-----| index.vue
--| _.vue
--| index.vue
知识点
一些代码只是为了做代码的结构备忘,不代表逻辑正确。
前端的语法糖太多了,我觉得这些语法糖是前端晦涩的一大原因。
不过,“无他,但手熟尔”,在前端也是一样。
-
命令行
nuxi
npx nuxi init nuxt3-test # 快速创建
-
layout 布局 html(静态)
layout文件夹 page文件夹
-
引入 css
<style scope> .abc {} </style>
-
应用状态state
<script setup> const options = reactive({ xxx: "xxx" }); </script> <template> <button class="abc" :class="options.xxx === 'xxx' && 'option-active'">abc</button> </template>
-
a标签替换为 NuxtLink 标签
<NuxtLink to="/">Home</NuxtLink>
-
简单的typescript
我觉得typescript的语法有点像golang。 看着也挺舒服的~
先定义个interface类型,一些选项/常量用enum定义,再const一个实例。
<script setup lang="ts"> interface OptionState { xxx: string, yyy: string } enum Xxx { AAA = "aaa", BBB = "bbb" } const obj := OptionState{ xxx: Xxx.AAA, yyy: Xxx.BBB } const options = reactive<OptionState>({ xxx: Xxx.AAA, yyy: Xxx.BBB }); </script>
-
import 其他文件里定义的内容
import {OptionState} from "@/data" // 其他文件要 export特定的定义
教程有的为了简化内容,直接准备一个json文件,然后import数据进来:
<script setup lang="ts"> import {xxx} from "@/data.json" const rest = { first: [...xxx].splice(0,25), second: [...xxx].spice(25,25), }; </script> <template> <Row v-for="r in rest.first" :key="r.id" /> <Row v-for="r in rest.second" :key="r.id" /> </template>
-
更新state
<script setup lang="ts"> const computeSelectedNames = () => { const filterNames = names .filter((name) => name.xxx === options.xxx ) .filter((name) => name.yyy === options.yyy ) .filter((name) => { return ... }) selectdNames.value = filterdNames.map((name) => name.xxx ) }; const selectdNames = ref<string[]>{[]} </script> <template> <button class="abc" @click="computeSelectName">Find</button> {{ selectedNames }} </template>
-
注意拆分component,以及通过props在父子组件中传值
<script setup lang="ts"> interface OptionProps{ option: { id: string, title: string, catagory: string, index: number }; } const props = defindProps<OptionProps>() </script>
在父组件中传入:
大写开头的标签就是component,有一些是 `nuxt` 保留的,用来作为头部header内容的,类似于 `metadata`
10. 通过事件触发父子组件传值。
```vue
// 父:
<script setup lang="ts">
const removeName = (index: number) => {
const filteredNames = [...selectdNames.value]
filterNames.splice(index,1)
selectedName.value = filterNames
}
</script>
<template>
<CardName v-for="(name, index) in selectedNames" :key="name" :name="name" @remove="()=> removeName(index)" :index="index" />
</template>
// 子CardName:
<script setup lang="ts">
interface NameProps{
name: string;
index: number
}
const props = defindProps<NameProps>();
const emit = defindEmits(["remove"])
const removeName = () => {
emit("remove", props.index)
}
</script>
<template>
<p @click="removeName">xxx </p>
</template>
-
常用的循环语句
v-for
-
Pages, 基于文件的路由route
<script setup lang="ts"> // import {} from "#app"; // 用来让编辑器不报错的,其实没什么用。 import xxx from "@/data.json" const route = useRoute(); const name = route.params.name; const x = xxx.find(r => r.name === name) </script> <template> {{ name }} </template>
-
404页面
-
Layouts
创建
layouts
文件夹,并创建default.vue
文件。在page中声明使用特定的layout
<template> <div> <NuxtLayout name="custom"> <Xxx /> </NuxtLayout> </div> </template>
在layout中插入slot(假设layout名error)
<template> <div> <slot name="xxx" /> <div>...</div> </div> </template>
在page中:
<template> <div> <NuxtLayout name="error"> <template #xxx> <h1></h1> </template> </NuxtLayout> <div>...</div> </div> </template>
-
metadata
为了方便控制,也可以写在
script
里,也可以使用
outside stylesheets
方式:在nuxt.config.js里配置。
export default defineNuxtConfig({ charset: "utf-8", viewport: "width=device-width, initial-scale=1, maximum-scale=1", meta: { link: [ { rel: "stylesheet", href: "/assets/css/style.css" }, { rel: "stylesheet", href: "/assets/css/font-awesome.min.css" }, ], script: [ { src: '/assets/js/jquery.min.js' }, ], } })
-
网页数据状态设计
-
composable,useState
用来保存状态。不能用
ref
做跟踪,因为这里没有render,会造成内存泄漏。 -
Global State
-
目前还是测试阶段,官方不建议用于生产。