简介

在Go语言中,fmt包提供了多种格式化输出的方法,其中Stringer接口用于生成自定义类型的字符串表示。fmt包中的Stringer接口定义如下:

1
2
3
4
type Stringer interface {
String() string
}
// fmt.Println fmt.Print生效

然而,fmt包还提供了一个更具体的接口GoStringer,用于生成Go语言的字符串表示,这在调试和日志记录中非常有用。GoStringer接口定义如下:

1
2
3
4
type GoStringer interface {
GoString() string
}
// fmt.Printf("%#v\n",x) 生效

核心概念和功能

GoStringer接口允许你为自定义类型定义一个方法,该方法返回一个Go语言的字符串表示。这在调试时非常有用,因为它可以提供关于对象的更详细的信息。

示例代码

假设我们有一个自定义类型Person,我们希望它能够提供一个Go语言的字符串表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

type Person struct {
Name string
Age int
}

func (p Person) GoString() string {
return fmt.Sprintf("Person{Name: %q, Age: %d}", p.Name, p.Age)
}

func main() {
p := Person{"Alice", 30}
fmt.Println(p) // 输出: {Alice 30}
fmt.Printf("%#v\n", p) // 输出: Person{Name: "Alice", Age: 30}
}

在这个例子中,我们定义了一个Person类型,并实现了GoStringer接口。当我们打印Person类型的实例时,它会调用GoString方法,返回一个Go语言的字符串表示。

Go语言的独特特性或优势

使用GoStringer接口的一个主要优势是它提供了更详细的字符串表示,这对于调试和日志记录非常有用。此外,它还允许你为自定义类型定义一个标准的字符串表示,这在与其他Go语言代码集成时非常有用。

完整示例

下面是一个更复杂的例子,展示了如何为一个自定义类型实现GoStringer接口,并在调试时使用它:

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
package main

import (
"fmt"
"reflect"
)

type Person struct {
Name string
Age int
}

func (p Person) GoString() string {
return fmt.Sprintf("Person{Name: %q, Age: %d}", p.Name, p.Age)
}

func main() {
p := Person{"Alice", 30}
fmt.Printf("%#v\n", p) // 输出: Person{Name: "Alice", Age: 30}
// fmt.Printf("%#v" 默认输出会携带包名,如 main.Person{Name: "Alice", Age: 30} )
// 使用reflect包来展示GoStringer接口的使用
v := reflect.ValueOf(p)
if gs, ok := v.Interface().(fmt.GoStringer); ok {
fmt.Println(gs.GoString()) // 输出: Person{Name: "Alice", Age: 30}
}
}

总结

通过实现GoStringer接口,你可以为自定义类型定义一个Go语言的字符串表示,这在调试和日志记录中非常有用。此外,它还允许你为自定义类型定义一个标准的字符串表示,这在与其他Go语言代码集成时非常有用。

在使用GoStringer接口时,需要注意的是,它返回的字符串表示应该尽可能详细,以便在调试时提供足够的信息。此外,你还可以考虑实现Stringer接口,以便在不需要Go语言的字符串表示时提供一个更简洁的字符串表示。

服务器端渲染(SSR)已经存在了一段时间,但仍然值得我们深入探讨。这项技术可以让你的应用更快、对搜索引擎更友好。在本指南中,我们将解释什么是 SSR,为什么你可能想要使用它,以及如何在不让你抓狂的情况下实现它。我们将涵盖基础知识,将其与客户端渲染进行比较,并讨论一些实际示例。

什么是服务器端渲染?

从根本上说,SSR 是关于在服务器上渲染你的网页,而不是在浏览器中。当用户请求一个页面时,服务器会完成所有繁重的工作,并将一个完全渲染的页面发送给客户端。然后,客户端的 JavaScript 接管,使其具有交互性。服务器在厨房里做准备工作,而浏览器只需将菜端上桌。
以下是一个最小的 Express.js 示例:

SSR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const express = require('express');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const App = require('./App');

const app = express();

app.get('/', (req, res) => {
const html = ReactDOMServer.renderToString(<App />);
res.send(`
<!DOCTYPE html>
<html>
<body>
<div id="root">${html}</div>
<script src="client.js"></script>
</body>
</html>
`);
});

app.listen(3000, () => console.log('Server running on port 3000'));

从服务器到浏览器,完全渲染的页面

当我们谈论 SSR 交付“完全渲染的页面”时,理解其真正含义非常重要。让我们来分解一下:

什么是完全渲染的页面?

完全渲染的页面是一个包含用户首次加载页面时会获得的所有内容的 HTML 文档。这包括:

  • 完整的 DOM 结构
  • 所有文本内容
  • 图像占位符和其他媒体元素
  • 初始样式

