nuxt3项目总结

5,895 阅读10分钟

nuxt3项目总结

仓库

前言

大半年的时间,项目从秋天到春天,从管理后台到APP再到数据大屏,技术栈从vue3到uniApp再到nuxt3,需求不停的改,注释掉代码都快到项目总体的三分之一。

一、准备-搭建项目架子

1.1 创建一个nuxt项目

nuxt中文文档,项目创建有完整的讲解和命令,复制使用就行。

1.2 项目基础配置

1.2.1 代码规范化配置

代码规范化,提高代码质量,遵循一定的规范可以减少错误和漏洞的出现,提高可读性和可维护性。在本项目的初期,搭建项目架子时,因为经验问题,我没有在项目中配置任何的规范性配置,导致其他同事在后期参与进来开发时,出现许多意料之外的错误。 配置命令

npm install eslint prettier eslint-plugin-prettier eslint-config-prettier --save-dev

在项目运行的终端运行此命令,添加eslint和prettier包,命令运行完成以后,在项目目录下新建**.eslintrc.js**,.eslintignore.prettierrc.js.prettierignore四个文件,完成以后目录结构如下图 目录结构 .eslintrc.js配置文件内容

// @see: http://eslint.cn

module.exports = {
	root: true,
	env: {
		browser: true,
		node: true,
		es6: true
	},
	/* 指定如何解析语法 */
	parser: "vue-eslint-parser",
	/* 优先级低于 parse 的语法解析配置 */
	parserOptions: {
		parser: "@typescript-eslint/parser",
		ecmaVersion: 2020,
		sourceType: "module",
		jsxPragma: "React",
		ecmaFeatures: {
			jsx: true
		}
	},
	/* 继承某些已有的规则 */
	extends: [
		"@nuxtjs",
		"@nuxtjs/eslint-config",
		"@nuxtjs/eslint-config-typescript",
		"plugin:@typescript-eslint/recommended",
		"prettier",
		"plugin:prettier/recommended"
	],
	/*
	 * "off" 或 0    ==>  关闭规则
	 * "warn" 或 1   ==>  打开的规则作为警告(不影响代码执行)
	 * "error" 或 2  ==>  规则作为一个错误(代码不能执行,界面报错)
	 */
	rules: {
		// eslint (http://eslint.cn/docs/rules)
		"no-var": "error", // 要求使用 let 或 const 而不是 var
		"no-multiple-empty-lines": ["error", { max: 1 }], // 不允许多个空行
		"no-use-before-define": "off", // 禁止在 函数/类/变量 定义之前使用它们
		"prefer-const": "off", // 此规则旨在标记使用 let 关键字声明但在初始分配后从未重新分配的变量,要求使用 const
		"no-irregular-whitespace": "off", // 禁止不规则的空白

		// typeScript (https://typescript-eslint.io/rules)
		"@typescript-eslint/no-unused-vars": "error", // 禁止定义未使用的变量
		"@typescript-eslint/prefer-ts-expect-error": "error", // 禁止使用 @ts-ignore
		"@typescript-eslint/no-inferrable-types": "off", // 可以轻松推断的显式类型可能会增加不必要的冗长
		"@typescript-eslint/no-namespace": "off", // 禁止使用自定义 TypeScript 模块和命名空间。
		"@typescript-eslint/no-explicit-any": "off", // 禁止使用 any 类型
		"@typescript-eslint/ban-types": "off", // 禁止使用特定类型
		"@typescript-eslint/explicit-function-return-type": "off", // 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式类型声明
		"@typescript-eslint/no-var-requires": "off", // 不允许在 import 语句中使用 require 语句
		"@typescript-eslint/no-empty-function": "off", // 禁止空函数
		"@typescript-eslint/no-use-before-define": "off", // 禁止在变量定义之前使用它们
		"@typescript-eslint/ban-ts-comment": "off", // 禁止 @ts-<directive> 使用注释或要求在指令后进行描述
		"@typescript-eslint/no-non-null-assertion": "off", // 不允许使用后缀运算符的非空断言(!)
		"@typescript-eslint/explicit-module-boundary-types": "off", // 要求导出函数和类的公共类方法的显式返回和参数类型

		// vue (https://eslint.vuejs.org/rules)
		"vue/no-v-html": "off", // 禁止使用 v-html
		"vue/script-setup-uses-vars": "error", // 防止<script setup>使用的变量<template>被标记为未使用,此规则仅在启用该no-unused-vars规则时有效。
		"vue/v-slot-style": "error", // 强制执行 v-slot 指令样式
		"vue/no-mutating-props": "off", // 不允许组件 prop的改变
		"vue/custom-event-name-casing": "off", // 为自定义事件名称强制使用特定大小写
		"vue/attributes-order": "off", // vue api使用顺序,强制执行属性顺序
		"vue/one-component-per-file": "off", // 强制每个组件都应该在自己的文件中
		"vue/html-closing-bracket-newline": "off", // 在标签的右括号之前要求或禁止换行
		"vue/max-attributes-per-line": "off", // 强制每行的最大属性数
		"vue/multiline-html-element-content-newline": "off", // 在多行元素的内容之前和之后需要换行符
		"vue/singleline-html-element-content-newline": "off", // 在单行元素的内容之前和之后需要换行符
		"vue/attribute-hyphenation": "off", // 对模板中的自定义组件强制执行属性命名样式
		"vue/require-default-prop": "off", // 此规则要求为每个 prop 为必填时,必须提供默认值
		"vue/multi-word-component-names": "off" // 要求组件名称始终为 “-” 链接的单词
	}
};

