0%

最近想要用 Notion 记笔记,奈何 Notion 的标签分类功能确实不太好用…… 看了看其它文章中配置多级标签的繁杂流程之后,我觉得还是写一个插件比较靠谱……

本文主要介绍 Notion 简单的插件开发,编程语言使用 JavaScript,所以熟悉 JS 的同学应该很快就能上手。官网链接:https://developers.notion.com/docs/getting-started,英语好的同学自取。

1. Notion 数据库

Notion 的灵魂在于数据库,它是 Notion 中最强大也最复杂的功能。下面简单梳理一下 Notion 数据库会用到的知识,其它内容估计自己试试也能摸索出来。数据库官方文档:https://www.notion.so/zh-cn/help/category/databases。

1.1 数据库是什么

简单来说,数据库是一个包含了多个 Notion 页面的集合。数据库可以以多种视图动态展现数据库下的页面:

image-20240814081835562

我们一般常用表格(Table)、看板(Board)、列表(List)三种视图,这里方便起见先用列表演示。列表中展示了数据库下所有的页面,初始化几个页面:

image-20240814082238905

把它们在表格视图下展示就是这个样子。同样地,你也可以在表格中的“名称”一栏新建数据库页面。

image-20240814082404908

1.2 页面属性

数据库中的页面与普通页面不一样,它具有属性(Property)。例如上图的标签、日期就都是属性。常用的属性类型有文本、单选、多选、状态等。

给页面添加属性可以在“表格”视图添加,也可以在页面内部添加:

image-20240814082940157

image-20240814083012668

(可能有些反直觉,但标签确实是用“多选”类型实现的)

那如何新建属性呢?点击表格视图下标题栏的“+”号,选择一个类型:

image-20240814083407514

再根据提示操作即可。

image-20240814083516614

简单布置一下,高级文章管理面板的感觉是不是出来了?

image-20240814083746202

image-20240814083837172

2. 创建你的第一个插件

终于进入正题了。这里将创建第一个 Notion 插件。全程只使用了一个 Notion API:数据库读取。

2.1 先决条件

一定的准备条件(虽然大部分同学可能已经拥有)

  • Node.js 及 NPM
  • 一个 Notion 账号及默认工作空间
  • 一个趁手的 IDE,这里以 VS Code 演示

2.2 在 Notion 中创建插件

(Notion 中,我们又将插件(Plug-in)称为集成(Integration))

打开集成页面,创建新集成;

image-20240814095155373

填写基本信息(集成分为自用的内部集成和公共集成,免费版用户只能创建内部集成):

image-20240814095304130

保存之后你会得到一个集成密钥。复制备用。

image-20240814095550340

2.3 准备 Node.js 环境

用 VS Code 打开一个文件夹,输入以下内容初始化并安装依赖:

1
2
npm init
npm install @notionhq/client

然后新建文件 index.js;

新建一个数据库。你可以自己添加内容,也可以复制官方模拟数据。然后在页面中复制 Database ID(如下图):

image-20240814101440079

在数据库页面授权你的插件访问,不然请求会被拒绝:

image-20240814104019011

然后就是 index.js 了~

打开 index.js,引入 Client:

1
const { Client } = require("@notionhq/client");

定义几个常量:

1
2
const NOTION_KEY = "xxx"; // 此处填写你的集成密钥
const NOTION_DATABASE_ID = "xxx"; // 此处填写 Database ID

创建主函数(异步),并在结尾调用。创建新的 Client:

1
2
3
4
5
async function main() {
const notion = new Client({ auth: NOTION_KEY });
}

main();

读取数据库:

1
const response = await notion.databases.retrieve({ database_id: NOTION_DATABASE_ID }); console.log(response);

全貌:

1
2
3
4
5
6
7
8
9
10
11
12
const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx"; // 此处填写你的集成密钥
const NOTION_DATABASE_ID = "xxx"; // 此处填写 Database ID

async function main() {
const notion = new Client({ auth: NOTION_KEY });
const response = await notion.databases.retrieve({ database_id: NOTION_DATABASE_ID });
console.log(response);
}

main();

运行:

1
node index.js

成功!

image-20240814104321778

3. Notion 页面 API

插件与 Notion 的交互主要依靠 API(即前面提到的 Client)。这里简单介绍一下 Notion 的页面 API。

官方文档:https://developers.notion.com/reference/post-page