以下是一个基本示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<title>My SSR Page</title>
<style>
/* Initial styles */
</style>
</head>
<body>
<header>
<h1>Welcome to My Site</h1>
<nav><!-- Fully populated navigation --></nav>
</header>
<main>
<article>
<h2>Article Title</h2>
<p>This is the full content of the article...</p>
</article>
</main>
<footer><!-- Fully populated footer --></footer>
<script src="hydration.js"></script>
</body>
</html>

与客户端渲染(CSR)的区别

相比之下,一个客户端渲染(CSR)的初始 HTML 可能如下所示:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<title>My CSR Page</title>
</head>
<body>
<div id="root"></div>
<script src="bundle.js"></script>
</body>
</html>

CSR 页面完全依赖 JavaScript 来填充内容。

完全渲染的 HTML 的好处

  • 更快的初始绘制:浏览器可以立即开始渲染内容。
  • 更好的 SEO:搜索引擎可以读取所有内容,而无需执行 JavaScript。
  • 改进的可访问性:屏幕阅读器和其他辅助技术可以立即访问内容。
  • 韧性:即使 JavaScript 加载失败,基本内容仍然可用。

The hydration process

在发送完全渲染的 HTML 之后,SSR 应用程序通常会经历一个称为 hydration 的过程:

  • 服务器发送完全渲染的 HTML。
  • 浏览器立即显示此 HTML。
  • JavaScript 加载并 hydrates 页面,添加交互性。
    以下是一个简化的 React 水合示例:
    1
    2
    3
    4
    5
    6
    // Simplified React hydration example
    import { hydrateRoot } from 'react-dom/client';
    import App from './App';

    const domNode = document.getElementById('root');
    hydrateRoot(domNode, <App />);
    这个过程允许快速的初始加载,同时仍然提供现代 Web 应用的丰富交互性。
    请记住,虽然 SSR 提供了这些完全渲染的页面,但它并非没有权衡。服务器的工作量更大,你需要在服务器和客户端之间小心处理状态。然而,对于许多应用来说,完全渲染页面的好处使 SSR 成为一个有吸引力的选择。

CSR 和 SSR 有什么区别?

客户端渲染(CSR)和服务器端渲染(SSR)是渲染网页的两种不同方法。以下是它们的主要区别:

客户端渲染(CSR)

  1. 服务器发送一个包含 JavaScript 包的最小 HTML 文件。
  2. 浏览器下载并运行 JavaScript。
  3. JavaScript 创建页面内容并使其具有交互性。

    优点:

  • 初始加载后平滑的交互
  • 需要更少的服务器资源

    缺点:

  • 较慢的初始页面加载
  • 潜在的 SEO 挑战

服务器端渲染(SSR)

  1. 服务器创建完整的 HTML 内容。
  2. 浏览器快速接收并显示预渲染的 HTML。
  3. 然后加载 JavaScript 以使页面完全具有交互性。

    优点:

  • 更快的初始页面加载
  • 更适合 SEO
  • 在较慢的设备上表现良好

    缺点:

  • 设置可能更复杂
  • 可能使用更多的服务器资源

CSR VS SSR

从本质上讲,CSR更多地在浏览器中工作,而SSR更多地在服务器上工作。它们之间的选择取决于项目的特定需求,平衡因素,如初始加载时间,SEO要求和服务器资源。

SSR 和搜索引擎:HTTP 中的完美匹配

服务器端渲染可以对搜索引擎如何看待你的网站产生重大影响。让我们来分解一下:

更快的索引

搜索引擎机器人是没有耐心的。它们希望立即看到你的内容。使用 SSR,当机器人来敲门时,你的页面已经准备好了——不需要等待 JavaScript 加载和渲染。

内容一致性

SSR 确保搜索引擎看到与用户相同的内容。使用客户端渲染,总是存在机器人可能错过一些动态加载内容的风险。

改进的加载时间

搜索引擎喜欢快速的网站。SSR 可以显著减少初始加载时间,这可能在排名中给你带来一些优势。

移动优先索引

随着谷歌的移动优先索引,SSR 在较慢的移动连接上的性能优势变得更加重要。

预览

虽然严格来说不是搜索引擎功能,但 SSR 使在社交平台上共享内容时生成准确的预览变得更加容易。这可以通过增加参与度和反向链接来间接提升你的 SEO。
SSR 是 SEO 的强大工具,但它不是唯一因素。内容质量、相关性和整体用户体验在搜索引擎排名中至关重要。SSR 只是确保搜索引擎可以有效地抓取和索引你的内容,潜在地在可见性和性能指标上给你带来优势。

