无标题文章

BlogVue

创建新项目

npm create vite

基于Vue3、Vite4、TypeScript

#安装依赖包

npm i

类型声明

TypeScript类型检查和自动补全功能,以增加代码的可读性和可维护性

vite-env.d.ts文件

/// <reference types="vite/client" />

declare module '*.vue' {

   import  { DefineComponent } from 'vue'

   const component: DefineComponent<{}, {}, any>

   export default component

}

有了上述文件 TypeScript 就能够正确地识别和检查 Vue.js 组件的 props、事件和组件实例等类型信息。

配置解析路径

配置模块路径别名

安装@types/node作为开发者依赖关系它是 Node.js 官方发布的类型声明文件,包含了 Node.js 标准库和常用模块的类型声明

npm install @types/node --save-dev

#实现方法

import path from 'path'

export const getRootPath = () => {

   return path.resolve(process.cwd())

}

export  const getSrcPath = (srcName = 'src') => {

   return path.resolve(getRootPath(), srcName)

}

vite.config.ts文件配置模块路径别名

   resolve: {

       alias: {

           '@': getSrcPath(),

           '~': getRootPath()

       }

   },

解析路径

tsconfig.json文件中 在compilerOptions对象加入

   "baseUrl": "./",  //指定了解析非相对路径模块时使用的基本目录路径

   "paths": {  //将路径缩写映射为具体的路径

     "@/*": ["src/*"],

     "~/*": ["./*"]

   },

安装Tailwind CSS框架

安装 Tailwind 以及其它依赖项:

npm install -D tailwindcss@latest postcss@latest autoprefixer@latest

创建配置文件

npx tailwindcss init -p

tailwind.config.js

/** @type {import('tailwindcss').Config} */

export default {

  content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],  //制定优化性能,扫描文件提取样式

  darkMode: 'class',  //class手动  media跟随浏览器

  theme: {  //用于指定自定义主题

   extend: {},

  },

  plugins: [], //用于指定一些插件

}

引入Tailwind

自定义一个样式文件

@tailwind base;

@tailwind components;

@tailwind utilities;

在main.ts引入

安装NaiveUI 前端框架

npm i -D naive-ui

全局引入NaiveUI

在main.ts引入

import naive from 'naive-ui'

app.use(naive)

NaiveUI全局挂载组件

挂载

组件包括 对话框、消息框,方便使用

在App.vue中包裹。

示例:

<template>

   <n-config-provider :theme="appStore.theme==='dark' ? darkTheme : undefined" :locale="locale" class="h-full">

       <n-loading-bar-provider>

           <n-message-provider>

               <n-dialog-provider>

                   <n-notification-provider>

                       <NaiveProviderContent></NaiveProviderContent>

                       <router-view></router-view>

                   </n-notification-provider>

               </n-dialog-provider>

           </n-message-provider>

       </n-loading-bar-provider>

   </n-config-provider>

</template>

<script setup lang="ts">

import {useDialog, useLoadingBar, useMessage, useNotification} from "naive-ui";

import {defineComponent, h, ref} from "vue";

import {darkTheme, zhCN} from 'naive-ui'

import {useAppStore} from "@/store";

const appStore = useAppStore()

const locale = ref(zhCN)

//定义新组建 实现挂载

const NaiveProviderContent = defineComponent({

   setup() {

       window.$message = useMessage();

       window.$dialog = useDialog();

       window.$notification = useNotification()

       window.$loadingBar = useLoadingBar()

   },

   render() {

       return h('div')

   },

})

</script>

<style scoped>

</style>

在模板中定义了一个名为 NaiveProviderContent 的组件,并在其中使用了 useDialog、useLoadingBar、useMessage 和 useNotification 四个 hook。

这四个 hook 返回了 Dialog、LoadingBar、Message 和 Notification 组件的实例,可以通过将其挂载在全局的 window 对象上,在全局范围内使用。