3.1 读取页面信息

做实验肯定需要原材料。所以打开数据库中的一个页面,随便写点什么:

image-20240814110035953

刚刚的数据库有 Database ID,那么页面肯定有 Page ID。复制备用:

image-20240814110409957

读取页面信息的 API 是 notion.pages.retrieve({page_id: string}),用上面的 Page ID 尝试一下:

1
2
3
4
5
6
7
8
9
10
11
12
const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx";
const NOTION_PAGE_ID = "xxx"; // 刚刚复制的 Page ID

async function main() {
const notion = new Client({ auth: NOTION_KEY });
const response = await notion.pages.retrieve({ page_id: NOTION_PAGE_ID });
console.log(response);
}

main();

看看返回了什么:

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
{
object: 'page',
id: 'your_page_id',
created_time: '2024-08-14T00:13:00.000Z',
last_edited_time: '2024-08-14T03:00:00.000Z',
created_by: { object: 'user', id: '4d41673d-b5c2-48eb-b9bc-ab8171bb1aae' },
last_edited_by: { object: 'user', id: '4d41673d-b5c2-48eb-b9bc-ab8171bb1aae' },
cover: null,
icon: null,
parent: {
type: 'database_id',
database_id: 'xxx'
},
archived: false,
in_trash: false,
properties: {
'重要性': { id: 'YqII', type: 'select', select: [Object] },
'状态': { id: 'eM%7B%7B', type: 'status', status: [Object] },
'标签': { id: 'n%7Dss', type: 'multi_select', multi_select: [Array] },
'名称': { id: 'title', type: 'title', title: [Array] }
},
url: 'https://www.notion.so/Article-1-your_page_id',
public_url: null,
request_id: '25d4c385-3100-4c19-bc34-27aeebb6e5d8'
}

再看看 Property,挑一个重要性来获取吧:

1
2
3
4
5
6
7
8
9
10
11
12
const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx";
const NOTION_PAGE_ID = "xxx";

async function main() {
const notion = new Client({ auth: NOTION_KEY });
const response = await notion.pages.retrieve({ page_id: NOTION_PAGE_ID });
console.log(response.properties.重要性.select);
}

main();

返回正常:

1
{ id: 'utS@', name: 'Very Important', color: 'red' }

3.2 读取页面信息

如果你们有看一些 Notion 的使用教学视频,就一定会听到一句话:在 Notion 中,万物皆为块。页面亦然。所以如果我们要读取页面内容,就要使用页面的另一层身份:块。

有关块信息的官方文档:https://developers.notion.com/reference/patch-block-children

块信息获取的 API:notion.blocks.retrieve({ block_id: string }),其中 Block ID 就是块的 ID。而页面的 Block ID 就是 Page ID。

尝试一下:

1
2
3
4
5
6
7
8
9
10
11
12
const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx";
const NOTION_PAGE_ID = "xxx";

async function main() {
const notion = new Client({ auth: NOTION_KEY });
const response = await notion.blocks.retrieve({ block_id: NOTION_PAGE_ID });
console.log(response);
}

main();

返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
object: 'block',
id: 'your_page_id',
parent: {
type: 'database_id',
database_id: 'xxx'
},
created_time: '2024-08-14T00:13:00.000Z',
last_edited_time: '2024-08-14T03:00:00.000Z',
created_by: { object: 'user', id: '4d41673d-b5c2-48eb-b9bc-ab8171bb1aae' },
last_edited_by: { object: 'user', id: '4d41673d-b5c2-48eb-b9bc-ab8171bb1aae' },
has_children: true,
archived: false,
in_trash: false,
type: 'child_page',
child_page: { title: 'Article 1' },
request_id: 'f3cd2746-ca65-4126-9b37-522b653c9e26'
}

页面内容呢?还有另一个 API:获取子块。notion.blocks.children.list({ block_id: string })

尝试一下:

1
2
3
4
5
6
7
8
9
10
11
12
const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx";
const NOTION_PAGE_ID = "xxx";

async function main() {
const notion = new Client({ auth: NOTION_KEY });
const response = await notion.blocks.children.list({ block_id: NOTION_PAGE_ID });
console.log(response);
}

main();

