日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > vue >内容正文

vue

一个nuxt(vue)+mongoose全栈项目聊聊我粗浅的项目架构

發布時間:2023/12/20 vue 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一个nuxt(vue)+mongoose全栈项目聊聊我粗浅的项目架构 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這是一篇求職文章 年齡21 坐標成都 找一份vue.js移動端H5工作
一份沒有任何包裝純真實的簡歷 簡歷戳這

求職文章一共有兩篇 另外一篇請點擊一個基于Vue+TypeScript的[移動端]Vue UI

項目簡介

名字

JsonMaker

作用

添加api和屬性,用于制造JSON

地址

??github

技術棧

前端

pug scss vue vue-router vuex axios nuxt element-ui 復制代碼

后端

node express mongoose mongodb jsonwebtoken 復制代碼

項目目錄

前端

assets 資源文件和js邏輯存放處
components 組件目錄 (因為引用了element-ui 項目不大 沒單獨構造組件)
layouts 布局目錄(此項目沒用上)
middleware 中間件目錄
pages 頁面目錄
plugins 插件目錄
static 靜態文件目錄
store vuex狀態數目錄

后端

actions js事件目錄
config 配置目錄
lib js模版目錄
middleware express中間件目錄
model mongoose.model 目錄
plugins 插件目錄
schmea mongoose.Schema 目錄
app.js 主app
router.js 路由

圖片

架構思路

前端

首先我們大致了解一下我們這個nuxt.config.js中的配置,之后會一個一個講解

nuxt.config.js

nuxt.config.js 配置

