库,框架和钩子

[TOC]

Library 与 Framework 的核心区别

you call library , framework call you

ylfy
在软件开发的世界里,Library(库)和 Framework(框架)是两个高频词汇,它们都旨在提升开发效率、促进代码复用。然而,尽管两者常被同时提及,其本质和在开发流程中的角色却大相径庭。理解它们的区别,对于开发者选择合适的工具、构建健壮的应用程序至关重要。

一个经典的概括,也是理解两者核心差异的关键:“你调用库的功能,而框架则调用你的代码。” 这句话精辟地指明了两者的控制流方向。

1. Library(库):你主动调用的工具箱

定义与目的:
Library,顾名思义,是一系列预先编写好的函数、类或模块的集合。它的主要目的是代码复用,让开发者无需从零开始实现某些通用或特定领域的功能。例如,一个数学库可能包含了各种复杂的数学运算函数(如计算三角函数、矩阵运算),一个图像处理库则提供了图片编辑、滤镜等功能。

控制权:
在使用 Library 时,控制权始终在开发者手中。你决定何时、何地、如何调用库中的功能。库更像是一个工具箱,里面装满了各种专业的工具,你需要用到哪个,就取出哪个来使用。你编写自己的主程序逻辑,然后在需要特定功能时,主动调用库中相应的方法。

特点:

  • 被动角色: 库等待你的调用。
  • 功能聚焦: 通常专注于解决某个特定领域的问题或提供一组特定功能。
  • 灵活性高: 你可以在任何类型的项目中自由选择和组合使用不同的库。

2. Framework(框架):为你搭建的骨架与流程

定义与目的:
与 Library 不同,Framework 更像是一个应用程序的骨架或蓝图。它已经预设了程序的整体结构、控制流程和设计模式。框架定义了一套应用程序的构建规范和生命周期,并在其中留下了许多“空白区域”(即扩展点或回调函数),等待开发者用自己的代码去填充和实现。

控制权:
在 Framework 中,控制权从开发者转向了框架。当你使用一个框架时,你不再是主导者。框架掌握了应用程序的整体控制流,并在其预设的生命周期或事件发生时,回调你所实现的功能。例如,Web 开发中常见的框架(如 Spring、Django、React、Angular)都提供了一个完整的应用结构,包括路由、数据绑定、请求处理等机制。开发者无需从头设计这些底层架构,只需遵循框架的约定,将业务逻辑代码写入框架预留的位置。当应用程序运行时,是框架在主导整个流程,并在适当的时候调用你的代码(如处理某个HTTP请求、渲染某个组件)。

特点:

  • 主动角色: 框架主导应用程序的运行流程。
  • 结构性强: 提供应用程序的整体架构和设计规范。
  • 降低复杂度: 开发者无需关心底层的基础设施和设计模式,只需专注于业务逻辑的实现。

3. 核心区别:控制反转(Inversion of Control - IoC)

Library 和 Framework 最根本的区别在于“控制反转”(Inversion of Control, IoC)。

  • 在使用 Library 时,是你(你的代码)在调用 Library 的功能。 你是主动方,Library 是被动方,听从你的指令。
  • 而在 Framework 中,是 Framework 在调用你(你的代码)。 Framework 是主动方,它掌握了应用程序的整体控制流,并在其预设的生命周期或事件发生时,回调你所实现的功能。这种由框架主导的调用模式,正是 IoC 的体现。

IoC 的好处在于,开发者可以将精力集中在业务逻辑的实现上,而无需关心底层架构、线程管理、生命周期管理等复杂问题,这些都由框架统一处理。这大大降低了开发的复杂度,并促进了代码的标准化和可维护性。

4. 形象比喻与协同工作

我们可以将 Library 比作一个专业的工具箱,里面有螺丝刀、扳手、锤子等各种工具,你需要用时就拿出来用。而 Framework 更像是一个已经搭建好主体的毛坯房,它决定了房间的布局、水电线路等基础结构,你只需要在其中添置家具、装修墙面(即填充你的业务代码)。

