帮公司搭了个Nuxt3项目框架(nui框架)

帮公司搭了个Nuxt3项目框架
theme: smartblue
最近公司立项了一个新项目,因为是to C 的,所以对SEO是有较高需求的,由于公司前端技术栈统一用的VUE,顺理成

theme: smartblue

近日,该公司启动了一个新项目。既然是C语言,对SEO的要求就很高,而且他们的前端技术栈使用的是VUE,那么nuxt这个全栈框架就自然而然的选择了。项目建立之后,我负责前端项目的框架搭建,但是根据我搭建过程的经验,我觉得技术栈切换到NUXT还是太难了,所以我就喜欢就在这里分享吧。我已经完善了一个完整的nuxt 项目框架,任何年轻人或老年人都可以立即使用。对了,你也可以关注我的微信公众号发烧码,获取最新的技术分享。

项目结构

-API

– 资产

– 图像

——郎

—- en_us.json

—- zh_cn.json

–sss

—- 常量.scss

—-索引.scss

– 成分

– 可组合

– 店铺

——松果属

–locale.js

–auth.js

— toast.js

-配置

– 持续的

–auth.js

-布局

–默认.vue

— 登录.vue

-中间件

–default.global.js

-页

– 插入

–pinia.js

– 民众

– 服务器

–API

—- 列表.js

– 中间件

—- 请求.js

-utils

— http.js

– .env 开发

– .env.local

– .env.生产

– .eslintrc.cjs

-.gitignore

– .prettierrc.json

-app.vue

– 错误.vue

-i18n.config.js

-nuxt.config.ts

– 包.json

NUXT项目配置

下面是nuxt项目配置的代码。 Acro-design也被集成到项目中,因为UI设计师选择的样式来自arco-design组件库。项目中采用的适配方案是px-to-vw,状态管理工具是nuxt自带的useState和pinia,useState足以进行复杂的状态管理。由于该项目的受众包括国内和国际行业参与者,因此环境变量以VITE_ 开头,客户端可以使用import.env.meta 访问或使用useRuntimeConfig 检索。

//https://nuxt.com/docs/api/configuration/nuxt-config

默认导出defineNuxtConfig({

devtools: {enabled: false},

应用程序: {

头: {

titleTemplate: \’%s-京东商城\’,

title: \”京东商城\”,

字符集: \’utf-8\’,

htmlAttrs: {

lang: \’zh-CN\’

},

元: [

{ name: \’关键词\’, content: \’网购、网上商城、家电、手机、电脑、服装、家居、母婴、美妆、个护、食品、生鲜、京东\’ },

{

name: \’描述\’,

内容:

“京东——专业的综合性网上商城,提供正宗低价的购物选择和优质便捷的服务体验。”产品涵盖家电、手机、电脑、服装、家居装饰、母婴、美妆、个人护理、食品、生鲜等多个品类来自全球数十万品牌商家,满足您的购物需求。 \’

}

]

}

},

//插入全局样式

css: [\’~/assets/scss/index.scss\’],

模块: [

//arco-design UI组件库

\’arco-设计-nuxt-模块\’,

//国际化插件

\’@nuxtjs/i18n\’,

[

//pinia状态管理库

\’@pinia/nuxt\’,

{

//自动将pinia的defineStore方法导入到你的项目中

autoImports: [\’defineStore\’]

}

]

],

//arco-design UI组件库设置

阿科: {

importPrefix: \’A\’,

钩子前缀: \’Arco\’,

locales: [\’getLocale\’],

localePrefix: \’阿科\’

},

//国际插件设置

i18n: {

Strategy: \’no_prefix\’, //如何添加路由前缀

locales: [\’en\’, \’zh\’], //设置语言

defaultLocale: \’zh\’, //默认语言

vueI18n: \’i18n.config.js\’ //通过vueI18n 配置

},

//配置nuxt组件库

组件: [

{

path: \’~/组件\’,

扩展名: [\’.vue\’],

pathPrefix: 假

}

],

导入: {

dirs: [

//扫描可组合目录中的所有模块(包括子文件夹)

\’可组合/**\’

]

},

//发送服务器设置

devServer: {

端口: 3001

},

构建: {

//使用babel对开发和生产环境中的es包进行语法翻译

transpile: [\’元素加/es\’]

},

vite: {

CSS: {

预处理器选项: {

scss: {

//全局引入scss常量供全局使用

添加数据: \’@import \’assets/scss/constant.scss\’;\’

}

}

}

},

postcss: {

插件: {

\’postcss-px-to-viewport\’: {

viewportWidth: 1920 /** 设计稿视口宽度*/,

UnitToConvert: \’px\’ /** 需要转换的单位,默认为\’px\’ */,

UnitPrecision: 5 /** 单位转换后保留的精度*/,

propList: [\’*\’] /** 可以转换为vw的属性列表*/,

viewportUnit: \’vw\’ /** 要使用的视口单位*/,

fontViewportUnit: \’vw\’ /** 字体使用的视口单位*/,

selectorBlackList: [] /** CSS 选择器被忽略*/,

minPixelValue: 1 /** 设置最小转换值*/,

mediaQuery: false /** 是否应该转换媒体查询的单位*/,

Replace: true /** 是否直接替换属性值而不添加替换属性*/,

except: undefined /** 忽略特定文件夹或特定文件中的文件*/,

include: undefined /** 设置仅匹配转换后的文件*/,

Landscape: false /** 是否添加根据LandscapeWidth @media生成的媒体查询条件*/,

LandscapeUnit: \’vw\’ /** 横向模式下使用的单位*/,

LandscapeWidth:unknown /** 横向模式下使用的视口宽度*/

}

}

},

运行时配置: {

mode: process.env.VITE_MODE,

Base_url: process.env.VITE_BASE_URL,

应用程序: {

mode: process.env.VITE_MODE,

Base_url: process.env.VITE_BASE_URL,

}

}

})

