[{"data":1,"prerenderedAt":80},["ShallowReactive",2],{"articles":3,"search-index":37},{"articles":4},[5,9,13,17,21,25,29,33],{"slug":6,"title":7,"description":8},"demo\u002Ftypst-syntax","Typst 语法样板","用于验证博客 Typst 子集的样板：标题、段落、列表、代码块、表格和网格。",{"slug":10,"title":11,"description":12},"ai\u002Ftokenizer","Tokenizer 定义与实现","Tokenizer 的学习笔记：定义、基本概念与实现。",{"slug":14,"title":15,"description":16},"code\u002Fjavascript\u002Fnpm-pnpm","Node.js 包管理：从单个包到 Workspace","一份面向初学者的 npm 与 pnpm 学习笔记。",{"slug":18,"title":19,"description":20},"code\u002Frust\u002Fcargo","Cargo：从单个包到 Workspace","一份面向初学者的 Cargo 学习笔记。",{"slug":22,"title":23,"description":24},"notes\u002Fopensuse","OpenSUSE 折腾指南","得益于OpenSUSE的Yast，安装过程可以说是相当的舒服，全部图形化操作，是我在安装过许多Linux发行版中，最方便最舒服的。这里就不详细写出安装步骤了（其实是我没有记录过程），但会提供官方文档。",{"slug":26,"title":27,"description":28},"notes\u002Fwin-deepin-dualboot","Windows + Deepin 双系统的安装与使用","说实话我受够了Windows的环境搭建，想要精简就极度繁琐，想要方便就得用宇宙IDE，占十几二十多GB存储。因此，Linux就是最好的选择，除非你需要写Win或Mac的软件等特定情况。",{"slug":30,"title":31,"description":32},"notes\u002Fpicgo-smms","Obsidian 或 Typora + Picgo + smms 实现图片自动上传",">typora写作是很舒服，但是到了图片上传我简直太难了。上一篇博客我说我图床用的是imgchr，现在我屈服了，玩博客用imgchr简直是魔鬼好吧，图片上传完了，居然是乱序上传，导致我很难改图片地址。",{"slug":34,"title":35,"description":36},"notes\u002Fhexo-blog","github + hexo博客搭建教程","托更了很久，我一点不好意思都没有手动滑稽，我想应该没有人等着我的教程的吧。之所以这时才做这教程，是因为我准备开启新项目了，如果还不填坑的话，坑就越来越多，然后就不想填了。为了避免这种情况，所以我决定先",{"articles":38},[39,45,53,58,64,69,73,77],{"slug":10,"title":11,"description":12,"plainText":40,"keywords":41},"基本概念Tokenizer 是连接原始文本与LLM 数值输入的编码系统，其基本任务是：文本 → Token 序列 → Token ID 序列 → LLM例如：\"unbelievable\" → [\"un\", \"believ\", \"able\"] → [412, 8271, 539]一个完整的 Tokenizer 通常包含：Vocabulary：Token ↔ ID 的映射；Tokenization Rules：文本如何被切分为 Token；Normalization：Unicode、空格等文本规范化规则；Special Tokens：如 \u003CBOS>、\u003CEOS>、\u003CPAD>；Decoder：Token 序列如何恢复为文本。因此，Tokenizer 并不只是一个映射表。tokenizer.json 只是将这些规则和数据序列化保存下来。为什么需要 TokenizerLLM 不能直接处理字符串，只能处理数值向量，因此首先需要把文本转换为离散 ID。最直接的两种粒度都有明显问题。字符 \u002F Byte 级例如：unbelievable → u n b e l i e v a b l e优点：词表很小；可以表示任意文本；几乎不存在 OOV（未知词）问题。缺点：序列很长；LLM 需要进行更多次计算。Word 级例如：unbelievable → [unbelievable]优点：序列较短。缺点：词表会非常大；unbelievable、unbelievably 等需要分别存储；无法良好处理新词、代码和多语言文本。因此现代 LLM 通常采用 Subword：unbelievable → un + believ + ableTokenizer 本质上是在：小词表 + 长序列 ↕ 大词表 + 短序列之间寻找合适的计算折中。Tokenizer 如何产生所谓“训练 Tokenizer”，通常不是神经网络意义上的梯度训练，而是：准备代表性语料 ↓ 确定预处理规则 ↓ 选择 Tokenizer 算法 ↓ 统计 \u002F 优化语料 ↓ Vocabulary + Rules ↓ 保存 Tokenizer例如选择：algorithm = BPE vocab_size = 32000算法会根据训练语料自动学习哪些文本片段值得成为独立 Token。因此可以将 Tokenizer 训练类比为：LLM: 数据 + 网络结构 + 优化算法 → Model Weights Tokenizer: 语料 + Tokenizer 算法 + 超参数 → Vocabulary + RulesTokenizer 一般在 LLM 正式训练前确定，并在训练和推理期间保持一致。Tokenizer 基本算法BPE（Byte Pair Encoding）BPE 的核心思想是：> 不断合并语料中最常共同出现的相邻片段。例如训练语料：low lower lowest初始拆分：l o w l o w e r l o w e s t统计相邻组合：(l, o) → 3 (o, w) → 3 (w, e) → 2 ...于是执行：l + o → lo得到：lo w lo w e r lo w e s t继续统计：lo + w → low最终可能得到：Vocabulary: l o w lo low er est ... Merge Rules: l o → lo lo w → low e r → er ...简化后的 BPE 训练算法：vocab = initial_units(corpus) while len(vocab) \u003C target_vocab_size: pairs = count_adjacent_pairs(corpus) a, b = most_frequent(pairs) new_token = a + b corpus = merge(corpus, a, b) vocab.add(new_token)因此 BPE 可以理解为一种根据语料自动学习压缩字典的方法。常见片段：the ing tion 人工 智能 return function更容易成为独立 Token。UnigramUnigram 的思路与 BPE 相反。BPE 从很小的词表开始：字符 \u002F Byte → 不断添加 TokenUnigram 通常从一个较大的候选词表开始：大量候选 Token → 不断删除价值较低的 Token每个 Token 都带有一个概率：Token Probability un 0.08 believe 0.03 able 0.05 ...对于：unbelievable可能存在多种切分：un + believable un + believe + able u + n + believe + ableUnigram 根据整条 Token 序列的概率选择较优方案：𝑃(𝑡1,…,𝑡𝑛)=∏𝑖𝑃(𝑡𝑖)训练过程中逐渐删除贡献较小的 Token，直到词表达到目标大小。其特点是：不依赖固定的 BPE Merge 顺序；同一字符串可以存在多个候选切分；可以通过概率模型选择更合适的 Token 序列。WordPieceWordPiece 与 BPE 类似，也通过 Subword 构建词表。区别主要在于 Token 的选择标准不同：BPE 主要根据相邻片段出现频率；WordPiece 更关注加入某个 Token 后对语言建模能力的改善。BERT 是典型的 WordPiece 使用者。例如：playing → play + ##ing其中 ## 表示该 Token 出现在单词内部。Tokenizer 的设计设计 Tokenizer 时主要需要决定以下内容：训练语料 ↓ 基础单位：Byte \u002F Character ↓ Normalization ↓ Pre-tokenization ↓ BPE \u002F Unigram \u002F WordPiece ↓ Vocabulary Size ↓ Special Tokens其中最重要的几个因素是：训练语料Tokenizer 会直接反映训练数据分布。如果大量数据是代码，可能学习：return const def :: ==如果大量数据是中文，则可能学习：中国 人工智能 模型 系统因此 Tokenizer 本身也是一种针对数据分布的编码设计。Vocabulary Size词表越大：文本 → 更少 Token但 Embedding 和输出层参数更多。对于隐藏维度 𝑑：𝑁embedding=𝑉×𝑑其中 𝑉 为词表大小。因此：Vocabulary ↑ Sequence Length ↓ Embedding Parameters ↑不存在无限增大词表的收益。评价指标Tokenizer 不应只看“能否编码文本”，还应关注：平均 Token 数；bytes \u002F token；中文、英文等不同语言的编码效率；Code Tokenization 效率；Vocabulary Size；对未知字符和异常输入的鲁棒性。Tokenizer 的局限传统 Tokenizer 有一个根本特点：> Token 的划分规则在 LLM 训练之前就已经固定。例如 BPE 可能始终将某个字符串编码为：un + believable而不会根据上下文动态改变计算粒度。它本身并不知道：当前 Token 的语义；当前任务是否困难；哪些区域值得更多计算；哪些区域可以被高度压缩。因此 Tokenizer 实际上人为规定了 LLM 的基础计算粒度。未来研究方向Learnable Tokenizer一种直接思路是：> 让一个神经网络决定文本应该如何分块。例如：Bytes ↓ Small Neural Network ↓ Dynamic Chunks ↓ Large Language Model与固定 BPE 不同，Chunk 可以根据上下文动态变化。简单区域可以使用较大的 Chunk：[The cat is sitting]复杂区域则使用更细粒度：[x] [9] [Q] [@] [7]此时 Tokenization 不再是独立的数据预处理，而成为模型的一部分。无固定 Tokenizer进一步可以完全取消传统 Vocabulary：UTF-8 Bytes ↓ Local Encoder ↓ Latent Patches ↓ Global Model其中：Local Model 处理字符、拼写等局部结构；Global Model 处理语义、知识与推理。核心思想从：如何设计一个更好的 Tokenizer？变成：模型应该如何自动决定自己的计算粒度？这类方法的优势包括：不依赖人工固定词表；无 OOV；对多语言更加自然；可以动态分配计算资源；Global Model 可以在更短的序列上工作。主要问题则集中在输出侧。层次化计算传统 LLM 基本在单一 Token 粒度上进行计算：token token token token ↓ Large Transformer但语言本身具有天然层次：Byte ↓ Character ↓ Subword ↓ Word ↓ Phrase ↓ Sentence ↓ Concept因此一种可能的未来架构是：Bytes ↓ Tiny Local Model ↓ Lexical Chunks ↓ Small Model ↓ Semantic Chunks ↓ Large Global Model低层模型负责：字符 拼写 局部语法高层模型负责：语义 知识 推理 规划其目的不是单纯“去掉 Tokenizer”，而是避免让昂贵的大模型对所有低层信息进行同等级计算。输出与 Diffusion无 Tokenizer 或 Latent 模型的一个核心困难是输出。传统自回归模型：token_1 → token_2 → token_3 → ...优点是质量高、因果关系明确，但必须逐步生成。Diffusion 可以同时更新多个位置：[MASK] [MASK] [MASK] [MASK] ↓ [word] [MASK] [word] [MASK] ↓ [word] [word] [word] [word]因此它有机会提高并行生成能力。但 Diffusion 也存在：需要多次 Denoising；每一步可能重新计算整段序列；输出长度处理更困难；更少 Denoising Step 通常意味着质量下降。因此目前的核心问题仍是：Generation Speed ↕ Generation Quality一种值得关注的结构是： Global Model ↓ Semantic Latent ↓ Local Diffusion Model ↓ Token \u002F Byte SequenceGlobal Model 负责低频、高层的推理；Local Decoder 负责高速展开具体语言。从 Tokenizer 到 Latent Thought更进一步，可以质疑另一个假设：> 推理本身是否必须以语言 Token 的形式进行？传统 Chain-of-Thought：内部表示 → 语言 Token → 内部表示 → 语言 Token → ...模型每进行一步推理，都必须把内部状态转换成人类语言。但人的思考体验往往并不完全表现为逐词生成：问题 ↓ 模糊的概念 \u002F 联想 \u002F 候选方案 ↓ 形成表达意图 ↓ 组织语言 ↓ 逐字、逐词说出因此可以考虑：Input ↓ Encoder ↓ Continuous Latent Thought ↓ Reasoning \u002F Planning ↓ Communicative Intent ↓ Language Decoder ↓ Text此时语言只是智能系统的一种输入输出形式，而不是推理本身。这种架构可能允许连续表示同时保留多个候选方案，而不必像 Token Generation 一样过早做离散选择。最终问题也从：如何设计 Tokenizer？逐渐演化为：模型应该使用什么内部表示进行思考？ 不同计算粒度之间应该如何组织？ 语言是否只是内部认知状态的一种表达形式？这已经从 Tokenizer 问题进一步扩展为 LLM 表示与推理架构问题。",[42,43,44],"tokenizer","bpe","llm",{"slug":14,"title":15,"description":16,"plainText":46,"keywords":47},"本文从 Node.js 项目为什么需要包管理器出发，依次介绍单个包、依赖、脚本、本地命令、锁文件和 Workspace。示例同时给出 npm 与 pnpm 的常用写法，但一个项目只应选择其中一个，并只提交对应的锁文件。本文以 Node.js 24 LTS、npm 11 和 pnpm 11 为基线。Node.js、npm 与 pnpm 分别发布，行为并不总是同步；遇到与本机不一致的地方，应先确认三者版本，再查看对应版本的官方文档。包管理器解决什么问题只有一个 JavaScript 文件时，可以直接运行 node index.js。前端项目变大后，还需要下载框架和构建工具、固定依赖版本、执行测试与打包命令，并协调多个应用和共享库。如果这些规则只存在于口头说明或全局环境中，其他人很难复现项目。npm 和 pnpm 将这些规则记录在项目中。它们主要负责：读取和更新 package.json；从 registry 解析并下载依赖；维护可复现安装所需的锁文件；将项目内工具暴露为可执行命令；运行 scripts 中约定的开发、测试和构建任务；管理包含多个 package 的 Workspace。包管理器并不是 JavaScript 编译器，也不会自行决定怎样处理 TypeScript、JSX、CSS 或图片。Vite、Rsbuild、TypeScript、ESLint 和测试框架等工具负责具体工作，npm 或 pnpm 负责安装并调用它们。这一点与同时负责依赖解析和 Rust 构建流程的 Cargo 不完全相同。六个基本概念Node.js 工具链经常同时出现 runtime、package、registry、dependency、script 和 workspace。它们描述不同层次。Node.js 是运行 JavaScript 和工具命令的 runtime。Node.js 通常附带 npm，但 pnpm 是独立发布的工具。Package 是由一个 package.json 描述的源码或发布单元。应用、组件库和命令行工具都可以是 package。Registry 是发布和下载 package 的服务。默认通常是 npm registry，但 package manager 与 registry 不是同一个事物。Dependency 是当前 package 声明需要的另一个 package；依赖还会继续拥有自己的间接依赖。Script 是 package.json 中命名的命令入口，例如 dev、build 和 test。Workspace 将多个 package 组织在一起，使安装、锁文件和批量命令可以统一管理。继续查阅：package.json 字段见 npm package.json；pnpm 与 npm 的设计差异见 pnpm vs npm。选择并固定包管理器npm 通常随 Node.js 一同安装。pnpm 可以由操作系统的包管理机制安装，也可以在 Corepack 可用时启用。Corepack 是否随 Node.js 提供取决于 Node.js 版本和发行方式：Node.js 25 起不再附带 Corepack，因此不要假定每台机器都有 corepack 命令。先查看本机实际版本：node --version npm --version pnpm --version团队项目应明确只使用一种 package manager。支持 packageManager 字段的工具还能据此识别项目期望的工具及精确版本：{ \"name\": \"my-project\", \"private\": true, \"packageManager\": \"pnpm@11.23.0\" }版本号只是示例，真实项目应填写团队实际验证过的版本。该字段是重要提示，但不是所有环境都会自动执行它；CI 和开发环境仍应显式提供相同版本。选择 npm 时提交 package-lock.json，选择 pnpm 时提交 pnpm-lock.yaml。不要在同一项目中交替运行两种工具并同时维护两份锁文件。继续查阅：Corepack 的版本范围和启用方式见 Node.js Corepack；pnpm 的安装方式见 pnpm Installation。管理单个 Package每个 Node.js package 都以 package.json 为配置入口。它描述包本身、依赖和命令，但源代码目录、构建入口与产物位置通常由具体工具决定，而不是由 npm 或 pnpm 统一规定。创建项目用 npm 在当前目录生成配置：mkdir my-project cd my-project npm init -y使用 pnpm 时：mkdir my-project cd my-project pnpm init-y 表示 npm 接受默认值。两条初始化命令都只创建 package.json，不会生成源码或测试；下面是需要继续手工创建的一种常见布局：my-project\u002F ├── package.json ├── src\u002F │ └── main.js └── test\u002F └── main.test.js先创建一个最小入口：console.log(\"Hello, Node.js!\");一个适合应用项目的最小 package.json 可以写成：{ \"name\": \"my-project\", \"version\": \"0.1.0\", \"private\": true, \"type\": \"module\", \"scripts\": { \"start\": \"node src\u002Fmain.js\", \"test\": \"node --test\" } }name 和 version 标识 package。private: true 防止意外发布应用。type: \"module\" 表示 .js 文件默认使用 ECMAScript Modules；如果省略，Node.js 对 .js 的默认解释会不同。scripts 保存项目命令。库项目若准备发布，需要进一步正确配置 exports、files、类型声明和发布内容；这些属于 package API 与发布设计，不能只靠删除 private 完成。运行项目命令执行脚本时，两种工具的基本形式相同：npm run start npm run testpnpm run start pnpm run teststart、test 等少数名称在 npm 中有快捷命令，但教程和团队脚本中统一写 run 通常更清楚。pnpm 在名称不与内置命令冲突时也允许省略 run：pnpm test向脚本后的程序传递参数时，用 -- 明确分隔 package manager 参数和程序参数：npm run start -- --port 3000 pnpm run start -- --port 3000开发、测试与生产构建Node.js package manager 没有 Cargo dev 和 release profile 的统一对应物。项目通过脚本名称表达任务，再由具体工具解释模式：{ \"scripts\": { \"dev\": \"vite\", \"build\": \"vite build\", \"preview\": \"vite preview\", \"test\": \"vitest run\", \"lint\": \"eslint .\", \"typecheck\": \"tsc --noEmit\" } }运行 npm run build 或 pnpm run build 只是调用本地 Vite。是否压缩、怎样拆包、输出到哪里，都由 Vite 配置决定。dist\u002F 是常见产物目录，但不是 npm 或 pnpm 的保证。继续查阅：脚本的精确行为见 npm Scripts和 pnpm run。具体构建选项应查看实际使用的构建工具文档。管理依赖依赖声明写在 package.json，锁文件记录解析后的完整依赖图，node_modules\u002F 则是安装结果。三者职责不同，不应手工把 node_modules\u002F 当作依赖清单。添加与移除依赖npm 使用 install 添加依赖：npm install react npm install --save-dev vite vitest npm uninstall reactpnpm 使用 add 添加依赖，裸 install 只安装清单中已有的依赖：pnpm add react pnpm add --save-dev vite vitest pnpm remove react--save-dev 可以缩写为 -D。命令会同时更新 package.json 和锁文件。生成结果大致如下：{ \"dependencies\": { \"react\": \"^19.0.0\" }, \"devDependencies\": { \"vite\": \"^7.0.0\", \"vitest\": \"^3.0.0\" } }上面的版本仅用于说明字段，不应复制为新项目的固定推荐。添加依赖时让 package manager 根据当前 registry 信息写入版本范围，再检查结果是否符合项目要求。依赖类型最常见的依赖字段有：dependencies：应用运行或库的公开实现正常工作所需的依赖；devDependencies：只用于开发、测试、检查和构建的工具；peerDependencies：库要求最终使用者同时提供的兼容依赖，例如组件库对 React 的要求；optionalDependencies：安装失败时允许继续的可选依赖，使用方必须处理它不存在的情况。应用使用的运行时库通常放在 dependencies，而 Vite、ESLint 和测试框架通常放在 devDependencies。不要因为构建工具会参与生成生产产物，就把它误判为应用运行时依赖。peerDependencies 主要用于发布库，不是减少应用安装项的通用手段。包管理器会如何安装和校验 peer dependency 与其版本有关；设计库的 peer 范围时，应按支持矩阵测试，而不是设置一个未经验证的宽范围。版本范围与实际版本package.json 中常见的 SemVer 范围不是精确锁定：^1.2.3 通常允许更新到兼容的 1.x 版本；~1.2.3 通常允许更新到 1.2.x；1.2.3 表示直接依赖要求该精确版本，但间接依赖仍由完整依赖图决定。package.json 表达允许范围，锁文件记录本次解析出的精确版本与来源。初学时应让添加命令生成常见范围；需要改变策略时再查询 SemVer，而不是随意删除 ^ 或把所有依赖写成 latest。锁文件与可复现安装npm 维护 package-lock.json，pnpm 维护 pnpm-lock.yaml。两者都应由 package manager 自动更新并提交到版本控制，通常不应手工编辑。本地开发安装：npm install pnpm installCI 中应要求清单与锁文件一致，并禁止静默改写锁文件：npm ci pnpm install --frozen-lockfilenpm ci 要求已有锁文件，会先移除现有 node_modules\u002F，不更新 package 清单或锁文件。pnpm 的 CI 默认行为可能随版本与环境变化，显式写出 --frozen-lockfile 更容易表达流水线意图。升级依赖是一次需要审查的项目变更。升级后应阅读锁文件 diff，并运行项目的测试、类型检查和构建，而不是只确认安装命令成功。npm 与 pnpm 的安装结构npm 通常会提升依赖，让许多 package 出现在根 node_modules\u002F。pnpm 使用内容寻址 store、硬链接和符号链接构造隔离的依赖图，项目根通常只直接暴露已声明的依赖。因此，一段代码在 npm 下可能偶然导入某个间接依赖，在 pnpm 下却报“找不到模块”。正确修复通常是把实际使用的 package 声明为当前 package 的直接依赖，而不是依赖 npm 的提升结果，也不是立即把 pnpm 改成完全平铺模式。node_modules\u002F 是可重新生成的安装产物，不应提交到版本控制。pnpm 的全局 store 也不是项目锁文件，不能代替 pnpm-lock.yaml。继续查阅：npm 安装见 npm install；pnpm 添加和安装见 pnpm add与 pnpm install；pnpm 的链接结构见 Symlinked node_modules structure。使用项目内工具把 CLI 工具安装到项目的 devDependencies，可以让团队和 CI 使用同一版本，而不是依赖每个人机器上不同的全局安装。脚本自动找到本地命令当脚本执行 vite、eslint 或 vitest 时，npm 和 pnpm 会将项目的可执行目录加入 PATH：{ \"devDependencies\": { \"eslint\": \"^9.0.0\" }, \"scripts\": { \"lint\": \"eslint .\" } }因此只需运行：npm run lint pnpm run lint不需要把 ESLint 全局安装，也不应在脚本里写某台机器上的绝对路径。直接执行命令需要临时调试脚本之外的命令时，可以使用：npm exec -- eslint --version pnpm exec eslint --versionpnpm exec 在项目环境中执行已安装依赖提供的命令。npm exec 会优先寻找本地命令，但本地不存在时还可以从 cache 或 registry 获取 package 后执行；它不是“仅本地执行”的可靠边界。若必须保证工具已经由项目声明，应先把它加入 devDependencies，再通过 scripts 调用。pnpm 在命令名不与内置命令冲突时还允许写成 pnpm eslint --version，但显式 exec 更容易区分 package manager 命令与依赖提供的命令。下载后一次性执行如果工具尚未加入项目，又只需执行一次，可以使用：npm exec --package create-vite -- create-vite my-app pnpm dlx create-vite my-appnpx 是 npm 提供的常用兼容入口，但它和 npm exec 的参数解析细节并不完全相同。文档和自动化脚本中优先使用语义明确的 npm exec。一次性执行仍会运行从 registry 获取的代码。应确认 package 名、版本和来源；对项目长期依赖的生成器或检查工具，更适合加入 devDependencies 并通过脚本调用。安装脚本与供应链边界依赖可能在安装期间声明 preinstall、install 或 postinstall 等生命周期脚本。npm 与 pnpm 不同版本对依赖构建脚本的默认策略并不相同；当前 pnpm 会要求显式批准部分依赖脚本，并提供 pnpm approve-builds 等机制。看到“build scripts were ignored”一类提示时，不应盲目允许全部脚本。先确认是哪个依赖、脚本为何必要，再按当前 pnpm 文档配置允许或忽略列表。CI 中应提交并审查这类策略，使开发机和流水线保持一致。继续查阅：命令执行见 npm exec、pnpm exec和 pnpm dlx；pnpm 构建脚本批准见 pnpm approve-builds。使用 Workspace当 Web 应用、组件库和内部工具分别成为 package 后，各自独立安装会产生多份锁文件和重复配置。Workspace 让 package manager 共同解析它们，并把本地 package 链接起来。Workspace 常用于 Monorepo，但两者不是同一个概念：Monorepo 描述多个项目存放在同一仓库，Workspace 描述 package manager 如何共同管理其中一组 package。虚拟项目结构下面的项目包含一个应用和两个共享 package：my-project\u002F ├── package.json ├── package-lock.json # 使用 npm 时存在 ├── pnpm-lock.yaml # 使用 pnpm 时存在 ├── pnpm-workspace.yaml # 使用 pnpm 时存在 ├── apps\u002F │ └── web\u002F │ ├── package.json │ └── src\u002Fmain.js └── packages\u002F ├── ui\u002F │ ├── package.json │ └── src\u002Findex.js └── config\u002F └── package.json图中同时列出两种工具的文件只是为了对照。真实项目应保留 package-lock.json，或者保留 pnpm-lock.yaml 与 pnpm-workspace.yaml，不能把两套配置混合使用。根 package 通常不发布，并保存跨成员运行的命令：{ \"name\": \"my-project\", \"private\": true, \"scripts\": { \"build\": \"echo run workspace builds\", \"test\": \"echo run workspace tests\" } }用 npm 声明 Workspacenpm 在根 package.json 中使用 workspaces：{ \"name\": \"my-project\", \"private\": true, \"workspaces\": [ \"apps\u002F*\", \"packages\u002F*\" ] }packages\u002Fui\u002Fpackage.json 声明自己的名称和版本：{ \"name\": \"@example\u002Fui\", \"version\": \"0.1.0\", \"type\": \"module\" }apps\u002Fweb\u002Fpackage.json 可以用满足本地 package 版本的普通 SemVer 范围引用它：{ \"name\": \"@example\u002Fweb\", \"private\": true, \"dependencies\": { \"@example\u002Fui\": \"^0.1.0\" } }npm 安装时会识别并链接匹配的 Workspace package。npm 不使用 pnpm 的 workspace: 协议，因此不要把 pnpm 示例原样复制到 npm 项目。在根目录安装全部成员：npm install给指定成员添加依赖：npm install react --workspace @example\u002Fweb运行单个成员或全部成员的脚本：npm run dev --workspace @example\u002Fweb npm run build --workspaces --if-present npm test --workspaces --if-present用 pnpm 声明 Workspacepnpm 使用根目录的 pnpm-workspace.yaml 选择成员：packages: - \"apps\u002F*\" - \"packages\u002F*\"根 package 总是属于 Workspace，即使 packages 没有显式包含根目录。成员 package 可以用 workspace: 协议要求依赖必须来自当前 Workspace：{ \"name\": \"@example\u002Fweb\", \"private\": true, \"dependencies\": { \"@example\u002Fui\": \"workspace:^\" } }workspace:^ 表示链接本地 package，并在发布时转换为相应的 caret 版本范围。应用通常不会发布，但显式协议仍能防止本地 package 不匹配时悄悄改从 registry 下载。在根目录安装全部成员：pnpm install给指定成员添加依赖：pnpm --filter @example\u002Fweb add react运行单个成员或递归运行脚本：pnpm --filter @example\u002Fweb run dev pnpm --recursive --if-present run build pnpm --recursive --if-present run test--filter 不只支持 package 名，还能按路径、依赖关系和变更范围选择 package。复杂过滤器应先阅读文档并用无副作用命令确认选择结果，避免对错误的一组成员批量修改依赖。共享依赖版本npm 与 pnpm 的 Workspace 都会共享根锁文件，但“锁文件中只有一个版本”并不意味着每个成员声明了相同范围。成员仍应只声明自己真实使用的依赖。pnpm 还提供 catalog，可以在 pnpm-workspace.yaml 中集中命名常用版本：packages: - \"apps\u002F*\" - \"packages\u002F*\" catalog: react: ^19.0.0 typescript: ^5.8.0成员使用 catalog: 引用：{ \"dependencies\": { \"react\": \"catalog:\" }, \"devDependencies\": { \"typescript\": \"catalog:\" } }Catalog 适合确实需要统一维护的版本，不应成为把所有依赖集中到根目录的理由。每个成员的 package.json 仍然是判断其直接依赖的入口。继续查阅：npm Workspace 见 npm Workspaces；pnpm Workspace、过滤和 catalog 见 pnpm Workspaces、Filtering和 Catalogs。在 CI 中安装与验证可靠的流水线应从仓库中的清单和锁文件重新安装，而不是复用开发机的 node_modules\u002F。最小流程通常是：# npm 项目 npm ci npm run lint npm test npm run build或者：# pnpm 项目 pnpm install --frozen-lockfile pnpm run lint pnpm test pnpm run buildWorkspace 可以改为前文的 --workspaces 或 --recursive 命令。流水线还应固定 Node.js 与 package manager 版本，并缓存 package manager 的下载缓存或 pnpm store，而不是把 node_modules\u002F 当作唯一缓存边界。安装成功只证明依赖图可以落盘，不证明源代码正确。至少应运行项目已经声明的类型检查、静态检查、测试和生产构建；库项目还应验证打包结果和公开 API。遇到问题时怎么查npm、pnpm、Node.js 和构建工具各有自己的版本。排错时先判断问题发生在哪一层，再进入对应文档，比反复删除锁文件更可靠。从版本与本机帮助开始node --version npm --version pnpm --version npm help install npm help run-script pnpm help install pnpm help recursive本机帮助与已安装 CLI 一致，适合确认参数名称。网上文章可能针对另一个大版本，复制命令前应先核对版本。观察依赖图npm 常用：npm ls npm explain react npm outdatedpnpm 常用：pnpm list pnpm why react pnpm outdatedls 或 list 查看安装图，explain 或 why 回答“为什么安装了这个 package”，outdated 查看可更新项。重复版本不必然是错误；先检查各成员和间接依赖的版本范围是否允许统一。按现象定位找不到模块：先确认实际导入它的 package 是否在自己的 package.json 中声明了直接依赖。锁文件不一致：使用项目指定的 package manager 更新依赖并提交锁文件，不要在 CI 中关闭冻结检查。npm 可以、pnpm 不行：检查是否依赖了被 npm 提升但未声明的 package，以及依赖安装脚本是否被 pnpm 拦截。脚本找不到命令：确认工具已加入当前 package 的依赖，并通过 run 或 exec 进入项目环境。Workspace 改错成员：确认当前目录和 --workspace 或 --filter 的实际选择范围。构建行为不一致：继续检查 Vite、TypeScript、测试框架等具体工具版本；package manager 只负责调用它们。删除 node_modules\u002F 后重新安装可以验证安装结果是否可重建，却不会自动修复错误的依赖声明。删除锁文件会重新选择整张依赖图，可能引入更多变化，不应作为没有分析的第一步。选择正确的官方资料npm CLI 文档：查询 npm 命令、package.json、锁文件和 Workspace；pnpm 文档：从 Motivation 了解设计，再进入 CLI、Settings 和 Workspace；Node.js API 文档：查询模块系统、runtime 行为和 Corepack；实际构建工具的官方文档：查询 dev、build、产物目录和配置文件。最后可以保留一个简单顺序：先确认 Node.js 与 package manager 版本，再阅读本机 help；依赖问题查看锁文件和依赖图，命令问题查看 scripts，构建问题进入真正执行构建的工具文档。这样无需把所有 CLI 参数背下来，也能知道下一步应去哪里验证。",[48,49,50,51,52],"npm","pnpm","node","workspace","包管理",{"slug":18,"title":19,"description":20,"plainText":54,"keywords":55},"本文从 Cargo 要解决的问题出发，依次介绍单个包、依赖、构建目标和 Workspace。示例只展示目录、命令与配置；其中的源文件只是占位，不包含具体 Rust 代码。本文以当前稳定版 Cargo 和 Rust 2024 Edition 为基线。Cargo 仍在演进；遇到与本机行为不一致的地方，应优先查看本机帮助和当前稳定版官方文档。为什么需要 Cargo只有一个源文件时，可以直接调用 rustc 编译。项目变大后，我们很快会遇到新的问题：源文件应该怎样摆放，第三方库从哪里下载，多个库应按什么顺序编译，开发构建和发布构建又该使用哪些参数？如果每次都手动处理，这些规则会散落在命令和脚本中，难以复现。Cargo 将这些规则集中到项目中。它主要负责：创建符合约定的 Rust 项目；下载并解析依赖；调用 Rust 编译器构建代码；运行程序和测试；管理包含多个包的 Workspace。四个基本概念Cargo 文档经常使用 package、crate、target 和 workspace。它们看起来相近，但描述的是不同层次。Package 是由一个 Cargo.toml 描述的源码集合。日常所说的“一个 Cargo 项目”，通常就是一个 package。Crate 是 Rust 编译器一次处理的代码单元。一个库或一个可执行程序都会被编译成 crate。Target 告诉 Cargo 从哪个入口编译出哪种 crate。常见 target 包括 library、binary 和 integration test。Workspace 将多个 package 组织在一起，让它们共享锁文件、构建目录和部分配置。一个 package 至少包含一个 crate；它最多有一个 library target，但可以有多个 binary target。Rust 的 module 是 crate 内部组织代码的方式，不属于 Cargo 的项目管理层次，本文不展开。继续查阅：概念不清楚时，可查看 Cargo 术语表和 Rust Book：Packages and Crates。管理单个包手动创建目录和编译命令很容易遗漏必要文件。Cargo 因而约定了一套默认布局，并提供命令生成它。只要项目遵守约定，大部分配置都不需要显式书写。创建项目在新目录中创建一个可执行程序：cargo new my-project在已经存在的目录中初始化项目：mkdir my-project cd my-project cargo init两种方式都会生成最小的 Cargo.toml 和源码入口。若要单独创建只提供库的 package，可以使用 cargo new \u003C目录> --lib。本文的 my-project 先保留可执行程序结构，其默认目录大致如下：my-project\u002F ├── Cargo.toml └── src\u002F └── main.rsCargo.toml 称为 manifest，即 package 的配置入口：[package] name = \"my-project\" version = \"0.1.0\" edition = \"2024\" [dependencies][package] 描述当前包。name 是包名，version 是当前包的版本，edition 决定采用哪一版 Rust 语言规则。[dependencies] 用来声明普通依赖；刚创建的项目没有依赖，因此它可以为空。检查、构建和运行编辑过程中，最常用的是 cargo check：cargo check它检查类型、借用等问题，但不生成最终机器码，因此通常比完整构建更快。链接或代码生成阶段的问题仍需通过 cargo build 发现。需要可执行产物时运行：cargo buildCargo 会先解析并构建依赖，再构建当前包。默认产物位于 target\u002Fdebug\u002F。如果只想立即运行程序，可以使用：cargo runcargo run 会在需要时先构建，再运行当前包的 binary target。若参数需要传给程序而不是 Cargo，用额外的 -- 分隔：cargo run -- --input sample.txt运行测试使用：cargo test它会构建并运行单元测试、集成测试和文档测试。测试代码通常还需要被测包之外的辅助依赖，这类依赖将在后文介绍。开发构建与发布构建开发时更在意编译速度和调试信息，交付时更在意运行速度。Cargo 用 profile 保存这两组构建策略。cargo build 和 cargo run 默认使用 dev profile。需要优化后的产物时使用：cargo build --release cargo run --releaserelease 产物位于 target\u002Frelease\u002F。cargo test 使用单独的 test profile，而不是简单复用 dev。初学阶段通常不必修改 profile；先选对 dev 或 release 即可。继续查阅：命令不熟悉时先运行 cargo help new、cargo help check 或 cargo help build。完整选项见 Cargo Commands；默认目录见 Package Layout。管理依赖项目使用第三方库时，手动下载源码会带来版本、更新顺序和间接依赖等问题。Cargo 允许在 manifest 中声明“需要什么”，再由解析器选择满足条件的一组具体版本。添加依赖推荐先使用命令添加依赖，而不是凭记忆编写配置：cargo add serde cargo add serde --features derive cargo add pretty_assertions --dev这些命令会更新 Cargo.toml。对应配置可能类似：[dependencies] serde = { version = \"1\", features = [\"derive\"] } [dev-dependencies] pretty_assertions = \"1\"[dependencies] 中的库参与正常构建。[dev-dependencies] 只供测试、示例和性能测试等开发目标使用，不会成为普通构建的一部分。命令行是常用入口，manifest 才是最终记录。修改后应阅读生成的配置，确认依赖类型、版本要求和 Feature 符合预期。版本要求不是精确版本依赖中的 version = \"1\" 不是“只能使用 1.0.0”，而是允许 Cargo 在兼容的 1.x 版本中选择。类似地，\"1.2.3\" 通常表示 >=1.2.3, \u003C2.0.0。初学时不必先记住所有版本运算符。更实用的做法是：让 cargo add 写入常见形式；需要精确控制时，再查询“version requirement”。随意使用精确等号会阻止兼容更新，也更容易造成依赖冲突。Feature 是可选能力一个依赖可能提供并非所有用户都需要的能力。Feature 让使用者按需开启它们：[dependencies] serde = { version = \"1\", features = [\"derive\"] }这里启用了 serde 的 derive Feature。Feature 名及其含义由依赖本身定义，不应猜测；应查看该 crate 的文档或其 Cargo.toml。临时选择当前包的 Feature 时，可以使用 --features、--all-features 或 --no-default-features。这些选项影响依赖解析，遇到复杂组合时应查阅 Cargo 的 Features 参考，而不是不断试错。Cargo.lock 记录实际选择Cargo.toml 描述允许的版本范围，Cargo.lock 记录本次解析实际选择的每个依赖版本。它由 Cargo 自动维护，不应手工编辑。对于应用和 Workspace，通常应将 Cargo.lock 提交到版本控制，以便不同机器得到一致的依赖组合。锁文件不会强迫使用库的下游项目采用同一组版本；如果不确定是否提交，Cargo 官方目前的建议也是提交。需要主动更新锁定版本时使用：cargo update cargo update -p serde继续查阅：依赖来源和版本规则见 Specifying Dependencies；Feature 见 Features；锁文件见 Cargo.toml vs Cargo.lock。组织项目目标一个程序常常既要提供可复用逻辑，又要提供命令行入口和外部测试。如果把它们都当成独立项目维护，会产生重复配置。Cargo 用 target 表示同一 package 中不同的编译入口。默认目录约定下面是一个只展示职责、不包含真实代码的虚拟项目：my-project\u002F ├── Cargo.toml ├── src\u002F │ ├── lib.rs # library target │ ├── main.rs # 默认 binary target │ └── bin\u002F │ └── admin.rs # 额外的 binary target └── tests\u002F └── cli.rs # integration test targetCargo 会自动发现这些入口：src\u002Flib.rs 是唯一的 library target；src\u002Fmain.rs 是与 package 同名的 binary target；src\u002Fbin\u002F 下的文件是额外的 binary target；tests\u002F 下每个文件都是独立的 integration test target。集成测试从 package 外部使用 library 的公开接口，因此通常需要 src\u002Flib.rs。如果项目只有 binary 而没有可复用库，仍然可以测试 binary，但组织方式和可访问接口会有所不同。运行指定 target 时，可以显式选择名称：cargo run --bin admin cargo test --test cli何时显式配置 target使用默认目录时，无需在 Cargo.toml 中重复声明 target。只有在入口路径或名称不符合默认约定时，才需要配置：[lib] name = \"shared\" path = \"src\u002Fshared.rs\" [[bin]] name = \"app\" path = \"src\u002Fentry.rs\" [[test]] name = \"cli\" path = \"tests\u002Fcommand.rs\"[lib] 只能出现一次；[[bin]] 和 [[test]] 是数组表，可以重复出现。显式配置很灵活，但也增加了读者需要维护的信息。能使用默认布局时，应优先依靠约定。继续查阅：目录自动发现和全部 target 类型见 Cargo Targets。不确定命令选择了哪些 target 时，查看相应命令帮助中的 “Target Selection”。使用 Cargo Workspace当 app、shared 和内部工具分别成为 package 后，每个包都有自己的 manifest。若它们各自维护版本、依赖和构建目录，配置会重复，包之间也难以保持一致。Workspace 用一个根 manifest 描述它们的关系。Cargo Workspace 常用于 Monorepo，但两者不是同一个概念：Monorepo 描述代码存放方式，Workspace 描述 Cargo 如何共同管理一组 package。虚拟项目结构先将原项目的 manifest、src\u002F 和 tests\u002F 移入 app\u002F，再新增共享库与内部工具，得到下面的虚拟 Workspace：my-project\u002F ├── Cargo.toml # Workspace 根配置 ├── Cargo.lock # 全体成员共享 ├── target\u002F # 全体成员共享 ├── app\u002F │ ├── Cargo.toml │ ├── src\u002Fmain.rs │ └── tests\u002Fcli.rs ├── shared\u002F │ ├── Cargo.toml │ └── src\u002Flib.rs └── tools\u002F ├── Cargo.toml └── src\u002Fmain.rsapp 是用户运行的程序，shared 是可复用库，tools 是内部辅助工具。测试仍是 app 的 target，不必为了“测试”再创建一个 package。声明 Workspace根目录的 Cargo.toml 不含 [package]，因此它是一个虚拟 Workspace：[workspace] members = [\"app\", \"shared\", \"tools\"] default-members = [\"app\"] resolver = \"3\" [workspace.package] version = \"0.1.0\" edition = \"2024\" [workspace.dependencies] shared = { path = \"shared\" } serde = { version = \"1\", features = [\"derive\"] }members 列出 Workspace 成员。default-members 表示在根目录运行命令且没有显式选择 package 时，默认操作 app。虚拟 Workspace 没有根 package，因此若不设置 default-members，默认会操作全部成员。resolver = \"3\" 采用与 Edition 2024 对应的依赖解析规则。虚拟 Workspace 自身没有 package edition，不能从 [package] 推断 resolver，所以应显式写出。[workspace.package] 和 [workspace.dependencies] 保存成员可以继承的公共值；仅仅写在这里还不会自动应用到每个成员。在成员中继承配置app\u002FCargo.toml 可以这样引用公共配置：[package] name = \"app\" version.workspace = true edition.workspace = true [dependencies] shared.workspace = true serde.workspace = true [dev-dependencies] pretty_assertions = \"1\"version.workspace = true 表示从 [workspace.package] 继承版本；shared.workspace = true 表示从 [workspace.dependencies] 继承依赖声明。这样可以集中维护公共版本和路径，同时让每个成员清楚声明自己实际使用了哪些依赖。shared\u002FCargo.toml 和 tools\u002FCargo.toml 也只继承各自需要的值：# shared\u002FCargo.toml [package] name = \"shared\" version.workspace = true edition.workspace = true [dependencies] serde.workspace = true# tools\u002FCargo.toml [package] name = \"tools\" version.workspace = true edition.workspace = true [dependencies] shared.workspace = true路径依赖中的 path 相对于声明它的 manifest。上例的 shared = { path = \"shared\" } 位于根 manifest，因此指向根目录下的 shared\u002F。选择要操作的包在 Workspace 根目录执行：cargo check由于设置了 default-members = [\"app\"]，Cargo 默认检查 app，并按依赖关系检查它需要的 shared。需要操作全部成员时使用：cargo check --workspace cargo test --workspace只操作某个 package 时使用 -p，它是 --package 的缩写：cargo check -p shared cargo run -p app cargo run -p toolsWorkspace 全体成员共享根目录下的 Cargo.lock 和 target\u002F。[profile.*] 等构建配置也只从 Workspace 根 manifest 读取；把 profile 写进成员 manifest 不会产生预期效果。继续查阅：Workspace 字段、继承范围和默认成员规则见 Workspaces。命令选择规则见任一 Cargo 命令页面中的 “Package Selection”。遇到问题时怎么查Cargo 的命令和配置很多，没有必要全部记住。更稳定的方法是先判断问题属于“命令怎么用”“某个字段是什么意思”，还是“为什么会选择这个依赖”，再去对应入口查找。从本机帮助开始查看 Cargo 总帮助：cargo help查看具体命令：cargo help build cargo help test cargo run --help本机帮助与当前安装版本一致，适合确认参数名称、默认行为和 package\u002Ftarget 选择选项。查看依赖关系当依赖版本或 Feature 与预期不同时，先观察 Cargo 实际解析出的图：cargo tree cargo tree -i serde cargo tree -e featurescargo tree 显示依赖树；-i 反向查找“谁依赖了它”；-e features 显示 Feature 如何被启用。看到重复依赖并不必然表示错误，应先检查版本要求是否允许统一。选择正确的官方资料The Cargo Book 是 Cargo 官方文档的总入口，其中不同部分解决不同问题：Guide 适合第一次了解项目布局、依赖和测试流程；Reference 适合确认 Cargo.toml 字段、版本规则、Feature 和 Workspace 的精确语义；Commands 适合查询某条命令的全部参数；Glossary 适合核对 package、crate、target 等术语。官方还提供 Cargo Book 中文版。当译文和当前工具行为不一致时，以本机帮助和英文稳定版为准。不要把 Cargo Reference 与 The Rust Reference 混淆：后者描述 Rust 语言本身，而不是 Cargo。最后可以保留一个简单的查询顺序：先运行 cargo help \u003Ccommand>，再阅读 Cargo Book 的 Guide；需要精确配置时进入 Reference，并搜索 manifest 中真实出现的字段名。这样无需背下整本手册，也能在需求变化时找到可靠答案。",[56,57,51,52],"cargo","rust",{"slug":6,"title":7,"description":8,"plainText":59,"keywords":60},"这是一篇用于验证博客 Typst 子集的样板文章，只使用最基本的结构：标题、段落、列表、代码块、表格、网格、对齐和图片。文章刻意不包含纸面布局指令，网页上的渲染行为由前端决定。基本结构段落之间以空行分隔，行内支持强调与等宽两种标记。列表用于枚举要点：一个标题与两个小节；三段短小的说明文字；一个代码块、一个表格和一个网格。编译本文可以使用：typst compile content\u002Fdemo\u002Ftypst-syntax.typ表格与网格表格适合呈现成对的数据：语言职责Typst内容源语法Rust解析与结构转换Vue网页渲染网格用于左右并排的独立内容：子集校验：只接受博客需要的语法。前端渲染：布局细节交给 CSS。图片由官方导出内嵌，博客改为内容寻址缓存：对齐用于居中或靠边的独立内容：一份居中的引文或题记。—— 右对齐的署名",[61,62,63],"typst","demo","样板",{"slug":34,"title":35,"description":36,"plainText":65,"keywords":66},"Introduction——前言托更了很久，我一点不好意思都没有[手动滑稽]，我想应该没有人等着我的教程的吧。之所以这时才做这教程，是因为我准备开启新项目了，如果还不填坑的话，坑就越来越多，然后就不想填了。为了避免这种情况，所以我决定先填了这坑，再开新坑。本篇文章将会一步步教你如何用github和hexo搭建属于自己的博客，以及搭建过程中的一些注意事项，也会教你如何解决搭建过程中出现的问题，我不仅仅会提供一种解决问题的方法，还会提供一种解决问题的思想。因为我早就搭建好博客了，所以为了模拟搭建过程和搭建过程所遇到的问题，我选择虚拟机来模拟这一效果。或许我可以不那么麻烦，但是我的记忆力实际上是不怎么好的，更何况现在距离我搭建完博客已经很久了，有些细节我都忘了，而这些细节又很重要。所以为了最好的效果，我就决定麻烦一些，既方便我以后的复习，也方便大家。Preparation——准备工作在这里，我将告诉你用github pages + hexo的方式搭建博客所需的一些前置要求。当然啦，平台是windows，linux以后再说吧。githubgithub 就不用多说了吧，想要用github pages 那就就免不了一个github的账号，当然有了就不用注册了，可以直接跳过。github是全英文的，我怕有的人看到英文就头疼，所以我就给一个注册的步骤吧。如果用谷歌浏览器的，直接用谷歌翻译后问题就不大了。下面我就教一些不用谷歌浏览器的人github的注册方法。打开github在网页的地址栏输入github的官网github.com，然后回车。打开注册页面在github的官网上头点击Sign up打开注册页面。输入信息并注册Node.jsHexo是基于Node.js开发的，所以要安装hexo，Node.js必不可少打开官网老司机可以直接跳过了。可以百度搜索Node.js，或者直接打开nodejs.org在右上角找到一个像谷歌翻译的图标，点击后，找到 中文(简体)并点击，即可更换语言。百度搜索到的一般是已经调好语言了的，所以不在需要更换语言，可以直接跳到下一步。如果对英文不敏感的话，也可直接省略。下载Node.js官网提供两个版本，一个最新版(即当前发布版)，和一个稳定版(即长期支持版)。个人建议下载稳定版，当然你想要最新版也没问题。安装安装没什么好说的，双击运行后，一路默认安装即可。完成后如图所示。检验安装安装后，就免不了检验是否安装成功。打开命令行，即\u003Ckbd>win\u003C\u002Fkbd>+\u003Ckbd>r\u003C\u002Fkbd>，输入 cmd，回车。即可打开命令行。然后在命令行中输入node -v，回车后，输出结果为版本号，说明node.js安装成功。在命令行中输入npm -v，回车后，输出结果为版本号，说明npm安装成功。Gitgit是一个好东西，他有挺多用处，但我都不懂用。如果想了解更多，请去廖雪峰老师的官方网站。不要害怕不精通就不会用，虽然我们不精通也不了解，但这并不妨碍我们使用它。下载首先打开Git的官方网站，同理你可以百度搜索，也可以直接打开git-scm.com进入官网。看到绿色的小电脑没有？点击下方的download即可下载。当然啦，因为是外国网站，下载速度是非常慢的，下面提供一个办法，打开阿里源的镜像站（注意我提供的网址仅限windows平台的Git），下滑找到最新版本，并点击。找到自己的操作系统类型，是32位还是64位。选择好后，点击下载。安装安装也是跟node.js一样，全部默认，毕竟看不懂英文嘛[手动滑稽]。检验安装安装完后，自然也免不了检验，老司机就跳过吧。同样打开命令行，即\u003Ckbd>win\u003C\u002Fkbd>+\u003Ckbd>r\u003C\u002Fkbd>，输入 cmd，回车。在命令行中输入git --version，回车。看到输出版本号即为安装成功。或者直接在桌面右键看到Git GUI Here和Git Bash Here即为安装成功。HexoHexo在我看来就是一个将Markdown按照模版转换为网页的工具。“什么是 Hexo？”>“Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown（或其他渲染引擎）解析文章，在几秒内，即可利用靓丽的主题生成静态网页。”>“——hexo官方文档”注意我提供的Hexo安装方法有两个，第一个是官方的安装方法，但是众所周知，因为国内的原因，所以下载速度十分缓慢，我并不推荐，所以我提供了第二个方法，利用阿里源的镜像，加快下载速度。当然这两种方法我都会在虚拟机上模拟，尽可能地排除问题。必要准备首先你需要找个位置新建一个文件夹，这个文件夹就是以后博客所在的文件夹。那里有hexo、hexo的主题、博客的文章等等文件，可以说那个文件夹就是本地博客与github pages的桥梁，所以必须找好地方。比如我，就直接反正D盘，命名为Blog。而在虚拟机中，因为我只是模拟效果，所以很随意地反在桌面。安装方法一现在我们已经创建好了文件夹，进入文件夹后，右键打开Git Bash Here在打开的命令行中输入npm install hexo-cli -g，回车。之后它就会自动下载文件，等待它命令运行完成即可。运行完成后，显示如图所示，仅供参考。然后输入hexo init，回车后继续等待。结果仅供参考。注意：不要中途退出或者\u003Ckbd>ctrl\u003C\u002Fkbd>+\u003Ckbd>c\u003C\u002Fkbd>停止运行命令，不然会出现一定的错误，这时候将文件夹里的文件清空，再重新运行命令。如果中途下载没有速度或者没有进展，也可用此方法，可能会有一定的效果。接下来检查一下目录，如图所示，会有以下文件和文件夹。检查完后，在命令行中运行npm install。结果如图所示。之后运行hexo s，复制提供的本地博客链接，在浏览器中打开。如图所示，即为安装成功。在命令行中按\u003Ckbd>ctrl\u003C\u002Fkbd>+\u003Ckbd>c\u003C\u002Fkbd>停止运行命令。安装方法二进入文件夹，右键打开Git Bash Here在打开的命令行中运行npm config set registry https:\u002F\u002Fregistry.npm.taobao.org，切换到淘宝源。注意：npm config set registry http:\u002F\u002Fwww.npmjs.org切换官方源。切换完后可用npm info underscore检测是否已经换到淘宝源，看到有淘宝地址，即切换成功。之后就跟方法一同样了。下面我就不重复了，就放一些效果图吧。Github And Hexo Setting——Github和Hexo的设置做了这么久的前期准备，下面终于到正片了。现在我也算是为什么很多教程都没有这么详细的缘故了，真是废时间啊。其实配置ssh key和创建github pages顺序是了调换的，但配置_config.yml必须放在这两个之后，看了就懂了。配置SSH Keyssh key在某种程度上就是一个登录github的密钥，但它实际上并不是，它实际上是github验证用户身份的身份证。有了ssh key，git才能上传文件到用户的仓库，没有ssh key也可以布置博客，只不过会非常麻烦，全部都要手动上传。检查ssh key在命令行中输入cd ~\u002F.ssh，即可检查是否创建过ssh key，如果没有则会提示找不到文件，如图所示。如果创建过ssh key，则任何提升都没有，但工作目录会移动到ssh，所以要注意，不要没切回去就乱输命令。如果已经创建，可以通过cat ~\u002F.ssh\u002Fid_rsa.pub查看ssh key。创建ssh key如果没有创建过ssh key则首先需要全局配置git的本地用户。理论上本地用户可以随意配置，但是我建议输入的是github的信息，不然以后容易遗忘。git config --global user.name \"用户名\" git config --global user.email \"邮箱地址\"配置完成后执行下面的命令生成ssh key，中途需要输入三次回车。ssh-keygen -t rsa -C '上面的邮箱'创建完后通过cat ~\u002F.ssh\u002Fid_rsa.pub查看ssh key。添加信任打开github的账号设置新建ssh key输入必要的信息，选择添加ssh key即可。注意输入的ssh key包括ssh-rsa这一部分。检验想知道是否成功配置ssh key，在git bash中输入ssh -T git@github.com（注意大小写）。出现“You’ve successfully authenticated, but GitHub does not provide shell access.”则说明添加成功。创建Github pages首先登陆github，在右上角点击加号旁的倒三角，选择New repository。填好仓库的信息（公共仓库与私人仓库随意选择），然后创建，创建完后自动开启github pages。注意：仓库名只能和用户名相同并且要加上.github.io，比如我的用户名为Selcon123，我的仓库名只能为Selcon123.github.io，这是因为github只给用户一个与用户名相同的仓库开启github pages。配置_config.yml在博客的文件夹中打开_config.yml，在最下面找到deploy，并进行修改，如图所示。特别注意：输完仓库名后还要加上.git，举个例子，假如我的用户名为Selcon123，我需修改为deploy: type: git repo: https:\u002F\u002Fgithub.com\u002FSelcon123\u002FSelcon123.github.io.git branch: masterDeploy Website——部署网站在这里我打算介绍一些指令，但从这里开始我就不模拟了，因为我的博客已经搭好了，如果模拟的话可能会出现一些问题。hexo官网提供挺多方法部署网站，但我只讲一种，如果想用其他的方法，可以执行研究。使用git部署首先先在git bash中，运行npm install hexo-deployer-git --save安装插件。如果在之前安装hexo时用方法二切换了阿里源，安装应该挺快。安装之后就可以用git部署了，也就是可以在博客文件夹中打开git bash，运行命令部署。以下即常用命令合集。hexo clean #清空缓存（有时候出问题时用到） hexo g #生成页面（缓存） hexo d #部署 hexo g -d #生成并部署 hexo s #本地预览（要先生成）下面是hexo自带的常用命令hexo new \"文章名\" #新建文章（博客） hexo new page \"页面名\" #新建页面（常用于个性化）Writing——正式写作下面我就介绍一整套写作流程流程。新建文章首先在git bash中执行hexo new “My first blog”新建一篇名为My first blog的文章。在.\u002Fsource\u002F\\_posts中找到并编辑，可以用windows自带的文本编辑器编辑，也可用其他。写作语法.md的文件格式说明了它的身份，它是一个Markdown格式的文本，一种对程序员比较友好的文本格式，但必不代表普通人就不懂用了，什么都是可以学习的嘛。Markdown我这里并不会教如何用Markdown，我只会讲如何学习Markdown。推荐菜鸟教程的Markdown教程，有关命令介绍得很清楚，很适合新手，当然我也是从那里学习的。学习完了Markdown，就可以使用其他软件快速地编辑文章，我推荐typora，它是一个优秀的Markdown文本编辑器。但是并不推荐新手直接就用这个编辑器，因为它太优秀了，以至于把Markdown的门槛给抹去了，这样其实对个人的成长并不怎么好。工具终究是工具，最好用的还是自己的脑袋，过度依赖工具，会使自己变成工具的“工具人”。htmlMarkdown是支持部分html标签的，但不是支持html。至于支持什么，自己查吧。图片相关图片这种基本老生常谈了，其实就是图床的问题。其实图片可以上传到github的仓库上，但是github的速度众所周知，所以比较推荐其他的图床。图床这种东西网上找一大片，但最重要的是稳定和速度。稳定就不用说了，要是几天这个平台就没了，那就欲哭无泪了，所以尽量选择老牌，而且最好是能看到他的盈利模式，一般免费的容易死的快。速度这个就更不用说了，要是速度还不如github那就呵呵了，所以尽量选择国内的吧，国外的一般在国内速度都不是很好，当然说不定也有例外。像我的话，用的是路过图床，2011年的，国内的，速度还行，盈利靠广告和企业收费，所以就比较令人放心。但唯一可惜的就是每小时只能传30张，每张不能超过10M。上传到博客在git bash执行hexo g -d部署到博客即可。自定义域名绑定因为我并不打算自定义域名（其实是没钱），所以我并不会自定义域名，可能以后有机会在写一下吧，在这之前可以百度学习。后话至于个性化的内容可能要等以后了，这篇文章也四千多字了，以后的个性化内容也不少，可以说是一个坑分成两次部吧，预告一下个性化的内容：404自定义，音乐播放器，主题更换，站长配置，评论系统，百度自动推送，广告添加，特效等等。如果等不及了可以自行解决，也可以拿钱砸我（明示打赏！），叫我更快点，反正我都不介意[手动滑稽]。如果看来我的教程还是不懂的话，可以到Hexo的官网查看文档学习。本来是想一天就解决了的，没想到事情突然变多，一直拖了三天左右，这也是没办法的事。之所以想要补了这个坑，是因为我想开新坑，至于是什么新坑，拭目以待吧。如果有什么错误，欢迎指出。",[67,68],"Markdown","Hexo",{"slug":22,"title":23,"description":24,"plainText":70,"keywords":71},"安装过程得益于OpenSUSE的Yast，安装过程可以说是相当的舒服，全部图形化操作，是我在安装过许多Linux发行版中，最方便最舒服的。这里就不详细写出安装步骤了（其实是我没有记录过程），但会提供官方文档。系统盘的制作这里我是不按官方的来了，中文wiki的方法其实已经不是很方便了，虽然谈不上过时，但对于小白来说相当复杂，而且不容易成功。这里我推荐新一代多系统启动U盘的方案——Ventory对于Ventory的使用也是相当简单，只需选择u盘，然后安装即可。然后再将相应的系统镜像拖入u盘即可。这里我使用的是OpenSUSE的Tumbleweed，这里比较推荐下载离线安装的版本，毕竟如果出现无法连接的时候就尴尬了。后面将不会再次说明OpenSUSE的版本，在本博客文章中，默认均为Tumbleweed。OpenSUSE的安装要想使用Ventory需要关闭bios的scurity boot功能。对于系统的安装就不详细说明了，OpenSUSE有许多其他发行版都没有的设置，极大丰富了自定义程度，具体可以看官方文档，或者可以参考别人的教程。安装时其实不是很建议选择中文和中文键盘布局，因为这样会导致很多问题，比如中文路径的问题，毕竟Linux对中文的支持也不是很好。这里主要讲述分区问题，个人比较建议使用专家模式进行分区。虽然官方推荐他默认的分区模式，但是对于一个成熟的Linux玩家，熟悉分区应当是基本操作。虽然专家分区名为专家，但其还是相当傻瓜的，甚至都不用手动选择挂载。这里我们选择专家模式后，他还会提供两个选项。一个是基于OpenSUSE提供默认设置来分区，一个是基于原本的分区来配置。如果要安装双系统且已经分好空闲容量建议后者。专家模式外观大致如下：虽然我在此较为详细讲述分区，但其实分区也没什么好说的，基本是二大二小的组合。二大指的是系统分区和数据分区，二小指的是EFI分区和交换分区。通常而言，建议分区顺序为EFI分区、系统分区、数据分区、交换分区，这样较为方便后续后悔时调整（虽然出现需求时大概率还得重装）。EFI分区：通常默认512MB系统分区：建议比数据盘大，因为软件基本都装在系统盘数据分区：个人喜好分配即可交换分区：建议与内存同等大小或偏大对于这四种分区，OpenSUSE都提供了相关的选项，对于玩家来说只需要选择文件系统类型，如：Brfs、etx4等，和设置分区标签（分区名字）。详细步骤：先删除所有分区EFI分区除外，再新建分区，根据上面的说明和个人需求及喜好分配硬盘空间，设置文件系统，设置分区标签。OpenSUSE的初步设置设置Wallet密码Wallet是KDE的一个组件，负责管理所有的密码和系统操作的授权。刚进入系统后，Wallet便会请求设置密码，选第一项后，设置好密码即可。中文支持由于在本博客中，默认使用英语作为第一语言，所以OpenSUSE不会安装中文字体，这会导致一些显示的问题。因此我们先要打开System Setting - language，在添加第二个语言，他便会自动下载中文字体并安装。重启后便可正常显示中文字体。换源OpenSUSE的换源相对麻烦，如果能正常更新其实不建议换。并且由于OpenSUSE的包管理器，到现在仍然是单线程下载，换源对速度的提升也不大。如果实在要换源，请参考清华源的相关说明，注意OpenSUSE的版本。输入法与键盘布局在Linux中，输入法一般是由输入框架（如：ibus、fcitx）、前端插件、用户界面和输入法引擎（如：rime）等组成；在输入法引擎中又可以包含许多输入方案，比如：雾凇输入法、幸运草输入法等。详细可参考fcitx官方wiki。在Linux中，ibus适配gnome更好一些，fcitx适配KDE更好一些。我个人是使用KDE作为桌面系统（废话，谁用OpenSUSE还不用KDE啊），并且fcitx5对fcitx进行了一些改进，因此我使用fcitx5 作为基本的输入法框架。而使用Rime，则是因为他是Linux最好的中文输入引擎，我觉得任何人应该都不能忍受默认自带的傻瓜中文输入引擎。雾凇输入法则是目前Github最受欢迎的中文输入方案。输入法框架与引擎由于不同输入框架会引起未知的冲突，因此我们先将OpenSUSE的输入框架卸载。sudo zypper remove ibus fcitx然后我们再安装fcitx5和rime的输入引擎。sudo zypper install fcitx5 fcitx5-rime雾凇输入法的安装对于雾凇输入法的安装其实相对简单，但是有大佬写了自动安装和自动更新脚本，那我们大可不必费力去折腾。先保证OpenSUSE中安装有Git和Ruby。sudo zypper install git ruby再在你喜欢的位置克隆脚本。git clone --depth=1 https:\u002F\u002Fgithub.com\u002FMark24Code\u002Frime-auto-deploy.git --branch latest进入项目目录。cd rime-auto-deploy执行自动化脚本。.\u002Finstaller.rb选择fcitx5框架，再选择自动安装模式，启动fcitx5，便可发现已完成雾凇输入法的安装。输入法相关设置到此可能很多人就觉得完成了，但实际上并没有，你会发现每次重启都需要手动启动fcitx5，并且有些软件无法使用输入法。这主要是由于一些软件十分固执，始终不支持新的前端插件。因此我们需要进行进一步的设置，值得注意的是wayland与x11显示管理的设置并不相同，请以官方wiki为准。在OpenSUSE中，默认使用x11，因此我们有许多方法可以进行设置。在这里我仅使用一种方法。先在home新建.xprofile文件，并编辑（这里我用vim，不懂使用的可以使用其他文本编辑器）。vim ~\u002F.xprofile向该文件添加以下内容，强制软件使用fcitx5框架，这里填fcitx主要为了兼容性。XMODIFIERS=@im=fcitx GTK_IM_MODULE=fcitx QT_IM_MODULE=fcitx重启后即可正常使用。输入法皮肤可以自行搜索皮肤主题安装，再通过fcitx的设置的ClassicUI进行皮肤和字体的相关设置。键盘映射的修复如果你是键圈的人，你便会发现，自己的键盘似乎每次重启都需要开启数字锁定，并且键盘上方的F区（功能键）变成了媒体键，这很令人烦躁。数字锁定这个问题，只需在设置里的input - keyboard中设置一下便可完成。功能键映射修复也许有些人喜欢媒体键更甚一点，但也有人不喜欢媒体键，我个人便是其中之一。这主要是由于目前的键圈大部分都用的是开源的方案，而Linux并没有对此方案进行适配。对于此情况的解决方案ArchLinux的Wiki有更加详细的解释，在此仅提供解决方法。echo 2 | sudo tee \u002Fsys\u002Fmodule\u002Fhid_apple\u002Fparameters\u002Ffnmode echo \"options hid_apple fnmode=2\" | sudo tee \u002Fetc\u002Fmodprobe.d\u002Fhid_apple.conf基本软件的安装在Linux中，很多软件并不提供rpm包（其实只是大部分国产软件不提供），并且对不同发行版的依赖问题也没有很好的处理。为解决不同发行版的软件包的不相互兼容的问题，一些公司和开发者提出自己的解决方案，比较出名的有：appimage、snap、flatpak。其中appimage就是将软件及其依赖直接打包为可执行文件，snap和flatpak都是运用容器化原理安装软件及其依赖。但这些解决方案都意味着每个软件将会比rpm所占空间更大，因为他们会把依赖也带入软件中（其实也没多大事，Windows不就一直这样吗）。但由于snap的限制较大且不开源，只能从官方源安装软件，下载速度相当慢，因此我个人不是很喜欢snap。考虑到KDE对flatpak 的支持也相对较好，因此在本博客中，对于不提供rpm的软件将会使用flatpak和appimage来代替，最不得已才考虑编译。Flatpak初始化检查系统是否已经安装Flatpak。sudo zypper install flatpak为Flatpak添加官方的Flathub源flatpak remote-add --if-not-exists flathub https:\u002F\u002Fdl.flathub.org\u002Frepo\u002Fflathub.flatpakrepoQQQQ官方实际上提供了rpm包，但官方很明显没有很好考虑不同发行版的依赖问题，如果OpenSUSE直接安装会报错。与此同时，本地安装的rpm包还意味着无法通过包管理器进行自动更新。因此我们考虑使用flatpak安装QQ。flatpak install flathub com.qq.QQ微信你在想屁吃呢？懂不懂微信团队的含金量啊。QQ桌面端三人团队，重写外观，跨平台支持；百人微信团队，屁都没有，究极摆烂。腾讯会议flatpak install flathub com.tencent.wemeet百度网盘百度说实话，能别装就别装，真的无语。别看官方提供了rpm和deb的软件包，但如果你使用的系统更新的较为频繁，那这玩意基本各种不兼容，安装了也是白装，而且一直没改。因此对于这种希望让用户擦屁股的厂商，我们完全不用客气。flatpak install flathub com.baidu.NetDiskEdge其实我本来不想用Edge的，但Firefox的多端同步，我不知道怎么回事一直无法正常同步；而且Firefox的插件还是太少了，无法满足我的正常需求。只能被迫使用Edge + Firefox。在这里我们导入微软的RPM签名并添加Edge的官方源，刷新一下获取软件列表后，就可以使用包管理正常更新和下载了。这里我安装的是正式版，当然你也可以安装Edge的测试版。sudo rpm --import https:\u002F\u002Fpackages.microsoft.com\u002Fkeys\u002Fmicrosoft.asc sudo zypper ar https:\u002F\u002Fpackages.microsoft.com\u002Fyumrepos\u002Fedge microsoft-edge sudo zypper refresh sudo zypper install microsoft-edge-stable",[72],"Linux",{"slug":30,"title":31,"description":32,"plainText":74,"keywords":75},"Introduction——前言>typora写作是很舒服，但是到了图片上传我简直太难了。上一篇博客我说我图床用的是imgchr，现在我屈服了，玩博客用imgchr简直是魔鬼好吧，图片上传完了，居然是乱序上传，导致我很难改图片地址。又因为imgchr每小时30张（根据服务器负载情况调整，大部分图床都有限制），而我图片都是一次全传完，所以就免不了被限制。 > >所以后来我就找了个法子——typora + picgo，自动上传图片，这样就可以避免图片都是同一时间传，本来我是想自己开发picgo + imgchr 的插件的，研究了半天，发现imgchr没有api，这就很尴尬了，所以最后只能选择SM.MS作为博客的主要图床了。 > >——2020\u002F04\u002F18时过境迁，物是人非，没想到没过几年这个教程就有些过时了；也没想到 smms 那么快就被墙了；更没想到 Typora 居然收费了。由于学业停更两年的我，终于又回归了。我在原来教程的基础上增加了Obsidian的设置，以及smms被墙的一个解决方案，其实这也是我重新开始所遇到的问题，希望对后来者有一定的帮助吧。Preparation——前期准备Obsidian新晋之星，Markdown 编辑器的清流。对个人不收费，但对团队收费。他对于多文档管理非常友好，但也因此对单文档异常不友好，必须新建库，才能编辑文件。但由于其携带大量插件，使得功能极大丰富，写作的舒适程度也不亚于 Typora ，也是一款能与传统笔记本界对抗的笔记软件。其特色功能——双向连接，使其具有 Notion 的风味。用来做大型教程或者官方文档翻译十分顺畅。Typora这个就不用多说了吧，Markdown写作神器，无需知道任何 Markdown 语法也能用的 Markdown 编辑器。与Obsidian较大的差别可能是付费且无双向连接，不太适用多个文件管理，但对于单个文件的处理较好。Node.jspicgo有两个版本，一个是CLI版本一个是GUI版本的(CLI版本即PicGo-Core，GUI版本即PicGo.app)。而我使用的是CLI版本的，也就是命令行版本的，可以减少常驻内存的占用。注意：根据Picgo官方文档的描述，要求Node.js 版本 >= 8。“##### PicGo.app和PicGo-Core之间的区别（命令行）”>“目前，PicGo（应用程序）仅中文。 - PicGo.app提供了一个GUI，因此与CLI版本相比更易于设置。 - 使用PicGo-Core（命令行）进行上传会消耗较少的计算资源，因为该过程仅在上传过程中运行，并且在上传成功或失败后将退出。通过PicGo.app上传时，PicGo.app将始终保持运行状态，不会自动退出。此外，PicGo.app是电子应用程序，它消耗了更多的计算资源。 - PicGo.app和PicGo-Core使用不同的配置文件，但是您可以将picBedPicGo.app的配置文件中key 下的json对象复制到PicGo的配置文件中。 - PicGo.app提供其他功能，例如上传历史记录，自动重命名等。”>“——typora官网文档”Picgopicgo-core以下简称picgo。安装在任意目录打开Git Bash或者cmd等等，输入命令安装。yarn global add picgo ## 或者 npm install picgo -g觉得安装慢的，我上一篇博客也有讲到，将 npm 切换到淘宝源或其他可用源即可。检验picgo -h ## 或者 picgo -v输出如图所示即为安装成功。“PicGo 本体支持如下图床：”>“ 微博图床 v1.0 微博图床从 2019 年 4 月开始进行防盗链，不建议继续使用 - 七牛图床 v1.0 - 腾讯云 COS v4\\v5版本 v1.1 & v1.5.0 - 又拍云 v1.2.0 - GitHub v1.5.0 - SM.MS v1.5.1 由于官方不再支持V1版本，暂时请使用smms-user插件 - 阿里云 OSS v1.6.0 - Imgur v1.6.0”>“本体不再增加默认的图床支持。你可以自行开发第三方图床插件。详见 PicGo-Core。”>“——PicGo”“为 PicGo 开发的一款插件，新增了SM.MS注册用户 图床。 使用SM.MS V2的API上传，适用于注册了SM.MS的用户。填写Authorization即可”>“——smms-user作者”由于 Picgo 本体不支持 SMMS API v2，因此我们得自行安装一个插件来解决此问题。根据PicGo文档的描述，我们得用picgo install [name]来安装插件，其中 [name] 代表的是插件名注意：picgo的插件普遍以picgo-plugin-[name]来命名，但安装插件时[name] 不是 picgo-plugin-[name]，它不需要前面的那一部分。上面也说了，本体自带的smms不能用了，想要用SM.MS图床就得安装smms-user插件。安装smms-user插件，只需执行picgo install smms-user安装即可。安装完后，默认是开启插件的，无需另外操作。SM.MS所以我们需要一个SM.MS的密钥。可以百度搜索，或直接进入SM.MS的官网，注册一个账号。登录后，进入个人的设置页面。进入API Token页面，生成密钥，新注册的是没有密钥的。生成完后就会出现在Secret Token里面。Setting——配置Picgo 配置方法一如果不想用 SMMS 可以通过 picgo use 切换图床，在此不详细说明。在命令行中输入 picgo set uploader 配置 SMMS API Token。上下方向键选择，回车确认，再根据提示填写密钥即可。不会，多尝试，大不了Ctrl + C 中断，再来一遍。Picgo 配置方法二在个人用户的文件夹进入 .picgo 文件夹，打开 config.json 进行配置。修改如下，注意将Authorization的参数换为SM.MS的密钥，即将Secret Token替换为密钥。{ \"picBed\": { \"current\": \"smms-user\", \"uploader\": \"smms-user\", \"transformer\": \"path\", \"smms-user\": { \"Authorization\": \"Secret Token\" } }, \"picgoPlugins\": { \"picgo-plugin-smms-user\": true } }关于 SMMS 被墙的解决方法由于某种不可抗力，sm.ms 目前被墙。但作者后来上架了备用域名，即 smms.app 。插件默认是使用备用域名的，但如果仍无法正常上传，那你可能需要修改一下 picgo 的配置文件。打开 picgo 的配置文件。搜索 sm.ms ，将网址更换为 smms.app 即可。Obsidian 的配置Obsidian 并没有自带图片上传的功能，我们需要自行去社区下载。打开 Obsidian 的设置，关闭安全模式，即可打开社区插件市场。如果你无法正常浏览插件，那么你可以安装ProxyGithub插件实现正常访问。GitHub 打不开也可以用 Gitee 。搜索 Image Auto Upload Plugin 安装插件，当然你也可以像 ProxyGithub 那样手动安装。最后将插件的默认上传器修改为 Picgo-Core 即可，如果你并非默认目录安装 Picgo ，那请记得修改路径。Typora打开typora，进入偏好设置。进入图像选项，将 插入图片时… 更改为上传图片，勾选对本地位置的图片应用上述规则、 插入时自动转义图片URL 。至于第二项我不推荐勾选，因为当你写博客时，想将上传好的图片更换位置，一复制一粘贴，它又给你上传了一遍…当然，如果你想用我也不会阻拦。“If you have “node” and “picgo” installed in system PATH directly, you could also fill “picgo upload” as the custom command directly.”>“——typora官方文档”根据官方文档的描述：因为我不是使用typora内置下载的picgo CLI版，所以我们得自定义命令。我们需要将上传服务改为Custom Command，然后在自定义命令里输入picgo upload 。至此所有的配置都完成了，之后就可以愉快的使用了。Usage——使用官方文档其实是有说明如何使用的，不过我还是演示一下吧。截图截图粘贴需要手动上传，当然啦这可能是为了避免我之前说描述的情况吧。拖拽拖拽就不用手动上传了，他会自动上传",[67,76],"Obsidian",{"slug":26,"title":27,"description":28,"plainText":78,"keywords":79},"Windows + Deepin 单硬盘双系统的安装与使用说实话我受够了Windows的环境搭建，想要精简就极度繁琐，想要方便就得用宇宙IDE，占十几二十多GB存储。因此，Linux就是最好的选择，除非你需要写Win或Mac的软件等特定情况。那为什么选择 Deepin ？首先因为它可以较为方便的下载和安装 Linux 软件，省去了较为麻烦的适配和编译过程。其次是因为它比较符合 Windows 的使用习惯，UI风格偏向于Mac，较为美观。我觉得作为一个初学者，Deepin 确实算是一个较为不错的选择。至于为何不用虚拟机。它虽然安全且方便，但由于性能的限制，会导致运行起来时不时卡顿，这实在谈不上是一个良好的体验。且似乎会导致物理本机性能下降（存在争议），因而不考虑用虚拟机。说是双系统，其实就是装系统，只不过多了一些步骤罢了。关于单硬盘双系统与多硬盘多系统。值得说明的是硬盘不等于磁盘，硬盘可以被分为多个磁盘，这个过程也就是所谓的分区。硬盘是物理上的，磁盘是硬盘被虚拟地划为多个区域，分别进行管理。多硬盘多系统指的是将多个系统装在大于等于两个硬盘内。系统的启动需要引导程序，每个系统都会有一个引导程序。一般而言，引导程序能够检测到同一硬盘下的所有系统，而难以检测到不同硬盘下的系统，因而多硬盘多系统需要额外的配置。（如果感兴趣可以自行搜索双硬盘双系统引导）准备相关物品及文件U盘对于速度无要求，但容量至少8GB以上，以保证能完成启动盘的制作。Deepin 的系统镜像Deepin 官网下载系统镜像。如果你不介意 bug 和生态，也可以下载预览版。Deepin 启动盘制作工具同样在Deepin 官网下载启动盘制作工具。启动盘的制作选择系统镜像。选择U盘和格式化，注意备份。然后就可以制作启动盘。系统盘分区使用分区助手对硬盘进行分区。（也可使用其他分区软件）具体容量建议为 32GB+32GB+8GB 以上。其中 32G 为系统盘，另外的32GB是资料盘，8G 为交换分区。交换分区是 Linux 的虚拟内存，一般容量与内存相同，内存 16G 以上可不设，但有交换分区可以使用休眠功能，可加快 Linux 启动速度，这取决于你用不用休眠。可以先拆分出一个分区。再将该分区删除。即可得到一个空白分区。Bios 的设置插入启动盘，启动时按 Delete 进入 Bios 界面。（不同主板有不同按键，请自行上网查询）进入启动界面，更改启动顺序，将启动盘启动顺序改为第一位，保存并重启即可正常进入启动盘安装 Deepin 。或者按 F12 手动选择启动盘启动。（不同主板按键不同）安装 DeepinDeepin 毕竟是国产系统，对中文支持比较完善，选择简体中文即可。硬盘分区部分选择手动安装。选择空白分区后，点击右方的编辑按钮。先建立一个 efi 引导分区。大小最低 300MB 即可。>其实不建立 Deepin 的引导分区也是可以的，但如果 Windows 的引导分区废了，那很可能两个系统都会无法启动。而且如果后续你还要安装 macOS 或者 Ubuntu ，可能会导致 Deepin 无法被引导程序发现。总之在国内互联网少有相关的资料的情况下，不建议这样做。建立一个交换分区。大小等同于内存大小。系统盘则分配 32GB，挂载在根目录，文件系统选择 ext4 。>其实剩下的都可以分配给系统盘，但我建议还是分一些给数据盘。这样系统盘崩溃后，不至于重装系统后，还要配置一次软件。剩下的分配给数据盘，挂载在 \u002Fhome 目录下。文件系统可以选择 ext4 或者其他，这里我想体验一下其他文件系统，因而选择 btrfs 。如果一切正常就点继续安装即可。但注意不要勾选集成NVIDIA闭源驱动，后面我们自己安装即可，这玩意存在问题。如果你不需要休眠功能，且内存也足够，并没有分配交换分区，则会出现提示，忽略即可。安装完成后，再次进入Bios，将硬盘启动顺序改为 Deepin 优先。初始化 Deepin重启后，在Grub界面等待几秒，自动进入 Deepin 。选择语言，简体中文，并勾选同意协议。键盘布局同样简体中文。时区选择北京。选择头像，输入用户名等信息。待其初始化完成后即可。Grub 美化可以在 gnome-look 上的 GRUB themes 上找到自己喜欢的主题。下载后根据说明进行安装即可。（基本就是运行脚本就可自动安装，除非你想自行安装）>如果不想切换终端工作目录，那就在主题目录右键打开终端。初识Linux的可以查看主题 README 给出的相关命令。如果无法下载，可以去寻找其 GitHub 镜像，常在标题处见。NVDIA驱动安装直接去官网上选择linux版下载即可。",[72],1789407123372]