拿到子块的 Block ID 了:

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
{
object: 'list',
results: [
{
object: 'block',
id: '88625397-c225-428b-97f4-fee545913d72',
parent: [Object],
created_time: '2024-08-14T02:59:00.000Z',
last_edited_time: '2024-08-14T02:59:00.000Z',
created_by: [Object],
last_edited_by: [Object],
has_children: false,
archived: false,
in_trash: false,
type: 'paragraph',
paragraph: [Object]
},
{
object: 'block',
id: '1d83b0c4-4a9f-4fe9-b877-43c5fd5810c5',
parent: [Object],
created_time: '2024-08-14T02:59:00.000Z',
last_edited_time: '2024-08-14T03:00:00.000Z',
created_by: [Object],
last_edited_by: [Object],
has_children: false,
archived: false,
in_trash: false,
type: 'paragraph',
paragraph: [Object]
},
{
object: 'block',
id: 'a47674ef-041d-48b5-a696-d23bc4a687b2',
parent: [Object],
created_time: '2024-08-14T03:00:00.000Z',
last_edited_time: '2024-08-14T03:00:00.000Z',
created_by: [Object],
last_edited_by: [Object],
has_children: false,
archived: false,
in_trash: false,
type: 'paragraph',
paragraph: [Object]
},
{
object: 'block',
id: 'cab3d9a8-40cd-46ed-9ffa-25b37ac3888e',
parent: [Object],
created_time: '2024-08-14T03:00:00.000Z',
last_edited_time: '2024-08-14T03:00:00.000Z',
created_by: [Object],
last_edited_by: [Object],
has_children: false,
archived: false,
in_trash: false,
type: 'paragraph',
paragraph: [Object]
}
],
next_cursor: null,
has_more: false,
type: 'block',
block: {},
request_id: 'e51de75e-9696-448f-b71a-a86b4b31cabf'
}

再获取一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx";
const NOTION_PAGE_ID = "xxx";

async function main() {
const notion = new Client({ auth: NOTION_KEY });
const response = await notion.blocks.children.list({
block_id: NOTION_PAGE_ID,
});
const ids = response.results.map((e) => e.id);
for (const i in ids) {
const j = await notion.blocks.retrieve({ block_id: ids[i] });
console.log(j.paragraph.rich_text); // 获取文本
}
}

main();

Perfect~

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
[
{
type: 'text',
text: { content: 'Paragraph 1', link: null },
annotations: {
bold: false,
italic: false,
strikethrough: false,
underline: false,
code: false,
color: 'default'
},
plain_text: 'Paragraph 1',
href: null
}
]
[
{
type: 'text',
text: { content: 'Paragraph 2', link: null },
annotations: {
bold: true,
italic: false,
strikethrough: false,
underline: false,
code: false,
color: 'default'
},
plain_text: 'Paragraph 2',
href: null
}
]
[
{
type: 'text',
text: { content: 'Paragraph 3', link: null },
annotations: {
bold: false,
italic: true,
strikethrough: false,
underline: false,
code: false,
color: 'default'
},
plain_text: 'Paragraph 3',
href: null
}
]

3.3 官方文档的阅读

本文不能列举出所有 API 的使用方法,具体详见前面给出的官方文档链接。

官方文档食用方法:以 Create a page 为例。(搭配英语好的大脑或浏览器翻译插件食用更佳)

左栏为说明及参数:

image-20240814123357744

右栏为示例。上方为示例代码(JavaScript 要选上!)

image-20240814123624415

下方为响应类型及响应内容(左边就是参数了)。

image-20240814123752128

4. Notion 数据库 API

简单介绍一下 Notion 的数据库 API。

官方文档:https://developers.notion.com/reference/create-a-database

4.1 读取数据库信息

即上文提到过的第一个 API。

API:notion.databases.retrieve({ database_id: string });

示例就不用了,看上文就行。

4.2 查询数据库信息

API:notion.databases.query({ database_id: string, sort: array, filter: json })

database_id 大家都很熟悉,这里说一下其它两个。

首先是 sort。sort 是一个存储排序规则的数组。排序规则为形似 { property: ‘Name’, direction: ‘ascending’ } 的 JSON。property 为依据排序的属性,direction 为排序方向。ascending 为升序,descending 为降序。

详见 https://developers.notion.com/reference/post-database-query-sort

然后是 filter。filter 即过滤器,可以帮你过滤你不想要的页面。“and”为和,“or”为或。示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"and": [
{
"property": "Done",
"checkbox": {
"equals": true
}
},
{
"or": [
{
"property": "Tags",
"contains": "A"
},
{
"property": "Tags",
"contains": "B"
}
]
}
]
}