同时修改了 NaiveUI 的用户的语言环境为中文

类型声明

创建一个类型声明文件。只要是.d.ts结尾文件都可以

声明全局变量 $message、$dialog、$notification 和 $loadingBar 的类型

在声明这些全局变量之后,就可以在项目中直接使用它们,而无需在每个需要使用它们的文件中都进行导入。

import {MessageApiInjection} from "naive-ui/es/message/src/MessageProvider";

import {DialogApiInjection} from "naive-ui/es/dialog/src/DialogProvider";

import {NotificationApiInjection} from "naive-ui/es/notification/src/NotificationProvider";

import {LoadingBarApiInjection} from "naive-ui/es/loading-bar/src/LoadingBarProvider";

/**

* ts 添加定义 识别

*/

declare global{

   interface Window{

       $message:MessageApiInjection,

       $dialog:DialogApiInjection,

       $notification:NotificationApiInjection

       $loadingBar:LoadingBarApiInjection

   }

}

安装状态管理Pinia及持久化插件pinia-plugin-persistedstate

npm i pinia

npm i pinia-plugin-persistedstate

引入pinia及插件

在main.ts

import {createPinia} from "pinia";

import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()

pinia.use(piniaPluginPersistedstate)

app.use(pinia)

创建相应store

export const useAppStore = defineStore('app', {

   persist: true, //使用持久化插件

   state: () => {   //是一个函数,返回一个对象,用于存储状态

       return {

           collapsed: true,

           theme: 'dark',

       }

   },

   getters: {},  //用于存储计算属性

   actions: {  //用于封装业务逻辑

       switchCollapsed() {

           this.collapsed = !this.collapsed

       },

       switchTheme() {

           if (this.theme === 'light') this.theme = 'dark'

           else this.theme = 'light'

       }

   },

})

安装axios

npm i axios

请求拦截器

//设置请求拦截器

axios.interceptors.request.use((config) => {

   // 携带上token

   let token = userStore.token

   token && (config.headers.Authorization = token)

   return config

}, error => {

   return Promise.reject(error)

})

响应拦截器

//响应拦截器

axios.interceptors.response.use(

   (response) => {

       //未登录

       if(response.data.code===405){

           router.push({

               path:'/auth'

           })

       }

       if (response.data.code !== 0) {

           window.$message.error(response.data.msg)

           return Promise.reject(response.data.msg); // 返回拒绝状态的 Promise 对象,将错误信息传递给后续的 Promise 处理函数

       }

       window.$message.success(response.data.msg)

       return response;

   }, (error => {

       // 请求错误时做些事

       let res = "";

       if (error.request) {

           res = error.request;

       } else if (error.response) {

           res = error.response;

       }

       if (res) {

           //@ts-ignore

           const data = JSON.parse(res.response)

           window.$message.error(data.msg)

           return Promise.reject(data.msg); // 返回拒绝状态的 Promise 对象,将错误信息传递给后续的 Promise 处理函数

       } else {

           window.$message.error(`链接服务器失败`)

           return Promise.reject(error); // 返回拒绝状态的 Promise 对象,将错误信息传递给后续的 Promise 处理函数

       }

   })

)

安装vue-router

npm i vue-router

创建router文件

import {createRouter, createWebHistory} from "vue-router";

const routes = [

       path: '/',

       name: 'redis',

       component: () => import('@/layout/admin/index.vue'),

]

const routerOptions = {

   history: createWebHistory('/redis'),

   routes: routes,

}

const router = createRouter(routerOptions)

export default router

在main.ts文件引入

import router from "@/router";

app.use(router)

安装 jsencrypt

对数据进行加密解密

npm i jsencrypt

BlogAPI

数据库设计

-- 用户表