客户端请求HTTP模块封装

作为一个全栈框架,Nuxt 有客户端和服务器的概念。在使用nuxt开发的过程中,很多人都存在误解。我相信所有前端请求都会发送到nuxt 服务器,然后该服务器将请求发送到后端接口并返回到前端。事实上,情况并非如此。仅当内容需要SSR(服务器端渲染)时才需要这样做。这意味着您只需对需要渲染的数据执行GET 请求即可。更改可以直接发送到客户端的后端。这是对HTTP模块的封装(requests库使用nuxt自带的$fetch)。

/**

* @描述http模块

*/

从\’@/utils/utils\’ 导入{ JumpToLogin }

//接口基地址

const BASE_URL=import.meta.env.VITE_BASE_URL

//环境

const MODE=import.meta.env.VITE_MODE

//生产环境

const MODE_Production=\’生产\’

//GET请求方法

常量METHOD_GET=\’获取\’

//成功状态

常量SUCCESS_STATUS_TEXT=\’确定\’

//响应类型

const RESPONSE_TYPE=[\’blob\’, \’stream\’]

//请求拦截器

const requestInterceptor=(配置)={

if (config.options.meta?needAuth) {

const { getToken, getUid }=useAuth()

常量令牌=getToken()

常量uid=getUid()

const 方法=config.options.method?toUpperCase()

如果(方法===METHOD_GET){

const 查询=config.options.query ||

config.options.query={ .查询、令牌、uid }

} 除此之外{

const body=config.options.body ||

config.options.body={ .body、令牌、uid }

}

}

返回配置

}

//响应拦截器