如何实际实现 SSR

实现 SSR 并不需要很复杂。让我们来看看如何使用 Next.js(一个流行的 React 框架,使 SSR 变得简单)来实现它:

  1. 设置一个 Next.js 项目。
  2. 创建服务器端渲染的页面。
  3. 让 Next.js 处理完全渲染的 HTML 和客户端水合。

以下是一个使用 App Router 的简单 Next.js 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// app/page.js
async function getData() {
const res = await fetch('<https://api.example.com/data>')
if (!res.ok) {
throw new Error('Failed to fetch data')
}
return res.json()
}

export default async function Home() {
const data = await getData()

return <h1>Hello {data.name}</h1>
}

在这个示例中:

  1. Home 组件是一个异步函数,允许服务器端数据获取。
  2. getData() 获取我们需要的数据。
  3. 组件直接渲染数据。

Next.js 自动处理 SSR 过程:

  1. 当请求进来时,Next.js 在服务器上运行这个组件。
  2. 它等待数据被获取。
  3. 它使用获取的数据渲染组件。
  4. 完全渲染的 HTML 被发送到客户端。
  5. 一旦浏览器中加载了 JavaScript,页面就变得具有交互性。

这种方法让你在不需要手动设置服务器或管理渲染过程的情况下获得 SSR 的好处。

更高级的 SSR 解决方案

如果你不想重新发明轮子,有几个框架可以为你处理 SSR 的复杂性。以下是不同生态系统中流行选项

选项

参考

在Windows系统,AppData,是一个包含三个子文件夹的文件夹:Local、LocalLow 和 Roaming。
了解不同类型的 AppData 文件夹及其用途可以帮助你进行故障排除、管理存储空间等。

AppData

当在 Windows 上安装程序时,它通常会被安装到 C:\Program Files 或 C:\Program Files (x86)(如果是 32 位程序)。该应用程序将为计算机上的所有用户安装,并需要管理员权限才能写入。存储在该文件夹中的任何应用程序设置由所有用户共享。这就是 AppData 的作用。
它是一个隐藏的文件夹,位于每个用户文件夹下。它位于 C:\Users<username>\AppData,包含可能与程序运行能力无关的程序特定信息,
如用户配置。在你的 AppData 文件夹中,你会找到像这样的文件:

  • 用户特定的安装
  • 应用程序配置文件
  • 缓存文件
    如果程序在安装时询问你是否要为所有用户安装的程序,它基本上是在询问你是否要将其安装到 Program Files 或 AppData。Python就是
    这样。此外,AppData 中有三个子文件夹,它们之间的区别很重要。

如何快速找到这些文件夹

  1. 键盘按下”Windows” + “R” 键,会弹出运行窗口
  2. 输入 %appdata% 或 %localappdata% 可以找到用户文件下的appdata

%localappdata% 将打开 C:\Users\你的用户名\AppData\Local 目录
%appdata% 将打开 C:\Users\你的用户名\AppData\Roaming 目录

什么是 Local

Local 文件夹用于存储无法从你的用户配置文件中移动的文件,也经常包含可能太大而无法与服务器同步的文件。例如,它可能存储一些视频游戏
运行所需的文件或你的网络浏览器缓存,这些文件可能太大或没有意义转移到其他地方。开发人员还可能使用 Local 来存储与此特定计算机上的
文件路径相关的信息。将这些配置文件移动到另一台计算机可能会导致程序停止工作,因为文件路径不匹配。其他存储在这里的文件往往是日志文
件、临时文件或非必要数据。

什么是 LocalLow

LocalLow 与 Local 非常相似,但名称中的 “low” 指的是授予应用程序的较低访问级别。例如,以隐身模式运行的浏览器可能仅限于只能访问
LocalLow 文件夹,以防止它能够访问存储在 Local 中的正常用户数据。基本上,这是针对以更受限的安全权限运行的应用程序的。

什么是 Roaming

如果你在域(domain)(a network of computers with a central domain controller that handles your login)上使用 Windows 计算机,那么你可能熟悉 Roaming 文件夹。
如果在同一个域上登录其他设备,此文件夹中的文件将被同步,因为它们被认为是使用你的设备所必需的。这可能是你的网络浏览器收藏夹和书
签、重要的应用程序设置等。当存储的数据可以在设备之间移动而不会出现任何问题时,建议使用此文件夹。例如,《我的世界》将其世界文件、
截图等存储在 Roaming 文件夹中,因为这些文件都可以被获取并迁移到新设备,并且预计可以正常工作。Roaming 非常适合企业环境,包括
Outlook 配置文件和网络打印机配置等设置。它通过存储用户特定的设置和文件,帮助在网络中的不同计算机之间为用户带来一致的环境。