.eslintignore配置需要忽略的文件

*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
html
/public
/docs
.husky
.local
/bin
.eslintrc.js
.prettierrc.js
/src/mock/*
.nuxt/

.prettierrc.js代码规范化配置

// @see: https://www.prettier.cn

module.exports = {
	// 超过最大值换行
	printWidth: 130,
	// 缩进字节数
	tabWidth: 2,
	// 使用制表符而不是空格缩进行
	useTabs: true,
	// 结尾不用分号(true有,false没有)
	semi: true,
	// 使用单引号(true单双引号,false双引号)
	singleQuote: false,
	// 更改引用对象属性的时间 可选值 "<as-needed|consistent|preserve>"
	quoteProps: "as-needed",
	// 在对象,数组括号与文字之间加空格 "{ foo: bar }"
	bracketSpacing: true,
	// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
	trailingComma: "none",
	// 在JSX中使用单引号而不是双引号
	jsxSingleQuote: false,
	//  (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号 ,always:不省略括号
	arrowParens: "avoid",
	// 如果文件顶部已经有一个 doclock,这个选项将新建一行注释,并打上@format标记。
	insertPragma: false,
	// 指定要使用的解析器,不需要写文件开头的 @prettier
	requirePragma: false,
	// 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行
	proseWrap: "preserve",
	// 在html中空格是否是敏感的 "css" - 遵守 CSS 显示属性的默认值, "strict" - 空格被认为是敏感的 ,"ignore" - 空格被认为是不敏感的
	htmlWhitespaceSensitivity: "css",
	// 换行符使用 lf 结尾是 可选值 "<auto|lf|crlf|cr>"
	endOfLine: "auto",
	// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
	rangeStart: 0,
	rangeEnd: Infinity,
	// Vue文件脚本和样式标签缩进
	vueIndentScriptAndStyle: false
};

.prettierignore 忽略文件

/dist/*
/html/*
.local
/node_modules/**

**/*.svg
**/*.sh

/public/*

配置文件完成以后,在项目的package.json文件中增加下面的配置``

"scripts": {
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "lint:fix": "eslint --fix --ext .js,.vue --ignore-path .gitignore .",
  }

项目就能在编写时进行代码规范化检测了,并且还可以使用vscode的一些配置进行自动的代码规范化。

1.2.2 插件配置

在本项目中引用element-Plus UI组件库,Echarts,Pinia状态管理器。 element-Plus

npm install element-plus --save

在项目目录中找到plugins文件夹,在文件夹下新建element-plus.client.ts文件

import { defineNuxtPlugin } from "#app";
import "element-plus/dist/index.full";
import zhCn from "element-plus/dist/locale/zh-cn.mjs";
import ElementPlus from "element-plus";
export default defineNuxtPlugin(nuxtApp => {
	nuxtApp.vueApp.use(ElementPlus, {
		locale: zhCn
	});
});

在项目配置文件nuxt.config.ts css修改成css: ["element-plus/dist/index.css"],,将element的样式引入到项目中。

Echarts

一个基于 JavaScript 的开源可视化图表库

npm install echarts --save

在项目目录中找到plugins文件夹,在文件夹下新建echarts.ts文件

import * as echarts from "echarts"; // echarts 按需引入

export default defineNuxtPlugin(nuxtApp => {
	nuxtApp.vueApp.config.globalProperties.$echarts = echarts;
});

在项目配置文件nuxt.config.ts 中vite下面的build要将echarts写入

build: {
		transpile: ["echarts"]
	}

在本项目中echarts是按需引入,为了减少项目的体积,因此,在查阅文档请查看echarts的按需引入源码。 项目还引入了highcharts,完成一些特殊的图表。highcharts是一个完全的js库,因此不需要使用上面的插件引入,只需要安装好包之后,在页面直接使用进行。

安装命令

npm i highcharts --save

使用示例

import Highcharts from 'highcharts'
import HighchartsMore from 'highcharts/highcharts-more'

HighchartsMore(Highcharts)

Pinia状态管理器

Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。

安装命令

npm install pinia @pinia/nuxt

在项目目录中找到plugins文件夹,在文件夹下新建pinia.ts文件

// plugins/pinia.ts
import { defineNuxtPlugin } from "#app";
import { createPinia } from "pinia";

export default defineNuxtPlugin(nuxtApp => {
	nuxtApp.vueApp.use(createPinia());
});

简单使用示例 在项目目录下新建文件夹stores,这个文件夹存放所有的状态变量。在文件夹下新建文件demo.ts

// stores/demo.ts
import { defineStore } from "pinia";

//
export const useExampleCount = defineStore({
	id: "pageState",
	state: () => ({
		Count: 0
	}),
	actions: {
		increment() {
			this.Count++;
		}
	}
});

在需要使用的页面引入使用

import { useExampleCount } from '@/stores/demo';
const pageState = useExampleCount();

使用时pageState.state代表状态的值,pageState.increment表示修改值的方法

1.2.3 数据请求

数据获取这一个块,Nuxt 提供了两个组合函数和一个内置库,用于在浏览器或服务器环境中执行数据获取:useFetch、useAsyncData 和 $fetch,点击查看详细教程。不过,在搭建架子时,我没注意到这两个函数,因此我引入axios,同时方便我写请求拦截和相应拦截。 安装命令

npm i axios 

安装命令完成以后,在项目目录下找到server文件夹,在api文件夹下新建request.ts文件

// 我们使用的是vue封装好的异步,所以需要引入axios
import axios from "axios";
// 引入element-plus 是为了请求失败时调用element-plus的弹框组件
import { ElMessage } from "element-plus";
// 创建axios实例
const service = axios.create({
	// 设置基础访问路径,看自己需要修改
	baseURL: "http://192.168.2.217", // 测试服务器地址
	timeout: 15000 // 请求超时时间
});
// http request 拦截器
service.interceptors.request.use(
	config => {
		// 这里可以处理 token ,需要根据需要进行处理,我这里先不处理,
		return config;
	},
	// 抛出错误,阻止程序运行
	err => {
		return Promise.reject(err);
	}
);
// http response 拦截器
service.interceptors.response.use(
	response => {
		// 判断请求是否正常
		if (response.data.code !== "000000") {
			// 出现错误,使用element-plus的弹窗组件,提示错误信息
			ElMessage({
				showClose: true,
				message: response.data.message,
				type: "error"
			});
			return Promise.reject(response.data);
		} else {
			// 成功返回数据
			return response.data;
		}
	},
	// 抛出错误
	error => {
		return Promise.reject(error.response);
	}
);
// 导出service
export default service;

在同级目录中,新建api.ts用来存放请求API

/ 这里引入我们封装的axios,看你自己的路径
import request from "@/server/api/request";
// const api_name = '/api/member/'
export default {
	/**
	 * 示例
	 * @param params
	 * @returns
	 */
	getdemo(params: any) {
		return request({
			url: ``,
			method: "post",
			data: params
		});
	}
};