详见 https://developers.notion.com/reference/post-database-query-filter

5. Notion 富文本

Notion 中的文本基于富文本。在 Notion 中,无论是获取信息,还是插入信息,都离不开它。下面简单介绍它的使用。

官方文档:https://developers.notion.com/reference/block,https://developers.notion.com/reference/rich-text

Notion 块的显示取决于两个重要因素,一个是块类型,另一个就是内部富文本。主要的块类型详见官方文档:

image-20240819171241390

如果没有特殊情况,一般的块类型为段落(Paragraph)。通常一个块的内容可以用如下方式定义:

1
2
3
4
5
6
7
{
paragraph: { // 可以切换成不同的块类型
rich_text: [
// 内部的富文本列表……
]
}
}

接着来说富文本。Notion 的富文本分为三类:文本、提及和等式。等式内容少用所以请自行查阅官方文档。

文本类的富文本主要结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
type: "text", // 类型
text: {
content: "some text", // 文本内容
link: null // 文本链接,少用
},
annotations: {
bold: false, // 是否为粗体
italic: false, // 是否为斜体
strikethrough: false, // 删除线
underline: false, // 下划线
code: false, // 代码风格文字,注意不是代码块
color: "default" // 颜色
},
plain_text: "some text", // 纯文本,便于程序员直接访问无格式文本内容,由 Notion 自动生成
// 如果自己初始化富文本,内容与文本内容一致即可
href": null // 链接,少用
}

提及即 Notion 中近似于链接的内容,熟悉 Notion 的同学应该清楚。提及中最重要的部分是 ID。主要结构如下(以数据库提及为例):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
type: "mention",
mention: {
type: "database", // 可以替换为其它内容类别,比如 page
database: { // 同上
id: "xxx" // ID
}
},
annotations: {
bold: false,
italic: false,
strikethrough: false,
underline: false,
code: false,
color: "default"
},
plain_text: "xxx",
href: "https://www.notion.so/xxx" // 格式为 https://www.notion.so/ 加上 ID
// Notion 一般会自动生成
}

下面是一个提及块的完整示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
paragraph: {
rich_text: [
{
type: "mention",
mention: {
type: "page",
page: {
id: "xxx",
},
},
annotations: {
bold: false,
italic: false,
strikethrough: false,
underline: false,
code: false,
color: "default",
},
plain_text: "This is a test page",
href: "https://www.notion.so/" + id.replace("-", ""),
},
],
},

由于富文本很多内容可以复用,所以建议封装一下:

image-20240819173206806

6. 实战

了解了一些 API 之后,我们就可以进入实战环节了。Notion 插件通常以浏览器插件 + Notion 集成的方式实现(或许也可以用油猴脚本?还没尝试过)。

我的 Notion 插件叫 TagPlus,顾名思义它用来增强 Notion 标签管理功能。它在本地运行得很不错:

image-20240819173500612

image-20240819173444363

最后,Enjoy it~

参考:
Notion API 探索笔记(一)
Notion Developers API Reference
Notion Developers Guides

之前我的编程有关的书本网站啥的全部集体吃灰,但是后面报了几个小比赛。然后想极速复习一下之前学的 C++ 知识。废话不多说~

1. 基础

数据类型

(只记有用的!)

int: ±2^31(-2147483648 ~ 2147483647)

double: 10 位有效数字

long long: ±2^63(-9223372036854775808 ~ 9223372036854775807)

unsigned 正数范围加倍,不能存储负数。

