feat:整理博客目录结构

This commit is contained in:
2026-06-05 15:53:29 +08:00
parent a5976c9783
commit a64f20e84f
7 changed files with 539 additions and 9 deletions

View File

@@ -5,8 +5,9 @@ export const routers = [
{
text: '🌿 Vue',
items: [
{ text: 'Vue3-Common', link: '/Web/Vue/Vue3-Common' },
{ text: 'Vue3架构原理', link: '/Web/Vue/Vue3-Architecture' },
{ text: 'Vue3自定义指令', link: '/Web/Vue/Vue3-Directive' },
{ text: 'Vue3 defineModel', link: '/Web/Vue/Vue3-DefineModel' },
{ text: 'JavaScript知识点整理', link: '/Web/Vue/JavaScript-Guide' },
{ text: '前端包管理器', link: '/Web/Vue/Package' },
{ text: 'Vite核心原理', link: '/Web/Vue/Vite-Principle' },
@@ -27,7 +28,6 @@ export const routers = [
{ text: 'SpringBoot注解', link: '/Web/SpringBoot/SpringBoot-Annotation' },
{ text: 'SpringBoot3原生镜像', link: '/Web/SpringBoot/SpringBoot3-GraalVM' },
{ text: 'SpringBoot技巧', link: '/Web/SpringBoot/SpringBoot-Skills' },
{ text: 'SpringBoot Common', link: '/Web/SpringBoot/SpringBoot-Common' },
{ text: 'SpringBoot RestClient简介', link: '/Web/SpringBoot/SpringBoot-RestClient' },
{ text: 'SpringBoot Redis简介和使用', link: '/Web/SpringBoot/SpringBoot-Redis' },
{ text: 'SpringBoot 线程与事件', link: '/Web/SpringBoot/SpringBoot-Thread' }
@@ -88,7 +88,7 @@ export const routers = [
{
text: '🚀 运维部署',
items: [
{ text: 'OpenObserve部署和使用', link: '/DevOps/OpenObserve' },
{ text: 'OpenObserve部署和使用', link: '/DevOps/OpenObserve' },
{ text: 'Jenkins部署和使用', link: '/DevOps/Jenkins' },
{ text: 'ElasticSearch部署和使用', link: '/DevOps/ElasticSearch' },
{ text: 'Docker简介和安装', link: '/DevOps/Docker' },
@@ -98,6 +98,13 @@ export const routers = [
{
text: '🏗️ 项目实战',
items: [
{
text: '🏢 通用模块',
items: [
{ text: 'Vue3 Common', link: '/Practice/Common/Vue3-Common' },
{ text: 'SpringBoot Common', link: '/Practice/Common/SpringBoot-Common' },
]
},
{
text: '🏢 公司项目',
items: [

View File

@@ -1,7 +1,6 @@
---
title: Docker简介和安装
date: 2025-12-15
isGreat: true
---
# 一、Docker简介

View File

@@ -1,6 +1,7 @@
---
title: OpenObserve部署和使用
date: 2025-11-26
isGreat: true
---
# 一、基础概念
@@ -551,6 +552,40 @@ public class FluentBitLogger : MonoBehaviour
docker-compose文件中也要增加9880端口映射。
:::
## 3.5 Vue
  发送HTTP数据到FluentBit中
```ts
export const sendError = (err, instance, info) => {
try {
const log = {
top: 'vue-ui',
level: 'ERROR',
info,
component: instance?.$options?.name || 'Anonymous',
message: err.message,
url: location.href
}
fetch('http://ip:9880', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(log)
})
} catch {
}
}
```
  利用app.config.errorHandler监听异常
```ts
app.config.errorHandler = (err, instance, info) => {
sendError(err, instance, info)
}
```
# 四、简单使用
## 3.1 配置数据流
  打开控制台,选择数据流->添加数据流输入fluent bit配置文件中的流名称选择数据流类型为Logs。

View File

@@ -365,7 +365,7 @@ public abstract class AbstractException extends RuntimeException {
```
:::
### 3.4 starters模块
## 3.4 starters模块
  该模块为整个项目的核心模块在实际项目中通过引入相应的starter模块并配合autoconfigure模块中的功能即可实现快速自动装配。
  该模块的pom.xml声明的依赖会注入到实际的项目中。
::: danger
@@ -373,7 +373,7 @@ public abstract class AbstractException extends RuntimeException {
之前的SringBoot2是在**main->resources->META-INF**目录下新建spring.factories文件。
:::
#### 3.4.1 stater-jdbc
### 3.4.1 stater-jdbc
  该模块主要提供数据库相关的配置功能。
  目前实现的功能有:
1. 配置MybatisPlus拦截器添加乐观锁和分页插件。
@@ -450,7 +450,7 @@ public class MybatisMetaObjectHandler implements MetaObjectHandler {
  `@ConditionalOnClass(SqlSessionFactory.class)`表示只有存在SqlSessionFactory类时这个配置类才会生效。即如果没有 MyBatis 相关依赖,这个配置类会被 Spring 完全忽略。
  `@Import`用于导入其他配置类或组件到当前配置类中。
#### 3.4.2 starter-web
### 3.4.2 starter-web
  该模块主要提供web相关的配置功能。
  目前实现的功能有:
1. 注册审计拦截器
@@ -748,6 +748,162 @@ HandlerInterceptor 拦截器执行顺序:
4. afterCompletion():视图渲染后 按注册顺序逆序执行。
:::
### 3.4.3 starter-logging
  `src->main->resources`中添加`logback-spring.xml`文件:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<!-- 自定义属性 -->
<springProperty scope="context" name="fluentdHost" source="logging.fluentd.host" defaultValue="localhost"/>
<springProperty scope="context" name="fluentdPort" source="logging.fluentd.port" defaultValue="24224"/>
<springProperty scope="context" name="topic" source="spring.application.name" defaultValue="default"/>
<!-- log路径 -->
<property name="log.path" value="logs/${topic}"/>
<!--控制台日志格式:彩色日志-->
<!-- magenta:洋红 -->
<!-- boldMagenta:粗红-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋红 -->
<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
<!--文件日志格式-->
<property name="FILE_LOG_PATTERN"
value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n"/>
<!--编码-->
<property name="ENCODING"
value="UTF-8"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!--日志级别-->
<level>DEBUG</level>
</filter>
<encoder>
<!--日志格式-->
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!--日志字符集-->
<charset>${ENCODING}</charset>
</encoder>
</appender>
<!--输出到文件-->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志过滤器此日志文件只记录INFO级别的-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_info.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 日志文件的最大大小 -->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志过滤器此日志文件只记录WARN级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_warn.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 日志文件的最大大小 -->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志过滤器此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_error.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 日志文件的最大大小 -->
<maxFileSize>100MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<!--输出到FLUENCY-->
<appender name="FLUENCY" class="ch.qos.logback.more.appenders.FluencyLogbackAppender">
<remoteHost>${fluentdHost}</remoteHost>
<port>${fluentdPort}</port>
<additionalField>
<key>topic</key>
<value>${topic}</value>
</additionalField>
<ignoredField>caller</ignoredField>
<useEventTime>true</useEventTime>
</appender>
<!-- 开发环境 -->
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<!-- 生产环境 -->
<springProfile name="prod">
<root level="INFO">
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
<!-- 容器环境 -->
<springProfile name="docker">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FLUENCY"/>
</root>
</springProfile>
</configuration>
```
&emsp;&emsp;该文件会根据当前环境自动添加日志输出节点。
# 四、项目发布
## 4.1 发布到Git中

View File

@@ -1,5 +1,5 @@
---
title: Vue3-Common
title: Vue3 Common
date: 2025-12-15 23:25
---
@@ -32,7 +32,7 @@
:::
# 二、vite.config.ts配置
## 2.1 生成Typescript类型文件
## 2.1 生成ts类型文件
&emsp;&emsp;安装vite-plugin-dts插件
```cmd
@@ -377,6 +377,167 @@ const app = createApp(App)
app.use(router)
```
8. 在layout中使用
```vue
<template>
<aside class="layout-sidebar">
<menu-list
:route="useRoute()"
:routers="menuRoutes"
:collapse="isCollapse"
class="layout-menu"/>
</aside>
</template>
<script lang="ts" setup>
import { MenuList } from 'vue3-common'
import menuRoutes from '@/router/menu'
import { useRoute } from 'vue-router'
</script>
```
&emsp;&emsp;如果需要对路由菜单进行权限处理,可以使用计算属性过滤一遍:
```ts
const routers = computed(() => {
const menuPathList = menuRoutes.map((item) => item.path)
const removePathList = menuPathList.filter((item) => !authStore.currentUser.menu.includes(item))
removePathList.forEach((item) => {
router.removeRoute(capitalizeStr(item.replace('/', '')))
})
return menuRoutes.filter((route) => !removePathList.includes(route.path))
})
```
::: tip
实现原理:
```ts
import type { RouteComponent, RouteRecordRaw, RouteMeta } from 'vue-router'
/**
* 任意对象类型
*/
interface ICommonObj {
[key: string]: any;
}
/**
* 路由meta配置
*/
interface IRouteMetaConfig {
[key: string]: RouteMeta;
}
/**
* 路由路径转为名称 /admin/dashboard -> AdminDashboard
* @param path
*/
export const convertPath2Name = (path: string): string => {
// 移除路径开头和结尾的斜杠(如果存在)
const newPath = path.replace(/^\/|\/$/g, '')
// 将路径按斜杠分割成多个部分
const parts = newPath.split('/')
// 处理每个部分:将连字符后的字母大写,并移除连字符;处理动态参数(如 :id
return parts.map((part) =>
part.split('-').map((subPart) => {
// 处理动态参数(如 :id
if (subPart.startsWith(':')) {
return subPart.slice(1).charAt(0).toUpperCase() + subPart.slice(2)
}
// 普通部分
return subPart.charAt(0).toUpperCase() + subPart.slice(1)
}).join('')).join('')
}
/**
* 根据meta的order排序
* @param routes 路由列表
*/
export const sortRoutesByOrder = (routes: RouteRecordRaw[]): RouteRecordRaw[] => {
// 排序当前路由的子路由
routes.forEach((route) => {
if (route.children && route.children.length > 0) {
// 如果有子路由,递归排序子路由
route.children = sortRoutesByOrder(route.children)
}
})
// 对当前路由按 meta.order 排序
return routes.sort((a, b) => (a.meta?.order as number || 0) - (b.meta?.order as number || 0))
}
/**
* 获取路由
* @param modules 通过import.meta.glob导入的模块
* @param layout 布局组件
* @param metaConfig 路由meta配置
*/
export const getRoutersByModules = (modules: ICommonObj,
layout: RouteComponent, metaConfig?: IRouteMetaConfig): RouteRecordRaw[] => {
// const modules = import.meta.glob('@/views/admin/**/*.vue')
const routes: RouteRecordRaw[] = []
Object.keys(modules).forEach((filePath) => {
// 获取文件的模块路径和文件名
const parentPath = filePath.substring(0, filePath.lastIndexOf('/')).replace('/src/views', '')
const path = filePath.replace('.vue', '')
.replace('/src/views', '')
.replace(/\[([^\]]+)]/g, '/:$1')
// 为每个文件生成对应的路由
const route: RouteRecordRaw = {
path,
name: convertPath2Name(path),
component: modules[filePath]
}
// 如果存在meta配置
if (metaConfig) {
route.meta = metaConfig[path]
// 设置重定向属性
if (route.meta?.redirect) {
route.redirect = route.meta?.redirect as any
}
}
// 如果该路径已经存在(嵌套路由),则将其作为子路由
let parentRoute = routes.find((item) => item.path === parentPath)
if (!parentRoute) {
parentRoute = {
path: parentPath,
name: convertPath2Name(parentPath),
component: layout,
children: []
}
// 如果存在meta配置
if (metaConfig) {
parentRoute.meta = metaConfig[parentPath]
// 设置重定向属性
if (parentRoute.meta?.redirect) {
parentRoute.redirect = parentRoute.meta?.redirect
}
}
routes.push(parentRoute)
}
// 如果是子路由,加入到父路由的 `children` 数组
parentRoute.children?.push(route)
})
return routes
}
```
&emsp;&emsp;核心原理就是利用`import.meta.glob`创建路径->组件的映射,然后结合路由配置表,转为路由对象并添加到`vue router`中。
:::
## 5.2 图标组件
1. 安装vite-plugin-svg-icons依赖
```cmd

View File

@@ -0,0 +1,93 @@
---
title: Vue3 defineModel
date: 2026-06-05
---
# 一、概念
&emsp;&emsp;defineModel是对 props + emit的语法糖返回一个可写的 ref。Vu3.4开始支持。
&emsp;&emsp;之前的做法:
```ts
// 子组件
const props = defineProps({
modelValue: Number
})
const emit = defineEmits(['update:modelValue'])
function increment() {
emit('update:modelValue', props.modelValue + 1)
}
```
&emsp;&emsp;现在做法:
```ts
const model = defineModel()
```
# 二、简单示例
&emsp;&emsp;父组件:
```vue
<script setup>
import Stepper from './Stepper.vue'
import { ref } from 'vue'
const quantity = ref(1)
</script>
<template>
<div>
<p>购买数量{{ quantity }}</p>
<Stepper v-model="quantity" />
</div>
</template>
```
&emsp;&emsp;子组件:
```vue
<script setup>
const model = defineModel({
type: Number,
default: 1
})
</script>
<template>
<button @click="quantity--"></button>
<span>{{ quantity }}</span>
<button @click="quantity++">+</button>
</template>
```
&emsp;&emsp;`defineProps`定义类似,可以指定类型和默认值。
# 三、多个v-model
&emsp;&emsp;父组件:
```vue
<UserForm
v-model:name="form.name"
v-model:age="form.age"
/>
```
&emsp;&emsp;子组件:
```vue
<script setup lang="ts">
const name = defineModel<string>('name', {
default: ''
})
const age = defineModel<number>('age', {
default: 0
})
</script>
<template>
<input v-model="name" placeholder="姓名" />
<input v-model="age" type="number" placeholder="年龄" />
</template>
```
::: tip
defineProps: 只读,不能改
defineModel: 可写
:::

View File

@@ -0,0 +1,79 @@
---
title: Vue3自定义指令
date: 2026-06-05
---
# 一、概念
&emsp;&emsp;自定义指令Directive 是 Vue 提供的一种机制,用于直接操作 DOM封装可复用的底层 DOM 行为。它是对模板能力的补充当现有模板语法v-if / v-bind / v-on不够用时就可以用指令。
# 二、简单示例
&emsp;&emsp;注册:
```ts
app.directive('demo', {
mounted(el, binding) {
console.log(el, binding)
}
})
```
&emsp;&emsp;使用:
```vue
<div v-demo="'hello'"></div>
```
&emsp;&emsp;使用该指令,就会触发打印效果。
# 三、生命周期
| 钩子 | 说明 |
| ----- | ----- |
| created | 绑定前 |
| beforeMount | 元素挂载前 |
| mounted | 元素已挂载 |
| beforeUpdate | 更新前 |
| updated | 更新后 |
| beforeUnmount | 卸载前 |
| unmounted | 卸载后 |
# 四、参数
&emsp;&emsp;mounted(el, binding, vnode, prevVnode)
| 参数 | 说明 |
| ----- | ----- |
| el | 当前 DOM 元素 |
| binding.value | 指令绑定的值 |
| binding.arg | 参数 |
| binding.modifiers | 修饰符 |
| vnode | 虚拟节点 |
# 五、封装
&emsp;&emsp;`src`目录新建`directives/**.ts`,定义指令:
```ts
import { useAuthStore } from '@/stores/auth'
export default {
mounted(el, binding) {
const authStore = useAuthStore()
const permission = binding.value
const hasPermission = authStore.currentUser?.permission?.includes(permission)
if (!hasPermission) {
el.parentNode?.removeChild(el)
}
}
}
```
&emsp;&emsp;该指令根据用户权限控制DOM是否显示。
&emsp;&emsp;然后在`main.ts`中注册指令:
```ts
import permissionDirective from '@/directives/permission'
app.directive('permission', permissionDirective)
```
&emsp;&emsp;最后在需要的元素中加上`v-permission=""`即可。
::: tip
自定义指令只做DOM相关的事情比如隐藏DOM禁止点击、修改样式等等。
:::