两者并非互斥,而是相辅相成。一个典型的开发流程通常是:开发者首先选择一个合适的 Framework 作为应用程序的骨架,然后根据业务需求,在 Framework 的结构内,通过调用各种 Library 来实现具体的功能。例如,在一个 Web 框架中,你可能会使用一个日期处理库、一个加密库或一个图表库来完成特定的数据操作和展示。

无论是 Library 还是 Framework,它们都通过 API (Application Programming Interface) 来向外部提供功能接口。API 是开发者与它们进行交互的“连接器”或“合同”,定义了如何调用它们的功能。

总结

综上所述,Library 和 Framework 在软件开发中扮演着不同的角色。Library 提供的是可复用的功能集合,开发者拥有完全的控制权;而 Framework 则提供了一个完整的应用结构和控制流,开发者需按照其规范填充业务逻辑,控制权则由框架掌握(即控制反转)。

理解这一核心区别,不仅能帮助开发者更好地选择和使用工具,更能深入理解现代软件架构设计的思想,从而构建出更高效、更易维护的应用程序。

好的,我们来深入探讨 Framework 如何通过“钩子”设计模式来实现其“调用你的代码”这一核心机制。


Framework 与“钩子”设计模式:实现控制反转的关键

在前面关于 Framework 的讨论中,我们强调了其核心特征是“控制反转”(IoC),即“框架调用你的代码”。那么,Framework 是如何做到这一点的呢?答案在于它广泛应用了各种**“钩子”(Hook)设计模式**。

1. 什么是“钩子”(Hook)?

在软件设计中,“钩子”可以理解为:

  • 预留的扩展点: 框架在执行其核心流程时,会故意在某些关键节点停下来,提供一个“出口”或“插槽”。
  • 回调机制: 这个“出口”或“插槽”允许开发者将自己的自定义代码“挂载”上去。当框架执行到这个节点时,它就会自动调用(即“回调”)开发者挂载的代码。

“钩子”本身并非一个单一的设计模式,而是一类思想或机制的统称,它可以通过多种具体的设计模式来实现,例如:

  • 模板方法模式(Template Method Pattern): 框架定义一个算法的骨架(模板),将某些步骤延迟到子类中实现。这些可被子类重写的方法就是钩子。
  • 策略模式(Strategy Pattern): 框架定义一个接口,开发者实现该接口以提供不同的算法或行为。框架在运行时调用这个接口的方法。
  • 观察者模式(Observer Pattern)/ 事件机制: 框架在特定事件发生时发出通知,开发者注册监听器或回调函数来响应这些事件。
  • 抽象方法/接口实现: 框架定义抽象类或接口,要求开发者实现其中的抽象方法。

2. Framework 如何利用“钩子”?

Framework 的设计者预见到了应用的通用流程和可能的定制点。他们将这些定制点设计成“钩子”,并规定了这些钩子的调用时机和参数。

  1. 定义骨架与流程: 框架首先定义了应用程序的整体运行流程和结构。例如,一个 Web 框架会定义请求如何被接收、如何路由、如何处理、如何渲染响应等一系列步骤。
  2. 设置“空白区域”(钩子): 在这个预设的流程中,框架会在关键位置留下“空白”或“占位符”。这些空白就是钩子方法或接口。
  3. 开发者填充钩子: 开发者根据业务需求,实现或重写这些钩子方法。这些实现包含了具体的业务逻辑、数据处理、视图渲染等自定义行为。
  4. 框架回调开发者代码: 当框架运行时,它按照预设的流程一步步执行。每当执行到预设的钩子点时,框架就会自动调用(回调)开发者所实现的相应方法。

形象比喻:
如果说 Framework 是一个已经设计好线路和插座的房子,那么“钩子”就是那些预留好的插座。你只需要把你的电器(你的代码)插上去,当房子里的电源系统(框架的控制流)运行时,你的电器就会被供电并工作。你不需要去改动房子的线路,只需要利用好它提供的插座。

3. “钩子”在 Framework 中的典型应用场景:

  • 生命周期钩子(Lifecycle Hooks):
    • Web 框架: before_request (请求前处理), after_request (请求后处理), init (应用初始化), destroy (应用销毁) 等。
    • 前端框架(如 Vue.js, React): created, mounted, updated, componentDidMount, useEffect 等,允许开发者在组件的不同生命阶段执行代码。
  • 事件处理钩子:
    • GUI 框架: onClick (按钮点击), onDraw (绘制界面), onKeyPress (键盘事件) 等,当用户进行交互时,框架回调开发者定义的处理函数。
    • ORM 框架: before_save (保存前验证), after_delete (删除后清理) 等,在数据库操作前后执行特定逻辑。
  • 扩展点钩子:
    • 插件系统: 框架提供接口(钩子),允许第三方开发者编写插件来扩展功能。
    • 中间件(Middleware): 在请求/响应处理链中插入自定义的处理逻辑。

4. “钩子”设计模式带来的益处:

  • 实现控制反转(IoC): 这是最核心的一点,让框架掌握控制流,开发者只需关注业务逻辑。
  • 代码分离与解耦: 框架核心逻辑与业务定制逻辑清晰分离,提高代码的可读性和可维护性。
  • 提高开发效率: 开发者无需关心底层架构和通用流程,只需专注于实现特定功能。
  • 增强可扩展性: 框架易于通过实现钩子来添加新功能或修改现有行为,而无需修改框架核心代码。
  • 强制统一规范: 框架通过钩子的定义,间接强制开发者遵循一定的开发规范和设计模式。

简而言之,“钩子”设计模式是 Framework 实现“控制反转”的基石和具体手段。 它使得 Framework 能够像一个精密的指挥家,在应用程序的生命周期中,在适当的时机,精准地调用开发者所提供的定制化代码,从而构建出强大而灵活的应用程序。

Zap日志库

zapboot

1
2
graph LR
A(config)--->|build|B(logger)

zap快速启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var logger *zap.Logger
func main(){
InitLogger()
//刷新缓冲区确保日志输出
defer logger.Sync()
}
func InitLogger() {
//生成开发者日志默认配置
config := zap.NewDevelopmentConfig()
config.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
//默认是[]string{"stderr"}
config.OutputPaths = []string{"stdout", "./logs/log.txt"}
//配置构建logger
l, _ := config.Build()
logger = l
}

Logger

通过调用zap.NewProduction()/zap.NewDevelopment()或者zap.Example()创建一个Logger。
上面的每一个函数都将创建一个logger。唯一的区别在于它将记录的信息不同。例如production logger默认记录调用函数信息、日期和时间等。
通过Logger调用Info/Error等。
默认情况下日志都会打印到应用程序的console界面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
"net/http"

"go.uber.org/zap"
)

var logger *zap.Logger

func main() {
InitLogger()
defer logger.Sync()
simpleHttpGet("www.5lmh.com")
simpleHttpGet("http://www.google.com")
}
func InitLogger() {
//生成开发者日志默认配置
config := zap.NewDevelopmentConfig()
config.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
//默认是[]string{"stderr"}
config.OutputPaths = []string{"stdout", "./logs/log.txt"}
//配置构建logger
l, _ := config.Build()
logger = l
}

func simpleHttpGet(url string) {
resp, err := http.Get(url)
if err != nil {
logger.Error(
"Error fetching url..",
zap.String("url", url),
zap.Error(err))
} else {
logger.Info("Success..",
zap.String("statusCode", resp.Status),
zap.String("url", url))
resp.Body.Close()
}
}

在上面的代码中,我们首先创建了一个Logger,然后使用Info/ Error等Logger方法记录消息。

日志记录器方法的语法是这样的:

1
func (log *Logger) MethodXXX(msg string, fields ...Field)

其中MethodXXX是一个可变参数函数,可以是Info / Error/ Debug / Panic等。每个方法都接受一个消息字符串和任意数量的zapcore.Field场参数。

每个zapcore.Field其实就是一组键值对参数。

我们执行上面的代码会得到如下输出结果:

1
2
3
4
{"level":"error","ts":1573180648.858149,"caller":"ce2/main.go:25","msg":"Error fetching url..","url":"www.5lmh.com","error":"Get www.5lmh.com: unsupported protocol scheme \"\"","stacktrace":"main.simpleHttpGet\n\te:/goproject/src/github.com/student/log/ce2/main.go:25\nmain.main\n\te:/goproject/src/github.com/student/log/ce2/main.go:14\nruntime.main\n\tE:/go/src/runtime/proc.go:200"}