(超出范围会溢出,所以,不开 long long 见祖宗

操作符优先级

先乘除后加减

&& > ||

% > + -

&& || > ? :(三元运算符)

实在不会加括号

类型转换

(类型名)待转换的数,比如 (int)1.2

向下取整

其实这是 C 风格,还有 C++ 风格:

static_cast<类型名>(待转换的数)

但是 C yyds!!!

printf scanf

(虽然 C++ 出了 cout cin,但是 printf scanf 又快又方便,所以 C yyds)

它们都在 cstdio 库里。只需要 #include 即可食用。

printf(“输出内容”,待打印项1,待打印项2……)

输出内容中 %d %f 之类的可以替换成待打印项。依次替换。其中 %d 替换整型,%f 替换浮点型,%s 替换字符串。

% 和字母之前可以加点东西,常见的比如加 .数字,比如 %.4d,它规定输出内容必须为 4 位。若输入不满 4 位用前导 0 填充。但如果是 %.4f 浮点型输入,则是小数点后必须为 4 位,若不满 4 位则在其后加 0。

scanf(“输入内容”,待输入项指针1,待输入项指针2……)

待输入项必须为指针,所以前面**必须加 &**。

常见输入内容为读取整型的 %d,浮点型的 %f,但是基本只会用到整型 %d。遇到换行或空格会停止读入。

如一段代码:

1
2
int a, b;
scanf("%d%d", &a, &b);

当输入:

1
3 5

则 a=3,b=5,

当输入:

1
2
3
5

也是 a=3,b=5。

scanf 也有类似的 %.4d 之类的操作,不过不常用。

while

当 while 后所接表达式为真时,继续循环。

(虽然这很弱智,但是之前我真的弱智地以为 while 后面接的是假表达式时才继续循环)

2. 指针与内存

指针初始化

使用 *变量类型名 指针名 = 变量 初始化,如 int *ptr = &a。

初始化之后 ptr 是 a 的内存地址。

解引用

使用 *指针名 解引用,如 *ptr 取得 ptr 所指变量的值。可能要与指针初始化的情况做区分。

取地址

使用 &指针名 取地址,即初始化指针时用的那个 &。特别的,数组无需取地址,例如 int *ptr = arr 即可。

memset

(其实还有 malloc free 或 new delete 创建动态数组的函数,但是其实没啥大用处)

memset 是一个挺牛的函数,它可以批量为数组赋值。**用法为 memset(数组名,需要批量赋值的值,sizeof(数组名))**。memset 也支持为二维数组批量复制。但是 memset 只能批量赋 0 或 -1(据说 ASCII 字符也可以,没试过)。

比如为一个数组 arr 批量赋值 0:

1
2
3
4
5
6
7
8
9
10
11
#include <cstring>
#include <cstdio>

int arr[100];
int main(){
memset(arr,0,sizeof(arr));
for (int i = 0; i < 100; i++){
printf("%d\n", arr[i]);
}
return 0;
}

输出结果全是 0。

3. 函数有关

传参有关

默认的传参形式不能改变实参,因为函数接收到的参数实际上是拷贝过的,作用域的问题。

然后有人在 C 语言里就使用指针传参以达到改变实参的效果,原理简单易懂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <cstdio>

// 一个利用指针传参实现的小交换函数
void swap(int *a, int *b)
{ // 注意区别函数定义的 * 和下面的 *
int temp = *a;
*a = *b;
*b = temp;
}

int main()
{
int a = 3, b = 5;
printf("%d %d\n", a, b);
swap(&a, &b); // 这里要传地址
printf("%d %d\n", a, b);
return 0;
}

当然这种方法被 C++ 的引用传参取代了,虽然并不是很好理解。其实只是换了种写法,原理不变:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <cstdio>

// 这是利用引用传参实现的交换函数版本
void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}

int main()
{
int a = 3, b = 5;
printf("%d %d\n", a, b);
swap(a, b); // 这里无需传地址
printf("%d %d\n", a, b);
return 0;
}

两者输出结果均是:

1
2
3 5
5 3
内联函数

通常用于短函数,用于提升代码可读性。它的效果是在编译后将函数的代码直接替换至调用处。使用方法直接在函数定义前面加上 inline。这通常用于卡常数(但其实没什么用)

但如果你的编译器不听话的话,它也可以不帮你展开。inline 只是一种建议或提示。

4. 结构体

结构体的使用

由于 OOP 对于 OI 变态的时间限制来说实在太慢,故不复习有关面向对象内容。但是 C++ 中提供了一个类似的东西:结构体。它和类的功能几乎完全相同。

面向对象应该没人不会吧,就跟创建对象一模一样。

1
2
3
4
5
6
7
#include <cstdio>

struct Student
{
int classNum;
int ID;
}; // 分号千万不能少

结构体中也可以定义成员函数,和类一样。

在结构体定义分号前可以直接创建一个实例:

1
2
3
4
5
6
7
#include <cstdio>

struct Student
{
int classNum;
int ID;
} xiaoMing = {3, 5};

或者在其它地方创建实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <cstdio>

struct Student
{
int classNum;
int ID;
};