const 响应拦截器=(响应)={

const res=响应.响应

if (res.status===200

res.statusText===SUCCESS_STATUS_TEXT

res._data.data

RESPONSE_TYPE.includes(res?type)

){

返回响应

}

如果(模式!==模式_生产){

console.log(res.url, {

code: res._data.code,

data: res._data.data,

res: res._data,

params: 响应.可选,

resHeaders: res.headers

})

}

if (res._data.code===0 || res._data.code===200) {

返回响应

否则if (res._data.code===-50) {

//Token过期或失效

const RouteMeta=useRouteMeta()

const {removeToken,removeUid}=useAuth()

const userInfo=useUserInfo()

删除令牌()

删除Uid()

//清除用户信息

用户信息.value={}

if (routeMeta.value.needAuth) {

//如果当前页面需要权限,登录失败则跳转到登录页面。

跳转到登录()

}

返回Promise.reject(res._data)

}

返回Promise.reject(res._data)

}

//错误拦截器

const errorInterceptor=(err)={

返回Promise.reject(err.error)

}

const httpInstance=$fetch.create({

baseURL: BASE_URL,

onRequest: 请求拦截器,

onResponse: 响应拦截器,

onRequestError: 错误拦截器

})

导出默认的httpInstance

API接口管理模块

///api/common.js

/**

* @description 项目中公开请求API

*/

从“@/utils/common”导入http

//获取上传临时密钥

导出函数getCommonList() {

返回http(\’/api/common/getList\’, {

方法:\’获取\’,

元: {

//标记该接口是否需要权限验证。如果需要,在请求拦截器中进行请求拦截相关的逻辑处理。

needAuth: 正确

}

})

}

NUXT服务端请求

Nuxt服务器接口开发,请求流程:前端-Nuxt服务器-Java等服务器接口

///server/api/list.js

从\’h3\’ 导入{ readRawBody, getQuery, getMethod }

默认导出defineEventHandler(async (event)={

//const res=等待useFetchData(\’/user-center/user/getUserInfo\’, {

//方法,

//baseURL:event.context.baseUrl,

//headers:event.context.headers,

//params: getQuery(事件),

//身体

//})

const 方法=getMethod(event).toUpperCase()

我们先来看看本体

if (方法!==\’GET\’) body=等待readRawBody(事件)

const res=等待$fetch(\’/user-center/user/getUserInfo\’, {

方法,

baseURL:event.context.baseUrl,

headers:event.context.headers,

params: getQuery(事件),

身体

})

返回值|| { userInfo: {} }

})

nuxt服务器中间件处理请求(所有对nuxt服务器的请求和nuxt服务器响应前端进程都在这里拦截并处理)

从\’h3\’ 导入{ getHeaders }

默认导出defineEventHandler((event)={

const reqHeaders=getHeaders(事件)

const ssrHeader=新标头()

const { 应用程序}=useRuntimeConfig()

ssrHeader.set(\’cookie\’, reqHeaders.cookie)

//将请求的基地址插入到nuxt请求中

event.context.baseUrl=app.base_url

//将请求头插入nuxt请求中

event.context.headers=ssrHeader

})

只需之前使用useFetch调用nuxt接口即可。

脚本设置

const { data: 结果}=等待useFetch(\’/api/list\’)

/剧本

路由守卫

Nuxt的路由防护是在客户端中间件中使用defineNuxtRouteMiddleware路由中间件函数实现的。 nuxt中设计的客户端中间件主要用于拦截路由。这意味着客户端的路由交换要经过客户端的中间件。当客户端向nuxt 服务器发送请求时,该请求会被服务器上的中间件拦截并处理。如果您的客户端的中间件文件名使用.global.js,则nuxt 会将其识别为全局有效的中间件。由于我们这里实现的是全局根保护功能,因此文件名为default.global.js。

///middleware/default.global.js

/**

* @description 全局根防护

*/

从\’@/utils/utils\’ 导入{ JumpToLogin }

默认导出defineNuxtRouteMiddleware((to, from)={

const RouteMeta=useRouteMeta()

const { getToken, getUid }=useAuth()

if (to.meta.needAuth (!getToken() || !getUid())) {

//检查您是否登录需要身份验证的页面

跳转到登录()

返回abortNavigation()

}

//记录当前访问页面的元信息,用于http模块认证(必须放在认证逻辑之后)。

rootmeta.value=to.meta ||

})

状态管理

对于项目状态管理,我们使用了Nuxt 集成的轻量级useState 和Vue 官方推荐的pinia。

使用useState封装的Hook直接放在/composables/store文件夹中。

//可组合/store/base.js

/**

* @description 项目基础状态管理模块

*/

从\’@/constants/state\’ 导入{ USER_INFO, ROUTE_META, LOCALE_TYPE }

//管理用户信息

导出const useUserInfo=()=useState(USER_INFO, ()={

返回{}

})

//根元数据

导出const useRouteMeta=()=useState(ROUTE_META, ()={

返回{}

})

//国际化- 本地语言类型

导出const useLocale=()=

> useState(LOCALE_TYPE, () => {
return \’zh\’
})

pinia模块直接放到/composables/pinia文件夹下管理维护。因为nuxt中pinia存在,刷新状态丢失的情况,所以在客户端的plugins中集成了pinia-plugin-persistedstate插件,实现状态数据持久化能力。

// plugins/pinia.js
// https://prazdevs.github.io/pinia-plugin-persistedstate/zh/guide/config.html
/**
* @description pinia数据持久化插件
*/
import piniaPluginPersistedstate from \’pinia-plugin-persistedstate\’
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.$pinia.use(piniaPluginPersistedstate)
})

国际化

项目中使用的国际化插件是vue-i18n,配置如下

// i18n.config.js
import en from \”assets/lang/en_us.json\”;
import zh from \”assets/lang/zh_cn.json\”;
export default defineI18nConfig(() => ({
legacy: false, // 是否兼容之前
fallbackLocale: \’en\’, // 区配不到的语言就用en
messages: {
en,
zh
}
}))