至此,项目的基础配置和一些必要的配置已经完成。

二,一些问题的记录

2.1 build

在我完成,最初一版需求编写完成以后,我觉得一切都很顺利,不过当我build的时候,项目一直报错,说找不到相应的项目文件,但当我检查对应文件时,并没有任何的问题。随后,便是查资料,定位问题最后发现项目的==父级目录是中文名导致一直找不到文件==,在更换项目父级目录名之后,成功build。

2.2 部署

由于公司此前并没有这样的SSR项目,以前的web项目都是由后端的同事通过nginx部署的。我只能自己使用pm2部署,由于此前服务器上的node环境是12,我又去更改node版本,修改环境变量,编写执行脚本,最终才将项目部署完成。 但是不太明白的时,每次重新登录服务器之后都要source /etc/profile重启配置文件,才能将node版本切回16,否则它就是12.

2.3 打包时清除打印日志与debug

起初,我并没有关注这个问题,但在项目中大量使用WebSocke来达到实时通信传递数据的要求。打包时,没有清除掉这些打印日志,导致项目在正式环境运行到半个小时以后网页直接崩溃。 后来,就着手解决这个问题,但在查了许多插件以后,都是支持vue3但不支持nuxt3,最后翻看公司以前的项目时,才找vite自带了这个能力,刚好nuxt3也使用vite,在nuxt配置文件中修改以后,打印时成功消除了注释。