int main()
{
Student xiaoMing = {3, 5};
return 0;
}

结构体中也可以轻松访问成员变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <cstdio>

struct Student
{
int classNum;
int ID;
};

int main()
{
Student xiaoMing = {3, 5};
printf("%d\n", xiaoMing.classNum);
xiaoMing.classNum = 4;
printf("%d\n", xiaoMing.classNum);
return 0;
}
结构体与普通函数配合使用

与上面的代码等价。

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
#include <cstdio>

struct Student
{
int classNum;
int ID;
};

void printClass(Student s)
{
printf("%d\n", s.classNum);
}

void changeClass(Student &s, int num)
{
s.classNum = num;
}

int main()
{
Student xiaoMing = {3, 5};
printClass(xiaoMing);
changeClass(xiaoMing, 4);
printClass(xiaoMing);
return 0;
}
结构体指针

如果你不小心拿到了一个结构体的指针,你可以使用 -> 代替 . 取得该结构体的成员变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <cstdio>

struct Student
{
int classNum;
int ID;
};

int main()
{
Student *xiaoMing;
xiaoMing->ID = 5; // 它等价于 (*xiaoming).ID = 5
printf("%d\n", xiaoMing->ID);
return 0;
}

运行结果为 5。

5. 其它

sizeof()

是一个长得有点像函数的关键字。它获取变量的存储空间的字节数。它也可以获取数据类型的字节数如 sizeof(int)。

用 sizeof 获取数组的容量时,得到的容量是数组长度 * 数据类型占用的字节数。

1
2
3
4
5
6
7
8
9
10
#include <cstdio>

int arr[100];
int main()
{
// sizeof() 的返回值为 long unsigned int 类型
// 所以如果不想看警告可以手动把它转成 int 类型
printf("%d\n", (int)sizeof(arr));
return 0;
}

输出结果为 400。

用 sizeof 获取结构体的容量时,得到的容量是结构体所有(非静态)成员变量的存储空间的和。

auto

这个东西可以用来代替 int float double 等结构类型,让编译器自动帮你寻找适合的结构类型。比如 auto i = 3 等价于 int i = 3。它主要用于例如 vector<vector> 这样的比较复杂的类型结构之中。

字符串

字符串在 包里,需要 using namespace std;。

字符串的一个冷知识:C++ 中的字符串是 vector 类型。所以 string 可以下标取值,比如 s[3]。

万能头

G++ 编译器里的一个神器:

1
2
#include <bits/stdc++.h>
using namespace std;

需要 using namespace std;。它可以引入 C++ 中的所有库,包括 iostream cstdio vector map 等等非常多。它只存在于 GCC 中,不存在于我之前使用的 MSVC 中,所以一直没发现(T_T)。

这几天刚配置了船新的 Hexo 博客,然后看到使用的 NexT 主题支持很多评论系统。我尝试配置了一下,发现里面坑太多了,真的挺费周章。于是想水一篇分享一下~

1. 为什么不是……

NexT 主题一共支持如下的几个评论系统:

image-20231028142301562

其中我觉得最后一个 Valine 是最好用的,当然也说一下前面几种方法我踩的坑(这里由于 Disqus 需要魔法所以不讨论)

1.1 畅言 / Livere

这两个评论系统都是来自专门做评论的公司,只不过畅言是中国(搜狐)的,而 Livere 是韩国的,音译来必力,不过无需魔法。它们有相同的特点:配置简单,日常使用免费,但是需要登录评论公司或其它的账号(比如畅言需要登录畅言或手机号、微信等账号才能发表评论)才能评论。

再者,畅言的评论系统看起来是真的有点…古老……

image-20231028144309364

反正如果你不想折腾,并且不担心读者体验,可以尝试使用它们。安装方法很懒人,直接在它们的网站上注册免费版,然后把 appid 和 appkey 复制粘贴至 NexT 的 _config.yml 即可。

1.2 Gitalk / Gitment

这两个是基于 GitHub Issues 的评论系统。NexT 貌似只支持 Gitalk。它们可以在 GitHub 上轻松找到。它们几乎是完美的:只需登录 GitHub 即可评论(不过貌似对非 IT 博客有些不友好);基于 GitHub Issues,无后端,开源,免费,管理方便。