{"level":"error","ts":1573180669.9273467,"caller":"ce2/main.go:25","msg":"Error fetching url..","url":"http://www.google.com","error":"Get http://www.google.com: dial tcp 31.13.72.54:80: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.","stacktrace":"main.simpleHttpGet\n\te:/goproject/src/github.com/student/log/ce2/main.go:25\nmain.main\n\te:/goproject/src/github.com/student/log/ce2/main.go:15\nruntime.main\n\tE:/go/src/runtime/proc.go:200"}
1.

配置

  • 生成环境和开发者环境

简易配置

1
2
3
4
5
6
7
8
9
10
11
12
13
var logger *zap.Logger
func InitLogger() {
//生成开发者日志默认配置
config := zap.NewDevelopmentConfig()
//日志级别debug info warn error
config.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
//默认是[]string{"stderr"}
config.OutputPaths = []string{"stdout", "./logs/log.txt"}
//配置构建logger
l, _ := config.Build()
logger = l
}

详细配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package main

import (
"os"

"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

func main() {
// 1. 配置 EncoderConfig
// 这是定义日志条目中各个字段如何编码的关键部分。
// 通常从 zap.NewProductionEncoderConfig() 或 zap.NewDevelopmentEncoderConfig() 开始,然后进行修改。
encoderConfig := &zapcore.EncoderConfig{
// TimeKey: 日志中时间戳的键名。生产环境通常使用 "ts" 或 "timestamp"。
TimeKey: "ts",
// LevelKey: 日志中级别的键名。生产环境通常使用 "level"。
LevelKey: "level",
// NameKey: 日志中 logger 名称的键名。如果使用 zap.Named() 设置了 logger 名称,则会显示。
NameKey: "logger",
// CallerKey: 日志中调用者信息(文件名:行号)的键名。
CallerKey: "caller",
// MessageKey: 日志消息内容的键名。生产环境通常使用 "msg" 或 "message"。
MessageKey: "msg",
// StacktraceKey: 日志中堆栈跟踪的键名。
StacktraceKey: "stacktrace",
// LineEnding: 日志条目结束时的字符。通常使用默认的换行符。
LineEnding: zapcore.DefaultLineEnding,

// EncodeLevel: 如何编码日志级别。
// zapcore.CapitalLevelEncoder: 大写字母表示 (如 INFO, ERROR)。
// zapcore.CapitalColorLevelEncoder: 带颜色的大写字母 (主要用于控制台输出)。
EncodeLevel: zapcore.CapitalLevelEncoder,

// EncodeTime: 如何编码时间戳。
// zapcore.ISO8601TimeEncoder: ISO8601 格式 (如 "2023-10-27T10:00:00.123Z"),人类可读且机器友好。
// zapcore.EpochTimeEncoder: Unix 时间戳 (秒)。
// zapcore.EpochMillisTimeEncoder: Unix 时间戳 (毫秒)。
EncodeTime: zapcore.ISO8601TimeEncoder,

// EncodeDuration: 如何编码持续时间字段。
// zapcore.SecondsDurationEncoder: 以秒为单位的浮点数。
EncodeDuration: zapcore.SecondsDurationEncoder,

// EncodeCaller: 如何编码调用者信息。
// zapcore.ShortCallerEncoder: 简短路径 (如 "main.go:23"),适合生产环境,占用空间小。
// zapcore.FullCallerEncoder: 完整路径。
EncodeCaller: zapcore.ShortCallerEncoder,
}

// 2. 构建 zap.Config 结构体并填充所有字段
cfg := &zap.Config{
// Level: 日志级别。
// zap.NewAtomicLevelAt(zap.InfoLevel): 在生产环境中,通常将日志级别设置为 Info,只记录 Info、Warn、Error 等重要日志。
// 如果需要更详细的日志,可以设置为 DebugLevel。
Level: zap.NewAtomicLevelAt(zap.InfoLevel),

// Development: 是否是开发模式。
// false: 生产模式。这意味着 DPanic 会记录日志并退出,而不是 panic;默认的编码器配置会更紧凑。
// true: 开发模式。DPanic 会 panic;编码器配置会更详细和人类可读。
Development: false,

// DisableCaller: 是否禁用调用者信息(文件名:行号)。
// true: 禁用。在生产环境中,出于性能和日志大小考虑,通常会禁用调用者信息。
// false: 启用。在开发调试时非常有用。
DisableCaller: true,

// DisableStacktrace: 是否禁用堆栈跟踪。
// false: 启用。在生产环境中,对于 Error、Panic、Fatal 级别的日志,通常需要启用堆栈跟踪以便排查问题。
DisableStacktrace: false, // 重要的错误日志应该包含堆栈信息

// Sampling: 日志采样配置。
// &zap.SamplingConfig{Initial: 100, Thereafter: 100}: 常见的生产环境设置。
// 意味着对于某个重复的日志消息,前 100 条都会被记录,之后每 100 条只记录一条。这有助于防止日志风暴。
// nil: 不进行采样。
Sampling: &zap.SamplingConfig{
Initial: 100, // 首次出现的相同日志,记录前100条
Thereafter: 100, // 之后每100条记录1条
},

// Encoding: 日志编码格式。
// "json": 生产环境首选,生成结构化日志,便于机器解析和日志收集系统(如 ELK Stack, Splunk)。
// "console": 更适合人类阅读,通常用于开发环境。
Encoding: "json",

// EncoderConfig: 上面已经详细配置好的编码器配置。
EncoderConfig: *encoderConfig,

// OutputPaths: 日志输出目的地。
// []string{"stderr"}: 生产环境中常见的设置,尤其在容器化部署时,日志通常会通过 stderr/stdout 发送给容器运行时。
// []string{"./logs/app.log"}: 也可以指定文件路径。可以指定多个路径,日志会同时输出到所有目的地。
OutputPaths: []string{"stderr"},

// ErrorOutputPaths: Zap 内部错误日志的输出目的地。
// []string{"stderr"}: 强烈建议将 Zap 自身的错误日志输出到 stderr,与应用程序日志分开,以便发现日志系统的问题。
ErrorOutputPaths: []string{"stderr"},

// InitialFields: 初始字段。这些字段会作为公共上下文添加到每个日志条目中。
// 适用于添加服务名称、环境、版本号、实例ID等全局信息,对日志聚合和过滤非常有用。
InitialFields: map[string]interface{}{
"service_name": "my-go-service",
"environment": "production",
"version": "1.0.0",
// "instance_id": "server-123", // 示例,可以动态获取
},
}

// 3. 构建 Logger
logger, err := cfg.Build()
if err != nil {
panic(err)
}
// 确保所有缓冲的日志都被写入。在程序退出前,这是一个非常重要的步骤。
defer logger.Sync()

// 4. 使用 Logger 记录各种级别的日志
logger.Debug("这是一个调试日志,因为日志级别是 Info,所以不会被记录。") // 不会被记录
logger.Info("这是一个信息日志,用于记录常规操作。",
zap.String("user_id", "user-123"),
zap.Int("request_id", 456),
)
logger.Warn("这是一个警告日志,表示可能存在问题。",
zap.String("component", "database_connection"),
zap.Error(os.ErrDeadlineExceeded), // 示例错误
)
logger.Error("这是一个错误日志,表示发生了错误。",
zap.String("error_code", "E001"),
zap.Error(os.ErrPermission), // 示例错误,会包含堆栈信息
)

// DPanic 在生产模式下只会记录日志并退出,不会 panic。
logger.DPanic("这是一个 DPanic 日志,通常用于开发环境,生产环境会记录并退出。",
zap.String("reason", "unrecoverable_state"),
)

// logger.Panic("这是一个 Panic 日志,会记录并 panic。") // 慎用,会导致程序崩溃
// logger.Fatal("这是一个 Fatal 日志,会记录并退出程序。") // 慎用,会导致程序退出
}

博客搭建

从零开始搭建个人独立博客 (Hexo + GitHub Pages)

本文将详细指导您如何从零开始,使用 Hexo 框架和 GitHub Pages 搭建一个个人独立博客。请按照步骤操作,勿跳过任何环节。

1. 准备工作

1.1. 下载并安装必要工具

  • Node.js: 访问 Node.js 官网 下载并安装。建议安装 LTS (长期支持) 版本。
  • Git: 访问 Git 官网 下载并安装。安装过程一路点击“下一步”选择默认设置即可。

1.2. 验证安装

以管理员身份运行命令提示符 (CMD) 或 Git Bash,依次输入以下命令并检查版本信息:

1
2
3
node -v
npm -v # npm 是 Node.js 自带的包管理器
git -v

2. 安装 Hexo

打开命令提示符 (CMD) 或 Git Bash,执行以下命令全局安装 Hexo 命令行工具:

1
npm install -g hexo-cli

3. 搭建 GitHub 仓库

GitHub Pages 将用于托管您的博客静态页面。

  1. 注册/登录 GitHub 账号。
  2. 点击右上角“+”号,选择 New repository (新建仓库)。
  3. Repository name (仓库名) 处,严格按照格式输入:your_github_username.github.io (例如:octocat.github.io)。
  4. 选择 Public (公开)。
  5. 勾选 Add a README file (添加 README 文件)。
  6. 点击 Create repository (创建仓库)。

4. 生成并配置 SSH Keys

SSH Keys 用于本地 Git 和 GitHub 之间的安全通信。

  1. 在任意文件夹内右键,选择 Git Bash Here,输入以下命令生成 SSH Key:

    1
    ssh-keygen -t rsa -C "your_email@example.com"

    过程中会提示输入密码,直接按 4 次 Enter 键跳过即可(不设置密码)。

  2. 找到生成的公钥:
    默认路径在 C:\Users\YourUsername\.ssh\,找到 id_rsa.pub 文件。
    用记事本或其他文本编辑器打开 id_rsa.pub,复制其中所有内容。

  3. 将公钥添加到 GitHub:
    登录 GitHub,点击右上角用户头像 -> Settings (设置)。
    在左侧导航栏找到 SSH and GPG keys
    点击 New SSH key (新建 SSH Key)。
    Title (标题) 随意填写,将复制的公钥内容粘贴到 Key (密钥) 文本框中。
    点击 Add SSH key (添加 SSH Key)。

  4. 测试 SSH 连接:
    在 Git Bash 中输入:

    1
    ssh -T git@github.com

    首次连接会提示确认,输入 yes 并回车。如果看到 “Hi your_username! You’ve successfully authenticated…” 的信息,则表示配置成功。

5. 本地初始化博客

  1. 在您喜欢的位置新建一个文件夹,例如 MyBlog (或 Blog),进入该文件夹。

  2. 在该文件夹内右键,选择 Git Bash Here,执行以下命令初始化 Hexo 博客:

    1
    hexo init

    注意:如果遇到 hexo 命令未找到的错误,尝试在命令前加上 npx,例如 npx hexo init

  3. 安装 Hexo 依赖:

    1
    npm install
  4. 生成并启动本地服务器:
    依次执行以下命令:

    1
    2
    hexo generate  # 简写为 hexo g
    hexo server # 简写为 hexo s

    提示:如果 hexo s 启动失败,可以尝试重复执行,有时是网络连接不稳定导致。

  5. 在浏览器中打开提示的链接 (通常是 http://localhost:4000),您应该能看到默认的 Hexo 博客页面。

  6. 回到 Git Bash 窗口,按 Ctrl + C 关闭本地服务器。

6. 安装 Git 部署插件

在您的博客根目录 (即 MyBlog 文件夹) 下打开 Git Bash,执行:

1
npm install hexo-deployer-git --save

7. 配置并部署博客到 GitHub Pages

  1. 配置 _config.yml 文件:
    在您的博客根目录 (MyBlog 文件夹) 下,用文本编辑器 (如记事本、VS Code) 打开 _config.yml 文件。
    滚动到文件最底部,找到 deploy: 部分。如果存在,删除其下的所有内容。
    将以下配置粘贴到 deploy: 下方,并注意缩进:

    1
    2
    3
    4
    deploy:
    type: git
    repository: <YOUR_REPOSITORY_HTTPS_URL>
    branch: main

    重要

    • 确保 typerepositorybranch 前面各有 两个空格
    • 每个冒号 : 后面都有一个空格。
  2. 获取仓库 HTTPS URL:
    回到您的 GitHub 仓库页面 (your_github_username.github.io)。
    点击绿色的 Code 按钮,选择 HTTPS 选项卡,然后复制显示的 URL (例如:https://github.com/your_username/your_username.github.io.git)。

  3. 粘贴 URL 并保存:
    将复制的 HTTPS URL 粘贴到 _config.yml 文件中 repository: 后面的位置。
    保存 _config.yml 文件。

  4. 生成并部署博客:
    在博客根目录的 Git Bash 中,依次执行以下命令:

    1
    2
    hexo generate # 生成静态文件
    hexo deploy # 部署到 GitHub Pages

    注意:首次部署可能需要您输入 GitHub 用户名和密码。

7.1. (可选) Git 代理配置 (针对网络受限情况)

如果您使用代理工具 (如 Clash Verge),且遇到 hexo d 部署失败,可能需要为 Git 配置代理。假设您的代理端口为 7897

  1. 清除现有代理设置 (可选,避免冲突):

    1
    2
    git config --global --unset http.proxy
    git config --global --unset https.proxy
  2. 设置 HTTP/HTTPS 代理:

    1
    2
    git config --global http.proxy http://127.0.0.1:7897
    git config --global https.proxy http://127.0.0.1:7897
  3. 验证代理设置:

    1
    2
    git config --global --get http.proxy
    git config --global --get https.proxy

    配置完成后,再次尝试 hexo deploy

7.2. (首次使用 Git) 配置用户信息

如果您是首次在当前设备上使用 Git,需要配置您的全局用户信息:

1
2
git config --global user.email "your_email@example.com"
git config --global user.name "Your Name"

配置完成后,再次执行 hexo deploy

部署成功后,您的博客即可通过 https://your_github_username.github.io 访问。

8. 博客基本信息配置

打开博客根目录下的 _config.yml 文件,找到 Site 部分,根据您的需求修改以下内容:

1
2
3
4
5
6
7
8
# Site
title: 您的博客标题
subtitle: 您的副标题
description: 您的博客描述
keywords: 您的关键词 (用逗号分隔)
author: 您的名字
language: zh-CN # 语言
timezone: Asia/Shanghai # 时区

保存文件后,重新执行 hexo generatehexo deploy 使更改生效。

9. 发布新文章

  1. 创建新文章:
    在博客根目录的 Git Bash 中,执行以下命令创建新的 Markdown (.md) 文章文件:

    1
    hexo new "您的文章标题"

    新文章文件将生成在 source/_posts/ 目录下。

  2. 编辑文章:
    您可以使用任何 Markdown 编辑器打开 .md 文件进行编辑。推荐使用 Typora (请支持正版)。

  3. 生成并部署:
    文章编辑完成后,保存文件,然后在 Git Bash 中执行:

    1
    2
    hexo generate
    hexo deploy

    您的新文章将发布到博客中。

10. 后续优化与建议

  • 主题选择与美化:Hexo 拥有丰富的社区主题,您可以访问 Hexo 官网主题页 浏览并选择您喜欢的主题(例如:ButterflyAnzhiyu)。安装和配置主题通常有详细的文档。

至此,您的个人独立博客已基本搭建完成!

编程方向键,Home,End键映射

太棒了!这是一个非常实用的 AutoHotkey 脚本,能极大地提升键盘操作效率。以下是为你准备的博客推文内容,包含了博客文章和推文版本:


解锁键盘超能力:用 CapsLock 掌控光标,告别手部移动!

你是否也曾因为频繁地从主键盘区(Home Row)移动手掌去触碰方向键、Home、End 等导航键而感到不便?尤其是在长时间编码、写作或浏览文档时,这种重复性的手部移动不仅降低效率,还可能增加手部疲劳甚至重复性劳损 (RSI) 的风险。

好消息是,有了 AutoHotkey,我们可以彻底改变这种低效的习惯!今天,我将分享一个巧妙的脚本,它能将你几乎不用的 CapsLock 键变成一个功能强大的导航中心,让你在不离开主键盘区的情况下,轻松实现光标的精准控制。

为什么选择 CapsLock?

CapsLock 键在日常使用中,除了偶尔需要输入全大写字母外,其利用率极低。将其重新映射为修饰键,不仅不会影响现有工作流,反而能:

  1. 提升效率: 指尖无需离开主键盘区,减少手部移动时间。
  2. 增强舒适度: 减少手腕和手指的疲劳,预防 RSI。
  3. 肌肉记忆: 配合 IJKL 的布局,非常符合人体工程学和许多 Vim/Emacs 用户的习惯。
  4. 智能修饰: 最棒的是,它还能智能地传递 ShiftCtrl 等修饰键,这意味着你可以轻松实现 Ctrl+Left (跳词)、Shift+End (选择到行尾) 等高级操作!

核心魔法:AutoHotkey 脚本解析

下面是实现这一功能的 AutoHotkey 脚本。如果你还没有安装 AutoHotkey,可以在 AutoHotkey 官网 下载并安装。

脚本代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
; 禁用 CapsLock 原功能
; 确保 CapsLock 不会切换大小写状态
SetCapsLockState, AlwaysOff

; 彻底禁用 CapsLock 键的原始行为,让它仅作为修饰键
CapsLock::Return

; 封装一个函数,用 CapsLock+方向键,根据 Shift/Ctrl 状态发送不同的组合
CapsNav(key) {
; 检查当前 Shift 键是否被按下
shift := GetKeyState("Shift", "P")
; 检查当前 Ctrl 键是否被按下
ctrl := GetKeyState("Ctrl", "P")

sendStr := ""
; 如果 Ctrl 键被按下,则在发送的按键前加上 Ctrl 修饰符 (^)
if (ctrl)
sendStr .= "^"
; 如果 Shift 键被按下,则在发送的按键前加上 Shift 修饰符 (+)
if (shift)
sendStr .= "+"

; 拼接并发送最终的组合键,例如 ^+{Left}
Send % sendStr "{" key "}"
}

; ---------------- 导航键绑定 ----------------
; CapsLock + J -> Left (左移)
CapsLock & j::CapsNav("Left")
; CapsLock + L -> Right (右移)
CapsLock & l::CapsNav("Right")
; CapsLock + I -> Up (上移)
CapsLock & i::CapsNav("Up")
; CapsLock + K -> Down (下移)
CapsLock & k::CapsNav("Down")

; CapsLock + U -> Home (移动到行首)
CapsLock & u::CapsNav("Home")
; CapsLock + O -> End (移动到行尾)
CapsLock & o::CapsNav("End")
; CapsLock + H -> PgUp (向上翻页)
CapsLock & h::CapsNav("PgUp")
; CapsLock + N -> PgDn (向下翻页)
CapsLock & n::CapsNav("PgDn")

如何使用:

  1. 将上述代码保存为一个 .ahk 文件(例如 CapsLockNav.ahk)。
  2. 双击运行该文件。一个绿色的 “H” 图标会出现在你的系统托盘中,表示脚本正在运行。
  3. (可选)将此 .ahk 文件放入 Windows 启动文件夹,让它开机自启动。

体验指尖的魔法

现在,你可以尝试以下操作:

  • CapsLock + J: 光标向左移动
  • CapsLock + L: 光标向右移动
  • CapsLock + I: 光标向上移动
  • CapsLock + K: 光标向下移动
  • CapsLock + U: 光标移动到行首
  • CapsLock + O: 光标移动到行尾
  • CapsLock + H: 向上翻页
  • CapsLock + N: 向下翻页

更强大的组合:

  • Ctrl + CapsLock + J: 向左跳过一个单词 (相当于 Ctrl+Left)
  • Shift + CapsLock + L: 向右选择一个字符 (相当于 Shift+Right)
  • Ctrl + Shift + CapsLock + O: 从当前位置选择到行尾 (相当于 Ctrl+Shift+End)

是不是非常方便?这种方式不仅能让你在编程时保持双手在键盘中心,也能在日常文档编辑、浏览网页时提供流畅无缝的体验。

总结

一个小小的 AutoHotkey 脚本,就能将一个利用率不高的 CapsLock 键,变成你提升键盘效率的强大工具。告别频繁的手部移动,拥抱更舒适、更高效的打字体验吧!

如果你也尝试了这个脚本,欢迎在评论区分享你的使用感受!