CREATETABLE`blog_user`(

`id`INT(11)NOTNULLAUTO_INCREMENTCOMMENT'主键',

`username`VARCHAR(50)NOTNULLCOMMENT'用户名',

`password`VARCHAR(255)NOTNULLCOMMENT'密码',

`email`VARCHAR(50)NOTNULLCOMMENT'电子邮件',

`role`VARCHAR(20)NOTNULLCOMMENT'角色',

`avatar`VARCHAR(255)NULLCOMMENT'头像地址',

`created_at`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',

PRIMARYKEY(`id`)

)ENGINE=INNODBDEFAULTCHARSET=utf8COMMENT='用户表';

-- 文章表

CREATETABLE`blog_post`(

`id`INT(11)NOTNULLAUTO_INCREMENTCOMMENT'主键',

`title`VARCHAR(255)NOTNULLCOMMENT'文章标题',

`content`TEXTNOTNULLCOMMENT'文章内容',

`user_id`INT(11)NOTNULLCOMMENT'作者id',

`category_id`INT(11)NOTNULLCOMMENT'分类id',

`created_at`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',

PRIMARYKEY(`id`),

FOREIGNKEY(`user_id`)REFERENCES`blog_user`(`id`),

FOREIGNKEY(`category_id`)REFERENCES`blog_category`(`id`)

)ENGINE=INNODBDEFAULTCHARSET=utf8COMMENT='文章表';

-- 分类表

CREATETABLE`blog_category`(

`id`INT(11)NOTNULLAUTO_INCREMENTCOMMENT'主键',

`name`VARCHAR(100)NOTNULLCOMMENT'分类名称',

`created_at`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',

PRIMARYKEY(`id`)

)ENGINE=INNODBDEFAULTCHARSET=utf8COMMENT='分类表';

-- 评论表

CREATETABLE`blog_comment`(

`id`INT(11)NOTNULLAUTO_INCREMENTCOMMENT'主键',

`content`TEXTNOTNULLCOMMENT'评论内容',

`user_id`INT(11)NOTNULLCOMMENT'评论者id',

`post_id`INT(11)NOTNULLCOMMENT'文章id',

`parent_id`INT(11)DEFAULTNULLCOMMENT'父级评论id,表示当前评论是对哪个评论的回复。如果不是回复,则为null',

`reply_to`INT(11)DEFAULTNULLCOMMENT'回复到哪条评论,表示该回复是针对哪个用户的回复。如果不是回复,为null',

`created_at`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',

PRIMARYKEY(`id`),

FOREIGNKEY(`user_id`)REFERENCES`blog_user`(`id`),

FOREIGNKEY(`post_id`)REFERENCES`blog_post`(`id`),

FOREIGNKEY(`parent_id`)REFERENCES`blog_comment`(`id`),

FOREIGNKEY(`reply_to`)REFERENCES`blog_user`(`id`)

)ENGINE=INNODBDEFAULTCHARSET=utf8COMMENT='评论表';

CREATETABLE`blog_reply`(

`id`INT(11)NOTNULLAUTO_INCREMENTCOMMENT'主键',

`content`TEXTNOTNULLCOMMENT'回复内容',

`user_id`INT(11)NOTNULLCOMMENT'回复者id',

`comment_id`INT(11)NOTNULLCOMMENT'评论id,表示回复是哪条评论的回复',

`parent_id`INT(11)DEFAULTNULLCOMMENT'父级回复id,表示当前回复是对哪个回复的回复。如果不是回复,则为null',

`reply_to`INT(11)DEFAULTNULLCOMMENT'回复到哪条评论,表示该回复是针对哪个用户的回复。如果不是回复,为null',

`created_at`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',

PRIMARYKEY(`id`),

FOREIGNKEY(`user_id`)REFERENCES`blog_user`(`id`),

FOREIGNKEY(`comment_id`)REFERENCES`blog_comment`(`id`),

FOREIGNKEY(`parent_id`)REFERENCES`blog_reply`(`id`),

FOREIGNKEY(`reply_to`)REFERENCES`blog_user`(`id`)

)ENGINE=INNODBDEFAULTCHARSET=utf8COMMENT='回复表';