但是它们有致命的问题:GitHub 自动创建 Issues 的 API 有用量限制,所以只能被迫使用第三方 API。Gitment 面对这个问题直接躺平,而 Gitalk 默认的 API 又随着 Cloudflare 的被墙被迫需要魔法,改 API 我试了很多次也没成功……

image-20231028150117154

所以这个方案我也只能被迫放弃。如果你热爱折腾,并且恰好魔法高强,可以逝世。

1.3 DisqusJS

这是国内大神 Sukka 写的一个访问 Disqus“评论基础模式”的评论系统。前文已经说过 Disqus 需要魔法,而 DisqusJS 可以打破魔法。不过它也有一个致命缺点:不能发评论,只能看别人的评论。再者最近好像 DisqusJS 也年久失修,无法正常使用了。

如果你对 DisqusJS 感兴趣,可以去 https://blog.skk.moe/post/disqusjs/ 看看,但我觉得没啥用……

2. Valine 配置过程

最后我选择了最后一个 Valine。Valine 轻量级,无后端,无需登陆账号即可发表评论,且开源免费,无需魔法,并且真的很好看:

image-20231029044028914

2.1 配置流程

Valine 的配置不是很复杂。它基于 LeanCloud,所以得先在 LeanCloud 上注册一个账号。注册完可能要实名。

然后新建一个开发版项目,名字随便填:

image-20231029044504174

image-20231029044609998

然后打开设置 - 应用凭证,复制 AppID 与 AppKey:

image-20231029044857715

然后前往设置 - 安全中心,设置一个安全域名,即你的博客所用的域名。Valine 有请求限制,即非安全域名对 LeanCloud 的请求都不会放行,即你不设置安全域名,到时候评论就用不了。但 localhost 不用设置,LeanCloud 自动放行。如下:

image-20231029051345932

然后转到 NexT 的 _config.yml,找到 comments 一栏(大概在第 550 行左右,或者 Ctrl + F),将 active 改成 valine,nav 改成如图所示的样子:

image-20231029051717591

往下滑找到 valine,将 enable 改为 true,然后粘贴 appid 和 appkey:

image-20231029052041829

下面还有几个选项,挑几个好用的说一下:

选项 默认值 含义及作用
notify false 邮件提醒你文章被评论了,LeanCloud 的功能
verify false 好像是是否需要验证码,没试过
placeholder Just go go. 评论框的默认填充
guest_info nick,mail,link 评论者可以填写的个人信息。nick 为名字,mail 为邮箱,link 为评论者博客的网址。举个例子,如果你填 nick,link,那就不能填写邮箱。
pageSize 10 每一页显示的评论条数
language zh-cn 语言,支持英语 en 和简体中文 zh-cn
visitor false 统计文章被阅读过几次。这个功能有点牛,作为一个评论系统竟然实现了统计阅读次数功能~
comment_count true 是否在博客主页显示评论数

大功告成!

P.S 这篇文章同步在 CSDN 博客上。

P.P.S 如果你正在我的博客看这篇文章,那么你会在这句话的下面看到 Valine。

大家好,这是一份简单的开站说明,今天开始我的 GitHub Pages 博客就正式开站了~

有其它博客吗?

其实作者还有一个 CSDN 博客 https://blog.csdn.net/raspi_fans ,上面主要写关于 IT 的内容,但由于素材匮乏,同时也不太想花太多时间精力写质量比较高的长文,于是 CSDN 博客停更了快 1 年(当然后面肯定会补上的……)

发些什么?

这个博客啥都发,包括但不限于编程语言及算法,有关系统折腾和树莓派的一些有的没的,还有一些中高考牲可能需要的一些学习笔记。而 CSDN 我就选一些自己觉得还可以的 IT 博客并且心情好的时候发吧……

有关小站本身的一些东西

首先这个站点是在 GitHub Pages 上部署的(其实看链接也能看得出来),博客用的 Hexo 部署。我用的简洁的 NexT + Pisces 主题。

虽然这是个静态博客,但是普通博客有的它也有,比如分类、存档、标签,甚至评论系统。评论系统我使用 Valine,之前试过畅言、Gitalk、Disqusjs、Gitment 还有 Livere 都没有 Valine 效果好,改天专门写一篇有关这个的。

另外,图床使用 GitCode 仓库(要不是 GitHub 魔法气息浓郁……)

P.S. 左边的 Stop 作名词其实是汽车停靠站的意思,不是停止(T_T)

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment