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