vite: {
		css: {
			preprocessorOptions: {
				scss: {
					additionalData: '@use "@/assets/styles/default.scss" as *;' // 注意文件路径要配成自己的
				}
			}
		},
		plugins: [],
		esbuild: {
			// * 打包去除 console.log && debugger
			pure: ["console.log", "debugger"]
		}
	},

2.4 keep alive页面保活

查看nuxt的官网,就只有一句简单的介绍 nuxt的keepalive 老实说,真的没太明白要怎么用,后来又找了不少资料,才明白它这个keepalive是在app.vue中来配置的

<template>
	<div>
		<NuxtPage keepalive />
	</div>
</template>
<script setup>
definePageMeta({
	key: route => route.slug,
	transition: {
		name: "page"
	},
	keepalive: {
		exclude: ["index"] // 需要缓存的页面
	}
});
</script>

2.5 全局自动适配

一开始时,也是尝试了不少方法,来满足项目在适合尺寸的屏幕上的适配。后来找到一些资料可以通过调整document的zoom属性来全局适配。到这里,项目已经能够完美的适配大屏了,我觉得这部分代码应该不会再改了。但我还是低估人的创造能力,突然,有一天客户拿着pad加载项目,问我为什么不适配。我顿时就麻了,有人拿移动设备来看数据大屏,这谁能想到,但是没办法接着改吧。

// 全局根据屏幕的变化进行适配 在app.vue中
 onMounted(() => {
     if (typeof window !== "undefined") {
     const percentageOn1 = (window.screen.width / 1920) * 100;
     document.body.style.zoom = percentageOn1 + "%";
    }
    if (window.screen.width > window.screen.height) {
       const percentageOn1 = (window.screen.width / 1920) * 100;
       document.body.style.zoom = percentageOn1 + "%";
     }
     else {
       const percentageOn1 = (window.screen.height / 1920) * 100;
       document.body.style.zoom = percentageOn1 + "%";
    }
 });

写在最后

最近时间不忙的话,会尽快将app中一些东西也记录一下,时间拖久了很多细节自己也会忘记。