// /assets/lang/en_us.json
{
\”home\”: \”Home\”
}

// /assets/lang/zh_cn.json
{
\”home\”: \”主页\”
}

为了使用方便,我特地在文件夹下封装了一个hook

/**
* @description 提供国际化功能的hook
*/
export function useI18nHook() {
const { locale, setLocale } = useI18n()
const localeLang = useLocale()
// 初始化本地语言类型
localeLang.value = locale

const setLocaleLang = (type) => {
setLocale(type)
localeLang.value = type
}

return {
locale: localeLang,
setLocale: setLocaleLang
}
}

在.vue模版中使用直接如下

<template>
<div>{{ $t(\’home\’) }}
</template>

不仅页面代码需要国际化处理,UI组件库也需要国际化处理,acro-designUI库使用a-config-provider标签在App.vue文件中全局注入本地语言

<template>
<div>
<a-config-provider :locale=\”locale\”>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</a-config-provider>
</div>
</template>
<script setup>
import enUS from \’@arco-design/web-vue/es/locale/lang/en-us\’
import zhCN from \’@arco-design/web-vue/es/locale/lang/zh-cn\’
const locales = {
zh: zhCN,
en: enUS
}
const { locale: langLocale } = useI18nHook()
const locale = computed(() => {
return locales[langLocale] || zhCN
})
useHead({
link: [
{ rel: \’shortcut icon\’, href: \’/favicon.ico\’ },
{ rel: \’apple-touch-icon\’, href: \’/favicon.ico\’ }
]
})
</script>

其他配置

Layout

// /layout/default.vue
<template>
<div class=\”layout-container\”>
<Header />
<main class=\”content\”>
<slot />
</main>
<Footer />
</div>
</template>
<style scoped lang=\”scss\”>
.layout-container {
min-height: 100vh;
.content {
min-height: calc(100vh – 447px);
background: #f7f8fa;
}
}
</style>

权限管理HOOK

// /composables/auth.js
/**
* @description 提供权限管理功能
*/
import { TOKEN_KEY, UID_KEY } from \’@/constants/auth\’
const MODE = import.meta.env.VITE_MODE
const DomainMap = {
development: \’demo.com\’,
production: \’demo.cn\’,
}
export function useAuth() {
const cookieToken = useCookie(TOKEN_KEY, { domain: DomainMap[MODE] }
const cookieUID = useCookie(UID_KEY, { domain: DomainMap[MODE] })
const getToken = () => {
return cookieToken.value
}
const setToken = (token) => {
cookieToken.value = token
}
const removeToken = () => {
cookieToken.value = undefined
}
const getUid = () => {
return cookieUID.value
}
const setUid = (uid) => {
cookieUID.value = uid
}
const removeUid = () => {
cookieUID.value = undefined
}
return {
getToken,
setToken,
removeToken,
getUid,
setUid,
removeUid,
}
}

全局Toast HOOK

toast提示使用的是vue-toast-notification插件

// /composables/toast.js
/**
* @description 全局用toast提示方法
*/
import { useToast as useToastNotification } from \’vue-toast-notification\’
export function useToast() {
return useToastNotification()
}

Eslint配置

配置里继承的@nuxt/eslint-config的规则较为严格,不用也可以去掉

module.exports = {
root: true,
extends: [\’@nuxt/eslint-config\’],
parserOptions: {
ecmaVersion: \’latest\’
},
rules: {
\’vue/multi-word-component-names\’: 0/**针对单个单词组件报错的规则关闭 */,
\’no-undef\’: 0, /**关闭变量未定义检查 */
\’no-debugger\’: 2 /**禁用 debugger */,
\’no-dupe-args\’: 2 /**禁止 function 定义中出现重名参数 */,
\’no-dupe-keys\’: 2 /**禁止对象字面量中出现重复的 key */,
\’no-empty\’: 1 /**禁止出现空语句块 */,
\’no-ex-assign\’: 1 /**禁止对 catch 子句的参数重新赋值 */,
\’no-extra-boolean-cast\’: 1 /**禁止不必要的布尔转换 */,
\’no-extra-parens\’: 1 /**禁止不必要的括号 */,
\’no-extra-semi\’: 1 /**禁止不必要的分号 */,
\’no-func-assign\’: 1 /**禁止对 function 声明重新赋值 */,
\’no-irregular-whitespace\’: 1 /**禁止在字符串和注释之外不规则的空白 */,
\’no-unexpected-multiline\’: 1 /**禁止出现令人困惑的多行表达式 */,
\’no-unreachable\’: 1 /**禁止在return、throw、continue 和 break语句之后出现不可达代码 */,
\’use-isnan\’: 1 /**要求使用 isNaN() 检查 NaN */,
\’dot-location\’: 1 /**强制在点号之前和之后一致的换行 */,
\’eqeqeq\’: 2 /**要求使用 === 和 !== */,
\’no-alert\’: 1 /**禁用 alert、confirm 和 prompt */,
\’no-case-declarations\’: 1 /**不允许在 case 子句中使用词法声明 */,
\’no-else-return\’: 1 /**禁止 if 语句中有 return 之后有 else */,
\’no-empty-function\’: 1 /**禁止出现空函数 */,
\’no-eq-null\’: 1 /**禁止在没有类型检查操作符的情况下与 null 进行比较 */,
\’no-eval\’: 1 /**禁用 eval() */,
\’no-fallthrough\’: 1 /**禁止 case 语句落空 */,
\’no-lone-blocks\’: 1 /**禁用不必要的嵌套块 */,
\’no-redeclare\’: 1 /**禁止使用 var 多次声明同一变量 */,
\’no-self-assign\’: 1 /**禁止自我赋值 */,
\’no-self-compare\’: 1 /**禁止自身比较 */,
\’no-unmodified-loop-condition\’: 1 /**禁用一成不变的循环条件 */,
\’vars-on-top\’: 1 /**要求所有的 var 声明出现在它们所在的作用域顶部 */,
\’eol-last\’: 1 /**强制文件末尾至少保留一行空行强制文件末尾至少保留一行空行 */,
}
}

Premitter配置

{
\”$schema\”: \”https://json.schemastore.org/prettierrc\”,
\”semi\”: false,
\”tabWidth\”: 2,
\”singleQuote\”: true,
\”printWidth\”: 100,
\”trailingComma\”: \”none\”,
\”useTabs\”: true
}

package.json

{
\”name\”: \”app\”,
\”private\”: true,
\”type\”: \”module\”,
\”scripts\”: {
\”build\”: \”nuxt build –dotenv .env.production\”,
\”build:dev\”: \”nuxt build –dotenv .env.development\”,
\”serve\”: \”nuxt dev –dotenv .env.local\”,
\”generate\”: \”nuxt generate\”,
\”preview\”: \”nuxt preview –dotenv .env.production\”,
\”postinstall\”: \”nuxt prepare\”,
\”lint\”: \”eslint . –ext .vue,.js,.jsx,.cjs,.mjs –fix –ignore-path .gitignore\”,
\”format\”: \”prettier\”
},
\”dependencies\”: {
\”@pinia/nuxt\”: \”^0.5.1\”,
\”arco-design-nuxt-module\”: \”^0.1.0\”,
\”blueimp-md5\”: \”^2.19.0\”,
\”clipboard\”: \”^2.0.11\”,
\”cos-js-sdk-v5\”: \”^1.8.1\”,
\”dayjs\”: \”^1.11.11\”,
\”nuxt\”: \”^3.12.2\”,
\”pinia\”: \”^2.1.7\”,
\”pinia-plugin-persistedstate\”: \”^3.2.1\”,
\”vue\”: \”^3.4.29\”,
\”vue-i18n\”: \”^9.13.1\”,
\”vue-router\”: \”^4.3.3\”,
\”vue-toast-notification\”: \”^3.1.2\”
},
\”devDependencies\”: {
\”@nuxt/eslint-config\”: \”^0.3.13\”,
\”@nuxtjs/i18n\”: \”^8.3.1\”,
\”postcss-aspect-ratio-mini\”: \”^1.1.0\”,
\”postcss-px-to-viewport\”: \”^1.1.1\”,
\”prettier\”: \”^3.3.2\”,
\”sass\”: \”^1.77.6\”,
\”sass-loader\”: \”^14.2.1\”
}
}

环境变量

环境变量定义了.local.env、.production.env、.development.env这里只单列一个

# 环境变量
VITE_MODE=development
# 接口基地址
VITE_BASE_URL=https://demo.cn

写在最后

扫码关注作者微信公众号fever code,获取一手技术分享

#以上关于帮公司搭了个Nuxt3项目框架的相关内容来源网络仅供参考,相关信息请以官方公告为准!

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/92913.html

(0)
CSDN's avatarCSDN
上一篇 2024年7月4日 下午4:33
下一篇 2024年7月4日 下午4:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注