创建新项目

npm create vite

基于Vue3、Vite4、TypeScript

#安装依赖包

npm i

类型声明

TypeScript类型检查和自动补全功能,以增加代码的可读性和可维护性

vite-env.d.ts文件

/// <reference types="vite/client" />

declare module '*.vue' {

   import  { DefineComponent } from 'vue'

   const component: DefineComponent<{}, {}, any>

   export default component

}

有了上述文件 TypeScript 就能够正确地识别和检查 Vue.js 组件的 props、事件和组件实例等类型信息。

配置解析路径

配置模块路径别名

安装@types/node作为开发者依赖关系它是 Node.js 官方发布的类型声明文件,包含了 Node.js 标准库和常用模块的类型声明

npm install @types/node --save-dev

#实现方法

import path from 'path'

export const getRootPath = () => {

   return path.resolve(process.cwd())

}

export  const getSrcPath = (srcName = 'src') => {

   return path.resolve(getRootPath(), srcName)

}

vite.config.ts文件配置模块路径别名

   resolve: {

       alias: {

           '@': getSrcPath(),

           '~': getRootPath()

       }

   },

解析路径

tsconfig.json文件中 在compilerOptions对象加入

   "baseUrl": "./",  //指定了解析非相对路径模块时使用的基本目录路径

   "paths": {  //将路径缩写映射为具体的路径

     "@/*": ["src/*"],

     "~/*": ["./*"]

   },

安装Tailwind CSS框架

安装 Tailwind 以及其它依赖项:

npm install -D tailwindcss@latest postcss@latest autoprefixer@latest

创建配置文件

npx tailwindcss init -p

tailwind.config.js

/** @type {import('tailwindcss').Config} */

export default {

  content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],  //制定优化性能,扫描文件提取样式

  darkMode: 'class',  //class手动  media跟随浏览器

  theme: {  //用于指定自定义主题

   extend: {},

  },

  plugins: [], //用于指定一些插件

}

引入Tailwind

自定义一个样式文件

@tailwind base;

@tailwind components;

@tailwind utilities;

在main.ts引入

安装NaiveUI 前端框架

npm i -D naive-ui

全局引入NaiveUI

在main.ts引入

import naive from 'naive-ui'

app.use(naive)

NaiveUI全局挂载组件

挂载

组件包括 对话框、消息框,方便使用

在App.vue中包裹。

示例:

<template>

   <n-config-provider :theme="appStore.theme==='dark' ? darkTheme : undefined" :locale="locale" class="h-full">

       <n-loading-bar-provider>

           <n-message-provider>

               <n-dialog-provider>

                   <n-notification-provider>

                       <NaiveProviderContent></NaiveProviderContent>

                       <router-view></router-view>

                   </n-notification-provider>

               </n-dialog-provider>

           </n-message-provider>

       </n-loading-bar-provider>

   </n-config-provider>

</template>

<script setup lang="ts">

import {useDialog, useLoadingBar, useMessage, useNotification} from "naive-ui";

import {defineComponent, h, ref} from "vue";

import {darkTheme, zhCN} from 'naive-ui'

import {useAppStore} from "@/store";

const appStore = useAppStore()

const locale = ref(zhCN)

//定义新组建 实现挂载

const NaiveProviderContent = defineComponent({

   setup() {

       window.$message = useMessage();

       window.$dialog = useDialog();

       window.$notification = useNotification()

       window.$loadingBar = useLoadingBar()

   },

   render() {

       return h('div')

   },

})

</script>

<style scoped>

</style>

在模板中定义了一个名为 NaiveProviderContent 的组件,并在其中使用了 useDialog、useLoadingBar、useMessage 和 useNotification 四个 hook。

这四个 hook 返回了 Dialog、LoadingBar、Message 和 Notification 组件的实例,可以通过将其挂载在全局的 window 对象上,在全局范围内使用。

同时修改了 NaiveUI 的用户的语言环境为中文