原文

What is AppData, and what are Local, LocalLow, and Roaming?

以下摘自对问题”永生是否是一种酷刑”的回答:

思维是指导行为的。小时候曾经有过一次,因上课不听话被老师叫到走廊罚站了整日。

到了晚上奶奶来接我,我说今日站在走廊经历同学走过议论,太丢脸了,不想再去学校了。奶奶笑着告诉我,现在我看是天大的事情,但等长大一些,今天不过是记忆中的一件小事罢了。

“永生是否是一种酷刑”,很像现代年轻人偶尔会陷入的悲观焦虑。有时候一件小事,在当时看来,是足以毁灭自己的,前途没了光明。其实,等你走到未来,发现还有无限可能。

前几天安慰一位妈妈,她说知道不对,但放不下对孩子每次考试成绩的焦虑。

我劝她不妨试着将目标放大,将结果放小去看,或许有用。

目标放大,想象孩子25岁成年工作、结婚的样子。孩子的这一次考试还是否还那么重要?

结果放小,体会你与孩子的每一次学习、玩耍的瞬间,他是不是努力、快乐、感到幸福?

如果你也有不开心的时候,感觉生命无法迈过的时刻,不妨也想象在更长的时间里,在宇宙的维度下,这烦恼不过是一瞬的烟云。认真把握每个精彩的时刻,过好每一个陪伴亲人的瞬间。

不要为没发生的事情而烦恼。“不悔既往,不负当下,不惧未来”,宇宙的安排都是完美的。

类似的句子

  • “The best way to predict the future is to create it.” - Peter Drucker
  • “In the end, we only regret the chances we didn’t take.” - Lewis Carroll
  • “Life is 10% what happens to us and 90% how we react to it.” - Charles R. Swindoll
  • “Worry does not empty tomorrow of its sorrow, it empties today of its strength.” - Corrie ten Boom
  • “The only thing we have to fear is fear itself.” - Franklin D. Roosevelt
  • “千里之行,始于足下。” - 老子
  • “路遥知马力,日久见人心。” - 《元曲选·争报恩》

原文

永生是否是一种酷刑? - 也说的回答: 不要用非永生的大脑,去思考永生后的烦恼。

IndexNow

使用IndexNow可能更加及时推送,也是Bing Webmaster现在推荐的方式

配置

使用github actions定时推送,配置如下,indexnow秘钥存储到github仓库的”Secrets and variables”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
name: "IndexNow"
on:
schedule:
# Scheduled in UTC.
- cron: '0 0,12 * * *'

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

jobs:
check-and-submit:
runs-on: ubuntu-latest
steps:
- name: indexnow-action
uses: bojieyang/indexnow-action@v2
with:
sitemap-location: "https://programnotes.cn/sitemap.xml"
since: 1
since-unit: "day"
key: ${{ secrets.INDEXNOW_KEY }}

参考

为什么迁移

在商店安装的子系统默认放C盘,磁盘剩余空间不多了

准备和验证

查询已安装的子系统

1
2
3
4
5
6
wsl -l

# 适用于 Linux 的 Windows 子系统分发:
# Ubuntu-24.04 (默认)
# docker-desktop
# docker-desktop-data

查询wsl安装的Ubuntu24磁盘位置, “Ubuntu-24.04”为需要查询的子系统版本

阅读全文 »

安装

使用docekr安装,默认为linux/x86_64

1
2
3
4
5
6
7
8
9
10
11
services:
coredns:
image: coredns/coredns:latest
ports:
- 53:53/udp
restart: always
command: -conf /root/Corefile
volumes:
- ./config:/root
hostname: coredns
container_name: coredns
阅读全文 »

redis为什么快

  1. 数据存储在内存中
    Redis完全基于内存存储数据,大部分请求是纯内存操作,速度极快。
    避免了传统磁盘文件数据存储的磁盘I/O开销。

  2. 高效的数据结构
    Redis有5种数据类型:String、List、Hash、Set、SortedSet。
    不同数据类型使用一个或多个底层数据结构支持,以实现更快的速度。

  3. 单线程架构
    单线程节省了上下文切换和CPU消耗的时间。
    没有竞态条件,无需考虑各种锁问题,避免了死锁导致的性能开销。
    允许使用各种“线程不安全”的命令,如Lpush。
    虽然是单线程,但Redis的其他功能(如持久化、异步删除、集群数据同步)由额外的线程执行。
    单线程易于实现,且CPU不会成为瓶颈,因此采用单线程方案。

阅读全文 »

配置nginx代理

1panel入口需要使命令获取

1
1panel user-info

nginx配置

阅读全文 »