module.exports = {// htmlhead: {title: 'JsonMaker一個JSON制造器',meta: [{ charset: 'utf-8' },{ name: 'author', content: 'Qymh' },{ name: 'keywords', content: 'Json,JSON,JsonMaker' },{ name: 'viewport', content: 'width=device-width, initial-scale=1' },{hid: 'description',name: 'description',content:'JsonMaker用戶制造JSON,一個全棧項目,前端基于Nuxt Vuex Pug Scss Axios element-ui 后端基于 Node Express mongoose mongodb jsonwebtoken'}],link: [{rel: 'icon',type: 'image/x-icon',href: 'https://nav.qymh.org.cn/static/images/q.ico'}]},// 全局csscss: [// reset css'~/assets/style/normalize.css',// common css'~/assets/style/common.css',// element-ui css'element-ui/lib/theme-chalk/index.css'],// 加載顏色loading: { color: '#409EFF' },// 插件plugins: [// element-ui{ src: '~/plugins/element-ui' },// widget{ src: '~/plugins/widget' },// 百度統計{ src: '~/plugins/baiduStatistics', ssr: false },// 百度站長平臺{ src: '~/plugins/baiduStation', ssr: false }],// webpack配置build: {extend(config, { isDev, isClient }) {// eslintif (isDev && isClient) {config.module.rules.push({enforce: 'pre',test: /\.(js|vue)$/,loader: 'eslint-loader',exclude: /(node_modules)/})}config.module.rules.push(// pug{test: /\.pug$/,loader: 'pug-plain-loader'},// scss{test: /\.scss$/,use: ['vue-style-loader','css-loader','sass-loader','postcss-loader']})},// postcss配置postcss: [require('autoprefixer')()],// 公用庫vendor: ['axios', 'element-ui']},router: {// 認證中間件middleware: 'authenticate'} } 復制代碼

解析nuxt.config.js中的插件

插件中我引用了4個

  • 1 element-ui 插件
  • 2 widget 這里面包裝了cookie的操作方法
    通過Vue.use()引入插件,直接通過vue環境下的this調用
    這個位置有一個坑,服務器端是沒有document這個屬性的,所以沒法獲取通過這種方式獲取cookie
    所以我們還需要構造一個從req獲取token的函數,我寫在了assets/lib/utils下
    cookie是從req.headers.cookie中讀取的
  • 3 引入百度統計
  • 4 引入百度站長平臺

解析 nuxt.config.js 中的 middleware

middleware目中就一個文件,這個文件包含了驗證用戶登陸和自動登陸的功能
這個位置也有一個坑,與非nuxt項目不同,我們平常的vue項目這個操作
是在router.beforeEach全局鉤子里進行驗證,而且在nuxt中你不光要驗證客戶端也要驗證服務器端
大體思路就幾點

  • 1 在需要登陸的頁面設置meta: { auth: true },不需要的頁面設置meta: { notAuth: true }
  • 2 當處于需要登陸的頁面如果有token直接退出,沒有則分兩部獲取token,一個客戶端,一個服務器端,最后如果token存在
    則執行全局系統參數的api調用然后寫入vuex,如果不存在則返回登陸界面
  • 3 在某些notAuth auth 都不存在時,檢查存放的userName屬性存在不,存在就跳到用戶首頁,不存在則跳到登陸界面

全局參數配置

每個人對這個全局配置理解不一樣,看習慣,有人喜歡把很多配置都往全局放,比如vue-router的配置,我覺得沒必要
我一般在全局配置中放一些配置沒那么復雜的,諸如項目名字啊還有各類插件的配置,這個項目不大,所以全局配置也不太多 assets/lib/appconfig.js

const isDev = process.env.NODE_ENV === 'development'// app export const APPCONFIG = {isDebug: true }// cookie 設置 export const COOKIECONFIG = {expiresDay: 7 }// server 設置 export const SERVERCONFIG = {domain: isDev ? 'http://127.0.0.1:5766' : 'https://api.qymh.org.cn',timeout: 10000 }復制代碼

全局還有一個配置就是api接口的配置,我喜歡把api接口放在一個文件里面,然后引入,這個項目不大,一共15個接口 assets/lib/api

// 獲取全局屬性 export const system = '/api/system'// 注冊 export const register = '/api/register' // 登陸 export const login = '/api/login'// 添加api export const addApi = '/api/addApi' // 獲取api export const getApi = '/api/getApi' // 刪除api export const deleteApi = '/api/deleteApi' // 修改api export const putApi = '/api/putApi'// 添加屬性 export const addProperty = '/api/addProperty' // 獲取屬性 export const getProperties = '/api/getProperties' // 刪除屬性 export const deleteProperty = '/api/deleteProperty' // 修改屬性 export const putProperty = '/api/putProperty'// 添加集合 export const addCollections = '/api/addCollections' // 獲取集合 export const getCollections = '/api/getCollections' // 刪除集合 export const deleteCollections = '/api/deleteCollections' // 修改集合 export const putCollections = '/api/putCollections'復制代碼

ajax函數請求架構

nuxt.config.js聊完了,我們來聊聊前后端分離的一個大點,就是請求,我的習慣的一層一層從底部往上抽離

  • 1 第一步,封裝攔截器
    攔截器就幾個部分,一個axios基礎參數配置,一個請求request攔截,一個響應response攔截
    一般在請求攔截就是構造參數,比如參數加密 請求頭的發送 之類的,這個項目暫時還沒做前端參數加密嗎,同時我也會在請求輸出log日志
    響應攔截也是一樣的,輸出接收到的參數日志并處理出錯的情況,我們來看看代碼
    assets/lib/axios.js
import axios from 'axios' import Vue from 'vue' import { SERVERCONFIG, APPCONFIG } from './appconfig'const isClient = process.client const vm = new Vue()const ax = axios.create({baseURL: SERVERCONFIG.domain,timeout: SERVERCONFIG.timeout })// 請求攔截 ax.interceptors.request.use(config => {const token = isClient ? vm.$cookie.get('token') : process.TOKENif (token) {config.headers.common['authenticate'] = token}const { data } = configif (APPCONFIG.isDebug) {console.log(`serverApi:${config.baseURL}${config.url}`)if (Object.keys(data).length > 0) {console.log(`request data ${JSON.stringify(data)}`)}}return config })// 響應攔截 ax.interceptors.response.use(response => {const { status, data } = responseif (APPCONFIG.isDebug) {if (status >= 200 && status <= 300) {console.log('---response data ---')console.log(data)if (data.error_code && isClient) {vm.$message({type: 'error',message: data.error_message,duration: 1500})}} else {console.log('--- error ---')console.log(data)if (isClient) {vm.$message({type: 'error',message:status === 0 ? '網絡鏈接異常' : `網絡異常,錯誤代碼:${status}`,duration: 1500})}}}return {data: response.data} })export default ax復制代碼
  • 2 第二部構造http請求底層
    底層分裝了4個方法,get post put delete, 增刪改查,用promise實現,一層一層往上套,我們來看看代碼

assets/lib/http.js

import ax from './axios' import Vue from 'vue'export default {/*** ajax公用函數* @param {String} api api接口* @param {Object} data 數據* @param {Boolean} isLoading 是否需要加載*/ajax(method, api, data, isLoading = false) {return new Promise((resolve, reject) => {let vm = ''let loading = ''if (isLoading) {vm = new Vue()loading = vm.$loading()}ax({method,url: api,data}).then(res => {let { data } = resif (data.error_code) {isLoading && loading.close()reject(data)} else {isLoading && loading.close()resolve(data)}})})},/*** post函數* @param {String} api api接口* @param {Object} data 數據* @param {Boolean} isLoading 是否需要加載*/post(api, data, isLoading = false) {return new Promise((resolve, reject) => {this.ajax('POST', api, data, isLoading).then(data => {resolve(data)}).catch(err => {reject(err)})})},/*** delete函數* @param {String} api api接口* @param {Object} data 數據* @param {Boolean} isLoading 是否需要加載*/delete(api, data, isLoading = false) {return new Promise((resolve, reject) => {this.ajax('DELETE', api, data, isLoading).then(data => {resolve(data)}).catch(err => {reject(err)})})},/*** put函數* @param {String} api api接口* @param {Object} data 數據* @param {Boolean} isLoading 是否需要加載*/put(api, data, isLoading = false) {return new Promise((resolve, reject) => {this.ajax('PUT', api, data, isLoading).then(data => {resolve(data)}).catch(err => {reject(err)})})} }復制代碼
  • 3 第三部分就是事件的邏輯代碼,我放在了assets/actions里面,同樣用promise實現,一步一步往上套,通過調用底層封裝的4個方法,調用封裝的全局api參數,這里舉一個關于api首頁獲取的操作事件的列子
    assets/actions/api.js
import http from '../lib/http' import * as api from '../lib/api'export default {/*** 獲取api*/getApi(userName) {return new Promise((resolve, reject) => {http.post(api.getApi, { userName }).then(data => {resolve(data)}).catch(err => {reject(err)})})}復制代碼
  • 4 其實一般到第三步,直接在vue中就可以引用 actions里面封裝好的事件了,但這個項目還多了一層,是用vuex再次封了一層
    這里仍然舉獲取api并操作vuex的列子,省略掉了非事件的代碼
import api from '~/assets/actions/api' import Vue from 'vue' const vm = new Vue()const actions = {// 獲取apiasync getApi({ commit }, { userName, redirect }) {await api.getApi(userName).then(arr => {commit('_getApi', arr)}).catch(() => {redirect({path: '/login',query: {errorMessage: '用戶不存在,請重新登陸'}})})}復制代碼
  • 5 下面就是在vue中引入actions就可以用了,接下來我們聊聊vuex的規范性

vuex的架構

  • 1 接口暴漏
    vuex中有四個屬性,state getters mutations actions
    按我的架構思路,我永遠暴漏在vue中可以使用的僅有兩個,一個getters,一個actions
    為什么呢?因為state改變后值不會在dom中刷新,mutations無法異步

  • 2 命名
    按官方建議要有一個mutations-type專門用于存放突變事件名字,我覺得沒必要,太麻煩了
    按第一點所說的,未暴漏的命名我會直接在前面加一個下劃線,就像我上面的代碼顯示的那樣

  • 3 事件和值的改變
    從名字上來講,actions表事件,mutations表突變,換句話來說,我執行事件邏輯,比如接口請求,我會在actions里面執行, 而改變vuex狀態樹的值,我會在mutations里面執行

  • 4 命名空間限定

    一定要在每個模塊上加入namespaced: true,一個是思路更清晰,第二個避免重復命名

后端

這個項目是我第二次用express寫后端,架構思路感覺自己還不太成熟,寫完之后發現有很多地方沒對.忙著找工作,時間也來不及了,之后改改

先來看看app.js

app.js

app.js干了幾件事

  • 1 引入mongoose并連接mongodb
  • 2 設置跨域CORS
  • 3 引入中間件和路由

全局參數

node后端也有全局參數,主要包含了錯誤代碼的集合還有一些常用的配置

config/nodeconfig.js

// token設置 exports.token = {secret: 'Qymh',expires: '7 days' }// 錯誤code exports.code = {// 用戶不存在noUser: 10001,// 密碼錯誤wrongPassword: 10002,// token過期outDateToken: 10003,// 檢驗不符合規則notValidate: 10004,// 已存在的數據existData: 10005,// 未知錯誤unknown: 100099,// 未知錯誤文字unknownText: '未知錯誤,請重新登陸試試' }// session exports.session = {secret: 'Qymh',maxAge: 10000 }復制代碼

數據存儲架構思路

  • 1 第一步 構建Schema

Schema也是mongoose需要第一個構建的,項目中引用了很多官方提供的驗證接口,我將Schema的配置放在了config/schema中,我們來看一下用戶的Schema是什么樣的

schema/user.js

const mongoose = require('mongoose') const Schema = mongoose.Schema const ApiSchema = require('./api') const config = require('../config/schema/user').USERSCHEMACONFIGconst UserSchema = new Schema({account: config.account,password: config.password,userName: config.userName,token: config.token,api: [ApiSchema]},config.options )module.exports = UserSchema復制代碼

config/schema/user.js

exports.USERSCHEMACONFIG = {// 帳號account: {type: String || Number,index: [true, '帳號已經存在'],unique: [true, '帳號已經存在'],required: [true, '帳號不能為空'],minlength: [5, '帳號長度需要大于等于5'],maxlength: [18, '帳號長度需要小于等于18'],trim: true},// 密碼password: {type: String || Number,required: [true, '密碼不能為空'],minlength: [8, '密碼長度需要大于等于8'],maxlength: [18, '密碼長度需要小于等于18'],trim: true},// 名字userName: {type: String || Number,index: [true, '用戶名已經存在'],unique: [true, '用戶名已經存在'],required: [true, '用戶名不能為空'],minlength: [2, '姓名長度需要大于等于2'],maxlength: [8, '姓名長度需要小于等于8'],trim: true},// tokentoken: {type: String},// schema配置options: {versionKey: 'v1.0',timestamps: {createdAt: 'createdAt',updatedAt: 'updatedAt'}} }復制代碼
  • 2 第二步構建model

model放在model文件夾中,接收傳來的Schema,然后傳出Model,我們來看看用戶的model

model/user.js

const mongoose = require('mongoose') const UserSchema = require('../schema/user')const UserModel = mongoose.model('UserModel', UserSchema)module.exports = UserModel復制代碼
  • 3 第三步構建數據存儲lib

這個存儲其實是為了actions文件服務的,actions接受路由事件,而lib則負責儲存,包含了注冊和登陸功能,然后在這個lib操作里面,我將對最后獲得數據的處理進行封裝,封裝到了plugins目錄,里面就包括了,對用戶的token處理,對用于注冊失敗成功和登陸失敗成功的回調參數處理,我們來看看用戶的lib

lib/user.js

const UserModel = require('../model/user') const UserPlugin = require('../plugins/user')/*** 注冊* @param {String | Number} account 帳號* @param {String | Number} password 密碼* @param {String | Number} userName 名字*/ exports.register = (account, password, userName) => {return new Promise((resolve, reject) => {const User = new UserModel({account,password,userName})User.save((err, doc) => {if (err) {err = UserPlugin.dealRegisterError(err)reject(err)}resolve(doc)})}) }/*** 登陸* @param {String | Number} account 帳號* @param {String | Number} password 密碼*/ exports.login = (account, password) => {return new Promise((resolve, reject) => {UserModel.findOne({ account }).exec((err, user) => {err = UserPlugin.dealLoginError(user, password)if (err.error_code) {reject(err)} else {user = UserPlugin.dealLogin(user)resolve(user)}})}) }復制代碼
  • 4 第四步 構建路由actions

actions目錄用于處理路由的接收,然后引入lib進行數據的存儲,我們來看看用戶的actions

actions/user.js

const user = require('../lib/user')// 注冊 exports.register = async (req, res) => {const data = req.bodyconst { account, password, userName } = dataawait user.register(account, password, userName).then(doc => {res.json(doc)}).catch(err => {res.json(err)}) }// 登陸 exports.login = async (req, res) => {const data = req.bodyconst { account, password } = dataawait user.login(account, password).then(doc => {res.json(doc)}).catch(err => {res.json(err)}) }復制代碼
  • 5 構建路由

router.js就是所有api的掛載處,最后在app.js里面引用即可掛載,這個項目不大,一共提供了16個api

數據儲存這5步就基本結束了,下面我們聊聊express的中間件

middleware中間件

這里的中間件主要就驗證token過期沒,過期了則直接返回,然后不進行任何操作

middleware/authenticate.js

const userPlugin = require('../plugins/user') const nodeconfig = require('../config/nodeconfig')// 驗證token是否過期 exports.authenticate = (req, res, next) => {const token = req.headers.authenticateres.locals.token = tokenif (token) {const code = userPlugin.verifyToken(token)if (code === nodeconfig.code.outDateToken) {const err = {error_code: code,error_message: 'token過期'}res.json(err)}}next() }復制代碼

我的出錯

后端的架構就上面這些了,在這次的后端架構中我出了一個錯誤,你可以看見我上面的userSchema是把apiSchema放在里面了,然后 apiSchema里面我有包含了兩個schema,一個propertSchema,一個collectionsSchema
為什么我會這么做呢,因為剛開始寫的時候想的是如果要從一個數據庫去搜索一個信息,這個信息是屬于用戶的,有兩個方法

  • 1 直接構造這個數據庫的model然后存儲,存儲中帶一個userId指向當前這個信息所屬的用戶
  • 2 將這個數據放在userModel用戶model里,查找的時候先查找當前用于然后再讀取這個信息

最后我選擇了第二個....因為我想的是如果數據10w條,用戶只有100個,去找100個總比找10w個好,我這么選擇帶來的幾個問題

  • 1 mongoose儲存的時候如果對象里面嵌套過多你想儲存是沒有api接口提供的.我看了幾遍文檔,只能通過$set $push 去存儲對象的最多第二屬性 比如下面的對象,是沒有直接的api提供去修改collections的值的,需要用其他的方法繞一圈
[{userName: 'Qymh',id: 'xxxxx',api: [{id: 'xxxx',apiName: 'test',collections:[{id: 'xxxx',age: 21,sex: man}]}]}] 復制代碼
  • 2 查找的時候挺麻煩的,比如我要查找到collections,我需要提供兩個參數,一個用戶的id先找到用戶,再一個就是api的id再找到api最后再去提取collections,如果選擇第一種只需要用戶id就行了

所以我感覺自己在這一步上出錯了

項目的掛載

  • 1 最后項目的掛載是通過pm2掛載的

  • 2 項目的node后端和前端都引用了ssl證書

現在項目已經掛到線上了但我的服務器太差,之前阿里云買的9.9元的學生機現在續費了只能拿來測試玩玩

之后要做的

這個項目斷斷續續寫了20來天,很多功能沒有完善,之后我會做的

  • 1 前端傳入參數加密
  • 2 api屬性加入類型判斷前端傳入后端,后端schema添加,比如mongoose的幾個類型string boolean schema.types.mixed 等
  • 3 后端密碼加鹽
  • 4 更過的功能點,比如不止制造json,制造xml,引入echarts加入數據可視化之類的

總結

以上是生活随笔為你收集整理的一个nuxt(vue)+mongoose全栈项目聊聊我粗浅的项目架构的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。