类型声明

创建一个类型声明文件。只要是.d.ts结尾文件都可以

声明全局变量 $message、$dialog、$notification 和 $loadingBar 的类型

在声明这些全局变量之后,就可以在项目中直接使用它们,而无需在每个需要使用它们的文件中都进行导入。

import {MessageApiInjection} from "naive-ui/es/message/src/MessageProvider";

import {DialogApiInjection} from "naive-ui/es/dialog/src/DialogProvider";

import {NotificationApiInjection} from "naive-ui/es/notification/src/NotificationProvider";

import {LoadingBarApiInjection} from "naive-ui/es/loading-bar/src/LoadingBarProvider";

/**

* ts 添加定义 识别

*/

declare global{

   interface Window{

       $message:MessageApiInjection,

       $dialog:DialogApiInjection,

       $notification:NotificationApiInjection

       $loadingBar:LoadingBarApiInjection

   }

}

安装状态管理Pinia及持久化插件pinia-plugin-persistedstate

npm i pinia

npm i pinia-plugin-persistedstate

引入pinia及插件

在main.ts

import {createPinia} from "pinia";

import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()

pinia.use(piniaPluginPersistedstate)

app.use(pinia)

创建相应store

export const useAppStore = defineStore('app', {

   persist: true, //使用持久化插件

   state: () => {   //是一个函数,返回一个对象,用于存储状态

       return {

           collapsed: true,

           theme: 'dark',

       }

   },

   getters: {},  //用于存储计算属性

   actions: {  //用于封装业务逻辑

       switchCollapsed() {

           this.collapsed = !this.collapsed

       },

       switchTheme() {

           if (this.theme === 'light') this.theme = 'dark'

           else this.theme = 'light'

       }

   },

})

安装axios

npm i axios

请求拦截器

//设置请求拦截器

axios.interceptors.request.use((config) => {

   // 携带上token

   let token = userStore.token

   token && (config.headers.Authorization = token)

   return config

}, error => {

   return Promise.reject(error)

})

响应拦截器

//响应拦截器

axios.interceptors.response.use(

   (response) => {

       //未登录

       if(response.data.code===405){

           router.push({

               path:'/auth'

           })

       }

       if (response.data.code !== 0) {

           window.$message.error(response.data.msg)

           return Promise.reject(response.data.msg); // 返回拒绝状态的 Promise 对象,将错误信息传递给后续的 Promise 处理函数

       }

       window.$message.success(response.data.msg)

       return response;

   }, (error => {

       // 请求错误时做些事

       let res = "";

       if (error.request) {

           res = error.request;

       } else if (error.response) {

           res = error.response;

       }

       if (res) {

           //@ts-ignore

           const data = JSON.parse(res.response)

           window.$message.error(data.msg)

           return Promise.reject(data.msg); // 返回拒绝状态的 Promise 对象,将错误信息传递给后续的 Promise 处理函数

       } else {

           window.$message.error(`链接服务器失败`)

           return Promise.reject(error); // 返回拒绝状态的 Promise 对象,将错误信息传递给后续的 Promise 处理函数

       }

   })

)

安装vue-router

npm i vue-router

创建router文件

import {createRouter, createWebHistory} from "vue-router";

const routes = [

       path: '/',

       name: 'redis',

       component: () => import('@/layout/admin/index.vue'),

]

const routerOptions = {

   history: createWebHistory('/redis'),

   routes: routes,

}

const router = createRouter(routerOptions)

export default router

在main.ts文件引入

import router from "@/router";

app.use(router)

安装 jsencrypt

对数据进行加密解密

npm i jsencrypt

BlogAPI

数据库设计

-- 用户表

CREATETABLE`blog_user`(

`id`INT(11)NOTNULLAUTO_INCREMENTCOMMENT'主键',

`username`VARCHAR(50)NOTNULLCOMMENT'用户名',

`password`VARCHAR(255)NOTNULLCOMMENT'密码',

`email`VARCHAR(50)NOTNULLCOMMENT'电子邮件',

`role`VARCHAR(20)NOTNULLCOMMENT'角色',

`avatar`VARCHAR(255)NULLCOMMENT'头像地址',

`created_at`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',

PRIMARYKEY(`id`)

)ENGINE=INNODBDEFAULTCHARSET=utf8COMMENT='用户表';

-- 文章表

CREATETABLE`blog_post`(

`id`INT(11)NOTNULLAUTO_INCREMENTCOMMENT'主键',

`title`VARCHAR(255)NOTNULLCOMMENT'文章标题',

`content`TEXTNOTNULLCOMMENT'文章内容',

`user_id`INT(11)NOTNULLCOMMENT'作者id',

`category_id`INT(11)NOTNULLCOMMENT'分类id',

`created_at`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',

PRIMARYKEY(`id`),

FOREIGNKEY(`user_id`)REFERENCES`blog_user`(`id`),

FOREIGNKEY(`category_id`)REFERENCES`blog_category`(`id`)

)ENGINE=INNODBDEFAULTCHARSET=utf8COMMENT='文章表';

-- 分类表

CREATETABLE`blog_category`(

`id`INT(11)NOTNULLAUTO_INCREMENTCOMMENT'主键',

`name`VARCHAR(100)NOTNULLCOMMENT'分类名称',

`created_at`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',

PRIMARYKEY(`id`)

)ENGINE=INNODBDEFAULTCHARSET=utf8COMMENT='分类表';

-- 评论表

CREATETABLE`blog_comment`(

`id`INT(11)NOTNULLAUTO_INCREMENTCOMMENT'主键',

`content`TEXTNOTNULLCOMMENT'评论内容',

`user_id`INT(11)NOTNULLCOMMENT'评论者id',

`post_id`INT(11)NOTNULLCOMMENT'文章id',

`parent_id`INT(11)DEFAULTNULLCOMMENT'父级评论id,表示当前评论是对哪个评论的回复。如果不是回复,则为null',

`reply_to`INT(11)DEFAULTNULLCOMMENT'回复到哪条评论,表示该回复是针对哪个用户的回复。如果不是回复,为null',

`created_at`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',

PRIMARYKEY(`id`),

FOREIGNKEY(`user_id`)REFERENCES`blog_user`(`id`),

FOREIGNKEY(`post_id`)REFERENCES`blog_post`(`id`),

FOREIGNKEY(`parent_id`)REFERENCES`blog_comment`(`id`),

FOREIGNKEY(`reply_to`)REFERENCES`blog_user`(`id`)

)ENGINE=INNODBDEFAULTCHARSET=utf8COMMENT='评论表';

CREATETABLE`blog_reply`(

`id`INT(11)NOTNULLAUTO_INCREMENTCOMMENT'主键',

`content`TEXTNOTNULLCOMMENT'回复内容',

`user_id`INT(11)NOTNULLCOMMENT'回复者id',

`comment_id`INT(11)NOTNULLCOMMENT'评论id,表示回复是哪条评论的回复',

`parent_id`INT(11)DEFAULTNULLCOMMENT'父级回复id,表示当前回复是对哪个回复的回复。如果不是回复,则为null',

`reply_to`INT(11)DEFAULTNULLCOMMENT'回复到哪条评论,表示该回复是针对哪个用户的回复。如果不是回复,为null',

`created_at`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',

PRIMARYKEY(`id`),

FOREIGNKEY(`user_id`)REFERENCES`blog_user`(`id`),

FOREIGNKEY(`comment_id`)REFERENCES`blog_comment`(`id`),

FOREIGNKEY(`parent_id`)REFERENCES`blog_reply`(`id`),

FOREIGNKEY(`reply_to`)REFERENCES`blog_user`(`id`)

)ENGINE=INNODBDEFAULTCHARSET=utf8COMMENT='回复表';

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容