<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>wuh.site - 博客订阅</title>
        <link>https://wuh.site</link>
        <description>吴尒红的个人博客内容</description>
        <lastBuildDate>Wed, 14 Oct 2026 16:53:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-cn</language>
        <copyright>© 2021–2026 wuh.site</copyright>
        <item>
            <title><![CDATA[Vibe Coding Workflow 概述]]></title>
            <link>https://wuh.site/post/172</link>
            <guid>https://wuh.site/post/172</guid>
            <pubDate>Wed, 07 Oct 2026 13:34:29 GMT</pubDate>
            <description><![CDATA[重点不是让 AI 更快写代码，而是让它在正确的阶段做正确的事、失败时停得住——一套包含阶段边界、启发式探索、渐进式披露、信号机制、自动迭代与基建的团队工作流。]]></description>
            <content:encoded><![CDATA[Vibe Coding Workflow 概述 在今年 4 月底我开始正式接触和使用 AI，最开始使用的 chatgpt 和 opus，使用的方式也很简单一问一答，跟 AI 一轮一轮地对话，让 AI 执行，然后人工介入验收代码和代码。 慢慢地我们开始发现，有点不太对劲。在团队开发的过程中，每一个人可能会遇到相同的需求，但是每一个的提示词不同，使用的大模型不同，使用的智能体不同，得出的结果差异性特别的大，所以迫切地需要一个统一的流程性的内容，来限制或者是引导 AI 的探索与开发。 让 AI 不要无限制的、无边界的、无目的性的，非常随意的开发模式，这一套内容就是Vibe Coding 的 workflow 工作流。 接下来，我会给大家详细介绍，我给我们的团队准备的 workflow，以及其中遇到的问题和解决思路，希望可以解决大家的一些问题。 首先解决第一个问题，为什么我们需要工作流? 工作流存在的意义? 六个阶段，一条流水线 不要让AI更快写代码，应该是让 AI 先明确边界。如果我们是做的团队使用的工作流，那么还需要赋予更多的限制条件，需要明确的是以下两点: 1. 它是不是在正确的阶段做正确的事 2. 它失败之后会不会继续乱做 如果这两个问题不解决，AI 越快，返工也越快。所以工作流存在的意义不是当加速器，而是给 AI 装上边界和刹车。 边界：把协作流程拆成阶段 我把整个 AI 协作流程拆成了 6 个阶段： 拆阶段不是为了显得流程完整，而是给 AI 一个明确边界。每个阶段只回答该回答的问题，不让 AI 提前越界: proposal 只回答 Why 和 What：需求为什么存在、用户真正想要什么、影响什么范围。最怕它在这个阶段过早进入实现思维，明明还在讨论业务目标，它已经开始说改哪个组件、字段放哪一列。一旦过早进入实现，AI 很容易把“推断”包装成“事实”。 discuss 才回答 How：找入口、看引用链、找相似实现、定影响范围，确认哪些文件可以动、哪些不该动，最后沉淀成执行契约。很多 AI 失控不是发生在写代码的时候，而是 discuss 阶段边界没收住。 apply 只执行，不重新设计：只做一件事，消费 discuss 交棒下来的执行契约。一旦允许它重新设计，几乎一定会扩大范围、偷偷改设计、顺手补它觉得也该改的东西。契约不完整，正确动作是回到 discuss 补齐，而不是继续猜。 review 验证的不是“能不能跑”，而是“有没有按约定做”：需求是否满足、设计是否按约定实现、改动有没有超出宣称范围、上游交棒信息有没有被绕过。review 真正防的就是 apply 阶段偷偷扩大范围。 archive 和 commit 把一次执行变成下次的资产：沉淀业务规则、代码定位路径、归档摘要，确认代码、文档、知识都到位，让下一次 AI 不用从零开始猜。 刹车：治理低能力智能体的失败行为 阶段拆好之后，还要解决另一个问题。我后来越来越确定： 低能力智能体最危险的，不是不会做，而是失败之后还在继续做。 第一次失败之后，它不知道为什么失败、不会停、换个参数继续试、换个工具继续试，最后把上下文越搞越乱。这种行为看起来像“很努力”，本质上是在制造噪音，是工作流里最大的噪音源。 它的问题不在理解，而在控制。很多时候它不是“不知道规则”，而是不知道什么时候该停，把所有失败都理解成“再试一次”。所以不要指望它自己判断什么时候该停，必须替它定义清楚： 可以重试：网络超时、临时连接失败、服务端 500 不能重试：参数错误、权限错误、业务状态错误、调错工具、目标不存在 熔断规则：同一工具 + 同一参数失败 2 次即停止；调错工具立即停止 再配合工具白名单、禁用工具清单、结构化步骤、状态回写、错误分类这些机制，比“多提醒几句就会变乖”有效得多。 启发式探索 启发式探索，先回答三个问题 大家一定也使用过类似于 superpowers/openspec 等等工作流，在使用这些工作流的时候，可能会遇到以下问题: 1. 无限制的读取源代码，如果当前文件处于文件树的最底层，它会读完所有的相关依赖的文件，会拉爆你的上下文 2. 拉爆上下文之后，智能体会自动进行Compact指令，压缩之后极有可能会导致 AI 幻觉 3. 查看 AI 的思考链路之后，发现它读取了很多无效的文件，有很多文件明明不相关，但是它还是会读 那么这些问题都体现了一个关键点，AI 可能会自由读取很多无意义文件。 有些需求，读两个文件就可以做出来，但是很不巧，源代码里面使用了一个超大的公共组件。这个时候 AI 会去读取这个超大的组件来了解它的用法，然后撑爆了你的上下文。 最后，做出来的效果，写出来的代码，不太尽如人意。你就会吐槽，垃圾大模型，垃圾智能体。 现在，我们来复盘一下，AI 频繁读取源代码/文件是一件合理的事情吗? 如果你的团队成员有 10 个人，每一个人都负责一块业务，他们肯定会有交叉的部分。当我们使用 AI 来探索需求的时候，对于交叉的部分需要每一个人都进行探索吗 大家一定使用过类似于codex/cc/trae/codebuddy 等等智能体或者是IDE，在使用的过程中一定也总结了很多 AI 经常犯的错误，将其升级沉淀到了 rules 文件中。 慢慢地，你发现 rules 文件太多了，内容太长了，已经开始占用AI 的 Context 了。过多的 rules 甚至已经开始影响 AI 的思考链路了，导致 AI 的推导结果相差甚远，token 使用量直线上升，推导时长过长的问题。 那就有意思了，添加 rules 文件是否合理? 或者是 哪些内容可以升级到 rules 文件里面去? 下面给大家介绍一下，我是怎么解决这些问题的，我们在做自己的工作流的时候一定要注意一个关键词启发式探索。 工作流给 AI 指明方向 启发式探索的核心，不是放开手让 AI 去读，而是先回答三个问题：从哪里开始、按什么路径读、什么时候停。 起点：从结构化线索出发，而不是从文件树出发。 需求进来后，先由上游工具把需求内容解析成分层信息（我们用 zentao-fetch 解析禅道需求）： facts：需求原文明确要求的事实 inferred：从截图、菜单、接口名推断出来的信息 technicalHints：菜单关键词、接口关键词、可能涉及的应用 openQuestions：还需要人确认的问题 discuss 阶段的代码探索就从这些线索出发：根据菜单、路由、接口、组件名、截图文本去定位入口文件，而不是从根目录开始盲扫。另外 facts 和 inferred 必须分开记录，AI 不允许把推断包装成事实。 路径：每个阶段只做该阶段的事。 | 行为 | proposal | discuss | |---|---|---| | 判断业务域、识别疑似应用 | 允许 | 允许 | | 定位入口、追溯引用链、对比相似实现 | 不建议 | 职责所在 | | 深读组件实现 | 不建议 | 有目的地读，挂到参考文件清单 | | 生成文件级任务清单 | 不允许 | 输出 tasks.md | proposal 阶段只允许轻量定位，输出口径也只能写“疑似涉及某仓库”，不能写“必须编辑某某 Vue 文件”。代码探索的主战场在 discuss，而且每个阶段消费的都是上游已经结构化好的结论，不用从头再猜一遍。 终点：探索有明确的停止条件。 已定位入口、引用链、改动边界 → 停止探索，进入设计 同一问题搜索超过 2 次仍未定位 → 停止，向用户确认 出现“可能 / 应该 / 通常”这类不确定判断 → 停止，标记为 unresolved 发现范围会扩大到新应用 → 停下来更新影响范围矩阵，向用户确认 这四条就是“启发式探索”和“自由探索”的分界线：探索是一个有目标的推理过程，目标达成了就停，而不是把目录树读完。 给 AI 提供有效的上下文 方向指明之后，下一个问题是：不让它乱读源码，那 AI 靠什么理解代码库？答案是靠工作流准备好的四类有效上下文。 1. 结构化事实源 .openspec.yaml：跨阶段唯一的交棒文件，记录需求事实、技术线索、影响范围矩阵、执行契约。每个阶段的 AI 消费的都是前序阶段已经结构化好的数据，不需要重新分析、重新猜。 2. 带理由的参考文件：discuss 阶段读的每一个文件，都必须记录四件事——路径、角色、参考目的、读取原因。角色是个固定枚举：entrypoint（入口）、pattern-reference（模式参考）、implementation-reference（实现参考）、dependency-reference（依赖确认）、negative-reference（确认不采用的反例）、risk-reference（风险验证）。读完还要写结论。这份清单同时也是给用户看的：你可以直接打开这几个文件，快速确认 AI 为什么这样设计。 3. 组件使用场景沉淀：这就是开头“超大公共组件”问题的解法。组件的真实使用场景沉淀在组件库仓库里，每个组件目录下有一组 specs，一个场景一个 spec.md + example.vue。example.vue 只保留关键代码——关键 props、events、slots、配置对象，删掉无关业务字段、敏感信息和一次性常量。AI 读几十行提炼过的示例，比读几千行组件源码拿到的上下文质量更高。 4. 归档知识：每次需求结束，archive 阶段把业务规则、代码定位路径、可复用模式沉淀进 summary.md。第一个探索过的人把结论留下来，后面的人直接消费，不再重复探索。 回头看开头列的三个问题——上下文拉爆、Compact 后幻觉、读了一堆无效文件——本质都是同一件事：喂给 AI 的上下文无效。把上下文从“海量源码”换成“结构化事实 + 有理由的参考文件 + 提炼过的示例 + 前人沉淀”，问题就从根上解掉了。 为什么不许 AI 读源代码 先澄清：这不是一刀切的禁令，而是三条具体的禁令。 第一，不许漫无目的地读。 AI 每读一个文件前，必须先回答“为什么读它”，回答不了就不要读。回到开头的例子：需求本身读两个文件就能做出来，但 AI 为了搞懂那个超大公共组件的用法，把几千行的组件源码读了一遍，上下文直接撑爆。而在我们的工作流里，它该读的是沉淀好的组件使用场景示例——几十行，直接告诉你这个场景怎么配。读源码是手段，不是目的；AI 探索真正要拿的是三个结论：入口在哪、模式是什么、改动边界在哪。 第二，不许在错误的阶段读。 proposal 阶段深读组件实现、追踪调用链都是被禁止的，只允许判断业务域、粗略识别疑似应用。因为在这个阶段读源码，AI 会把“推断”包装成“事实”，后面整个设计链路就全歪了。代码探索只属于 discuss，而且要受停止条件约束。 第三，不许重复读别人已经读过的。 团队 10 个人负责的业务一定有交叉。同一段公共逻辑，不应该每个人用 AI 探索时都重新读一遍。 第一个人读完后沉淀成代码定位路径和组件使用场景，后面九个人直接消费结论。读源码这个动作，在整个团队的生命周期里，一个模式只该发生一次，甚至零次。 说到底，“不许读源代码”背后是换了一个工作模式：把 AI 从“每次都从源码里重新猜结论”，切换成“消费沉淀的结论，按需回到源码验证”。至于沉淀下来的知识哪些进 rules、哪些按需加载，就是下一节渐进式披露要回答的问题。 渐进式披露 上下文分四层，逐层披露 上一节结尾留了一个问题：沉淀下来的知识，哪些进 rules、哪些按需加载？ 在回答之前，先想清楚一个反直觉的结论：rules 文件膨胀的根源，不是你写进去的内容错了，而是所有知识都常驻在上下文里。无论这次需求是改表格还是改权限，AI 都背着同一整套 rules 在跑。知识本身没有错，错的是披露的时机。 所以渐进式披露要解决的就是这件事：把“一次性全量塞给 AI”，改成“分层、按需、在正确的时机披露”。 上下文分四层，越常驻的越少而稳定 我把给 AI 的信息分成四层： 1. 第一层：常驻规则（rules）。只放每一类任务都绕不开的最小集合——阶段边界、每个阶段回答什么问题、输出口径。这一层 100% 占用上下文，所以必须少而稳定。 2. 第二层：任务契约（按类型加载）。配置类、权限类这类步骤固定的任务，做成 YAML 执行契约，只有做这类任务时才加载对应契约。 3. 第三层：知识库（按需检索）。组件使用场景、代码定位路径、业务规则，平时不进上下文，探索碰到相关域时才去检索。 4. 第四层：源代码（最后兜底）。前三层都覆盖不了的时候，才回到源码验证。这也是上一节“不许读源代码”的完整含义：源码不是不能读，而是它排在披露顺序的最后。 YAML 契约的三层结构正好是这套思路的缩影： schema 定义字段和规则，几乎不变 template 定义标准执行步骤，按任务类型复用 instance 在具体需求里填真实参数，每次现场生成 AI 常驻的只有 schema 那一小段，模板按需取，实例现场填，新需求不用每次重新发明格式。 知识库的检索顺序，就是披露的路径 第三层要真正可用，光有知识还不够，还得有收敛的检索顺序。组件网关帮 AI 建立的正是这个： 每往下一层，进入上下文的信息都更精确：类型层只有几行，组件层多几行，到场景层才是具体案例。AI 的猜测空间随层级收敛。 要让这条路径稳定工作，有两个前提： 主归属规则：跨组件场景只保留一份，放在主导组件下面。否则按需加载时会同时加载到两份互相冲突的知识，反而制造噪音。 知识必须是提炼过的：spec.md 放模式，example.vue 只保留关键代码。按需加载进来的每一层，都应该是“几十行说清一件事”，而不是把业务包袱原文搬进来。 阶段本身就是一次渐进式披露 回头看工作流的 6 个阶段，其实时间维度上也在做同一件事： proposal 只披露 Why 和 What discuss 才披露 How、参考文件、影响范围 apply 只披露执行契约和任务清单 信息不是开局全给，而是随着阶段推进逐层披露。这也解释了 proposal 为什么不许读源码——源码是披露链的最底层，在错误的阶段读它，就是披露错位。 所以，什么该进 rules 回到开头的问题，判断标准就一句话： 每一类任务都绕不开的，进 rules；只有一类任务用得到的，进契约；只有具体场景用得到的，进知识库。 现在主流的智能体基本都支持 skill 这类机制：常驻上下文里只放一段简短的描述（什么时候触发、干什么用），正文按需加载。这正是渐进式披露的工程化落地。rules 文件不再膨胀的标志，不是你写得少了，而是大部分知识从一开始就不需要进 rules。 信号机制 把流程变成状态机 前面两节解决了“方向”和“知识”的问题，但还剩一根支柱：这些结构和策略，到底以什么形态传达给 AI？ 边界一节列过治理手段——白名单、状态回写、错误分类；刹车一节定义过策略——什么能重试、什么要熔断。但策略本身不会生效，它必须变成 AI 能直接消费的东西。我的答案是用信号 Signal：把控制信息从“自然语言里的叮嘱”，变成“结构里的字段”。 状态：把流程变成状态机 先看配置类任务最典型的问题。一个简单的修改动作，正常顺序是： 1. 找到目标对象 2. 查旧值 3. 预览变更 4. 确认 5. 提交修改 6. 同步 / 记录结果 交给低能力智能体，就很容易变成：没查旧值直接改、没预览 diff 就提交、失败后直接重试。这类任务最重要的不是“会不会改”，而是会不会按顺序改。 所以我把它做成 YAML 状态机： 每一步挂一个状态： 有了状态，AI 的任务就不再是“理解一整段复杂流程”，而是： 找到当前 pending 的那一步 执行 回写状态 遇到 blocked 停止 上一步不是 done，下一步就不会开始——跳步骤在结构上就不可能发生。 回写：让失败可以从断点恢复 状态写下来不是做样子的，关键在回写： 低能力智能体还有一个很明显的问题：失败之后，它通常不知道应该从哪一步重新开始。没有状态，它就会从头来过——重复调用、重复改动、重复制造噪音。有了状态，下一次就很明确：从 modify 这一步继续，或者直接被阻断。 这才是“可恢复执行”。同样的信号也作用在阶段层面：.openspec.yaml 里始终维护着 currentStage / lastCompletedStage / nextStage，哪怕跨了多轮对话，流程推进到哪一步依然是确定的。 枚举：用清单代替叮嘱 很多控制信息，写成自然语言是建议，写成结构就是信号。 在提示词里写“请不要调用 toolX”，低能力智能体经常视而不见；但写进契约里： 它就不是建议，而是契约。重试策略同理：retryPolicy 里写明网络错误最多重试 2 次、调错工具直接 block，比反复叮嘱“失败了不要乱试”有效得多。 影响范围矩阵也是同一件事——用 files.edit / readOnly / forbidden 三个清单，代替“大概会改某几个文件”；review 的四个检查项（需求满足、设计符合、范围未越界、任务完成），把验收从“看起来不错”变成逐项 passed / warning。 阻断：信号也要会说“停” 信号不只告诉 AI“做什么”，还要告诉它“停下来”。除了步骤级的 blocked，我们把阻断做成显式条件，比如 commit 阶段： | 条件 | 动作 | |---|---| | 有代码改动但无 summary.md | 阻断 | | 有新组件场景但未沉淀 | 阻断，或说明不沉淀原因 | | 契约状态与实际阶段不一致 | 阻断 | | 归档文档未纳入提交 | 阻断 | 探索阶段同理：出现“可能 / 应该 / 通常”这类不确定判断，就标记 unresolved，而不是带着模糊继续往下做。 交棒：信号在阶段之间流动 最后把视角拉高一层。阶段之间的信息传递，如果只是一段自然语言——“这次应该改表头配置，顺便同步一下”——那执行阶段必然重新理解：这属于哪类配置、用哪个工具、要不要先查旧值、同步前要不要预览。名义上进入了开发阶段，实际上它还在继续做设计。 所以我有一个很强的判断： 阶段边界不是靠名字划出来的，而是靠交棒契约锁出来的。 交棒契约要同时解决三件事： 1. 事实源必须明确：一个 menuId，要能看出它是查出来的还是猜的，能不能换 2. 执行边界必须明确：哪些文件可动、只读、禁止；哪些工具允许、禁止 3. 错误策略必须前置定义：什么可重试、什么必须阻断 交棒的本质不是记录信息，而是限制下一阶段的自由度——让下一阶段做的事，从“重新理解任务”变成“执行明确约束下的任务”。 不是所有任务都要信号化 总结一下信号机制的价值：它把 AI 的执行从“读懂一段话”变成“读一个字段”——复杂度从“隐含在 AI 脑子里”，变成“显式写在契约里”，执行因此变得可读、可追踪、可恢复、可熔断、可审查。 但它有明确的适用边界。适合信号化的任务：步骤固定、状态清晰、输入输出明确、失败成本高、容易误调工具；不适合的：开放式设计讨论、复杂代码重构、强依赖临场判断的问题。信号不是为了替代思考，而是为了约束那些本来就不该靠自由发挥完成的任务。 自动迭代 迭代的单位不是重试，是回写 刹车一节说过，低能力智能体失败后的“迭代”是换个参数、换个工具继续试——那是噪音。但消灭乱试，不等于不要迭代。工作流需要的是另一种迭代：每一次执行，都让系统本身变得更好一点。 两者的区别可以浓缩成一句话： 普通智能体的迭代单位是一次重试，工作流的迭代单位是一次回写。 具体有四条回路。 偏差回路：范围外变更不许悄悄改 apply 阶段如果发现要动一个不在契约里的文件，最危险的动作是“顺手改了”。工作流定义了标准回路： 1. 暂停实现 2. 说明新增文件的原因和所属应用 3. 更新影响范围矩阵 4. 更新 touchPolicy 5. 更新 design.md / tasks.md 6. 必要时请用户确认 7. 再继续实现 同时把这次偏差记入 deviations：动了哪个文件、为什么、矩阵和任务清单有没有同步更新、用户有没有确认。 这样一次偏差的结果，不是范围悄悄扩大，而是契约变得更完整——下个需求再遇到类似情况，契约里已经有答案了。 卡点回路：把模糊变成问题抛回给人 启发式探索那一节说过探索什么时候停，但停止只是迭代的一半，另一半是停了之后往哪走： 搜索 2 次仍未定位 → 停下来，向用户确认 出现不确定判断 → 标记 unresolved，带着清单回到用户面前 范围要扩大到新应用 → 先更新影响范围矩阵，再确认 AI 的迭代不等于自力更生。把卡点变成一个高质量的提问，也是一种迭代——它把“AI 瞎猜”换成了“人在关键节点做决策”。 委派回路：发现的副产物不丢 discuss 阶段经常发现一些“本次不做、但值得沉淀”的东西，比如报表页面的组件使用场景。工作流的做法是给一个明确的动作枚举： create-page-level-case：当前 agent 新增页面级使用案例 update-existing-case：更新已有案例 reused-existing-case：已有 specs 覆盖，只复用 delegate-to-other-ai：需要沉淀，但交给其他 AI 执行 not-needed：本次没有新增场景 关键是委派那一项：即使沉淀由其他 AI 异步执行，discuss 也必须把场景、来源文件、参考文件、目标 specs 目录记录清楚。副产物以任务的形式进入系统，而不是随着本次对话消失。 沉淀回路：知识库的自动更新 archive 阶段有一套固定的检查流程： 配套一条粒度标准，防止知识库被业务包袱撑爆： 沉淀：新的组件组合方式、新的 props/events/slots 使用模式 更新：老场景但示例已过时 不沉淀：纯业务字段差异、一次性页面逻辑、敏感数据 这样每次需求收尾，知识库就自动迭代一次。这正好回到第一节说的那句话：archive 和 commit 把一次执行变成下次的资产。 四条回路是同一件事 偏差、卡点、副产物、结论——自动迭代做的，就是把执行过程中产生的这四样东西，全部变成对系统的回写：契约更完整、问题回到人、任务不丢失、知识变资产。 普通智能体跑一百个需求，是一百次孤立的执行；工作流跑一百个需求，是一百次对同一套系统的迭代。跑得越久，差距越大。 基建任务 流程跑在什么上面 前面几节讲的都是流程怎么跑：边界怎么划、探索怎么引导、知识怎么披露、控制怎么变成信号、执行怎么回写。这一节换个视角：这些流程跑在什么上面？ 答案是跑在一组资产上。我把这类不直接产出业务功能、但决定 AI 下一次表现的工作，叫做基建任务。 基建不建，前面所有机制都会退化： 没有结构化事实源，交棒退化成自然语言 没有知识库，探索退化成读源码 没有统一入口，知识退化成离散资产 实际开发里，真正浪费时间的往往不是“少一个组件”，而是：已经有组件但不知道在哪、已经有用法但不知道怎么找、已经有类似实现但不知道谁是主归属。所以基建的第一件事，是先收敛发现路径，再谈复用效率。 基建资产有哪些 | 资产 | 形态 | 服务谁 | |---|---|---| | .openspec.yaml | 机器可读的结构化事实源，跨阶段渐进填充 | 各阶段的 AI 交棒 | | proposal.md / design.md / tasks.md | 人类可读的阶段产物 | 评审、验收、归档 | | summary.md | 归档摘要，固定结构含知识沉淀栏 | commit 检查、后续需求探索 | | 组件 specs（spec.md + example.vue） | 场景级知识库 | discuss 检索、后续需求 | 这张表里有一个刻意的设计：机器读的和人读的是分开的。.openspec.yaml 是给 AI 消费的结构化事实源；summary.md 是给人看的归档摘要，固定结构里必须包含知识沉淀栏——业务规则、代码定位、可复用模式、组件使用场景。一次需求的上下文，最终同时沉淀成“AI 下次的输入”和“人能复核的档案”。 原则一：能复用就不新增 做基建最容易犯的错，是给每个环节都发明一个新文件：proposal 交棒一个 proposal-handoff.md，discuss 再交棒一个 discuss-handoff.md……三个月后，没人说得出哪个文件是最新的事实源。 所以我们有一条硬原则：不新增额外交棒文件，复用现有制品。.openspec.yaml 一个文件从头用到尾，字段渐进填充，不要求 proposal 阶段一次写满。文件少，事实源才唯一。 原则二：分类按角色，不按业务 统一入口要求分类稳定。我们把前端资源按资源角色拆成五类：业务组件、布局、逻辑复用、全局服务、工具。 这个划分看起来普通，关键好处是：它不随业务震荡。按业务域拆目录，业务一调整入口就跟着晃，AI 的检索路径天天失效；按资源角色拆，业务再怎么变，入口结构是不动的。 原则三：页面级案例是最高价值的知识 组件 API 文档只回答“能做什么”，回答不了“通常怎么做”。真正反复出现的，从来不是某个 prop 怎么传，而是页面级模式： 汇总页 + 明细页 多 Tab 报表 表格列点击下钻 组织树九级下钻 动态表头切换 这些模式如果只躺在业务代码里，每个需求都要重新猜一次。所以页面级案例必须进知识库——但注意，不是整页代码搬运，而是提炼五件事：主导组件、页面层次、关键交互、参数方式、关联能力。知识库里存的是“模式”，不是“业务包袱”。 归属上遵守主归属规则：案例归到主导组件下面，弹窗、路由这些只作为关联出现，示例永远只维护一份。 执行层：一套 CLI，把约定变成机制 写到这里，资产齐了，但还有一个漏洞：文档解决的是“AI 知道约定”，解决不了“约定一定被执行”——AI 拿着 shell，随时可以绕过所有文档直接 git add .。 所以我们的 shadow-dev-workflow 搭配了一套 shadow-dev-cli，作为工作流的确定性执行层：skills 只负责编排与判断，brief、INDEX、Git、GitHub 的全部写操作都由 CLI 完成，AI 不直接执行写命令。CLI 独立成仓、锁版本分发，插件在会话启动时自动把锁版本安装到位，出问题可以一键回滚。 它用几个机制把“约定”升级成了“机制”： plan / execute 两段式：所有写操作先 plan 生成快照和哈希，再 execute 校验后落地；plan 之后任何相关状态变化都会让哈希失效，必须重新 plan。想跳过预览直接执行？返回的是错误码，不是警告。 写操作必须 --confirm：缺确认一律拒绝。执行权和确认权是分离的，AI 没有“顺手落地”这个选项。 输出是信号：每条命令输出单行 JSON，退出码分级——成功、校验错误、缺确认、外部错误、不支持的操作。AI 判断下一步靠读字段，不靠感觉。 硬约束写死在命令里：commit 只接受明确文件列表，git add .、git add -A、路径越界直接拒绝；任务未完成、review 未通过，提交和发布都会被机械门禁挡下。 熔断是默认行为：网络步骤失败立即停止并报告错误码，不换方式重试；修复后按断点续跑语义重新 plan + execute，已完成的步骤自动跳过。 回头看，前面几节的设计在这一层全部兑现：信号机制要的“读一个字段”、刹车要的“不换方式重试”、回写要的“可恢复执行”，在这里都是 CLI 的默认行为，不再依赖 AI 的自觉。 基建是流程里的显式任务，不是顺手的事 最后强调一点：以上这些不会自动发生。 它们必须被编排进工作流，变成显式任务——archive 阶段的组件场景检查流程、summary.md 的知识沉淀栏、commit 阶段“有新场景未沉淀就阻断提交”。自动迭代那一节的沉淀回路，最终落点就是这里的基建。 换句话说，基建不是流程之外的额外负担，而是流程内部的一等公民。这也是整套工作流的落脚点： 让 AI 更快，只能快一次；让系统和知识可积累，才能快每一次。 <!-- wuh-site-metadata: {"summary":"重点不是让 AI 更快写代码，而是让它在正确的阶段做正确的事、失败时停得住——一套包含阶段边界、启发式探索、渐进式披露、信号机制、自动迭代与基建的团队工作流。","cover":"https://cdn.wuh.site/2026-10/2026-10-07-122611.png","keywords":["Vibe Coding","AI 工作流","智能体","团队协作"]} -->]]></content:encoded>
            <enclosure url="https://cdn.wuh.site/2026-10/2026-10-07-122611.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[再读《坐忘歌》]]></title>
            <link>https://wuh.site/post/165</link>
            <guid>https://wuh.site/post/165</guid>
            <pubDate>Sun, 05 Jul 2026 12:32:22 GMT</pubDate>
            <description><![CDATA[再读《坐忘歌》 image-20260705202606322 常默元气不伤，少思慧烛内光。 不怒百神和畅，不恼心地清凉。 不求无谄无曲，不执可圆可方。 不贪便是富贵，不苟何惧公堂。 味绝灵泉自降，气定真息自长。 触则形毙神游，想则梦索尸僵。 气漏形归后土，念漏神归死乡。 心死方得神活，魄灭然后魂昌。 转物难穷妙理，应化不离真常。 至精潜于恍惚，大象混于渺茫。 造化若知规矩，鬼神莫测行藏。 节饮节]]></description>
            <content:encoded><![CDATA[再读《坐忘歌》 image-20260705202606322 常默元气不伤，少思慧烛内光。 不怒百神和畅，不恼心地清凉。 不求无谄无曲，不执可圆可方。 不贪便是富贵，不苟何惧公堂。 味绝灵泉自降，气定真息自长。 触则形毙神游，想则梦索尸僵。 气漏形归后土，念漏神归死乡。 心死方得神活，魄灭然后魂昌。 转物难穷妙理，应化不离真常。 至精潜于恍惚，大象混于渺茫。 造化若知规矩，鬼神莫测行藏。 节饮节食少寐，是谓真人坐忘。 -- 司马承祯 我们公司的一个同事，见面就给我吐槽压力很大，中年危机。我也不善言辞，不知道要怎么安慰他才好，只能跟他插诨打科，消解一下他的苦闷。 今天本来是给自己安排的游戏日，本想称着台风天在家里打一天游戏。不知道是不是他影响了我一下，早上7点醒过来，随口念了两句不怒百神和畅，不恼心地清凉。 可能是太长时间没有压力了，前后几句全给忘记了，所以只好临时改变了一下计划，今天还是读书日吧。 这首歌是一首道教的养气歌，司马承祯是唐代玄宗时期的著名道士，活了90多岁，非常难得。 同时，他还是李白的恩主，李白少年出川时遇到了他，后期的书法作品《上阳台帖》就是这两人的羁绊。 六戒 我还记得之前有一个玩笑，问: 如何让自己长命百岁，回答就是保持呼吸，不要断气。这个纯是瞎扯淡的，只能活跃气氛不能帮助他人。 我们来看一下大佬是如何劝人的，首先明确一点人家在几千年前活了90多岁，多少还是有点帮助的。 常默元气不伤，少思慧烛内光 这一段就是在劝我们不要胡说八道，胡思乱想，紊乱自己的磁场。后面人家就详细展开说了六种场景，给了我们一些参考。这首歌里面没有生僻字，跟白居易的风格一样，所见即所得，几乎没有理解难度。 人生一世不过3万多天，实在是太短了。 欲望，是最恐怖的东西。孔夫子说要克己复礼，何也？他所推崇的周礼在春秋末已经彻底破产，没有半点市场了，尊王攘夷成了笑话了。他忽略了人的贪心，他认为可以使用礼法的教育达到克己的目的，但是环境已经不允许了。 春秋以来的义战已经转化为孙子兵法中的兵不厌诈了。为了胜利可以不择手段，我们都在笑宋襄公的半渡不击，却没想到他也只是一个不死的老兵，新时代的船上已经没有他的位置了。 环境在方方面面影响着我们，但是我们先思考一下，我们真正的需求有哪些？ 日不过三餐，夜不过一宿，四季的衣服8套已经足够了，出行除了在老家乡下，到处走都方便。我也不知道是不是我的需求太低，太容易满足，比起物质上的富足我更倾向于精神上的进步。 没了那么些乱七八糟的想法，我发现我的生活及其轻松。上班就好好上班，把自己的本质工作做好已经是非常好的了，很多人都做不好自己的工作。下班了就好好生活，给自己找一点乐子，玩游戏、写代码、找朋友聊天，玩累了就看看书、写写字。 此乐何极 六真 中间4句不用看，直接看后四句。里面非常唯物的提出了六个修真的方法，但是我只需要最后一个。 节饮节食少寐，是谓真人坐忘 年龄越来越大了，代谢能力也在慢慢减弱，现在得控制一下自己的体重，30来岁的人是不可以太胖的，那样形象非常不好，尤其是不能有小肚子。 节饮节食的意思并不是说让你少吃少喝，而是节制自己，饭吃八分饱，保留一点点饥饿感最好。 少寐的意思不是让你少睡觉，人家睡觉的时间是6个时辰12个小时，我现在的睡眠质量特别的差，也不知道为什么，1点睡去7点醒来。有的时候睡到8点，那简直是烧高香了，所以每天都是晕晕乎乎，身体素质贼差，很容易感冒。 <!-- wuh-site-metadata: {"cover":"https://cdn.wuh.site/2026-07/1783254259449download.png"} -->]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[重启计划]]></title>
            <link>https://wuh.site/post/155</link>
            <guid>https://wuh.site/post/155</guid>
            <pubDate>Sun, 21 Jun 2026 14:56:38 GMT</pubDate>
            <description><![CDATA[这两天重新读了一下过去写过的博客，才发现我已经很久没有继续写博客了。现在都是在公司内部分享一些技术知识，而且年度总结也已经很久没有写了，这样不好。 继续学习阮老师, 每周发布一篇博客, 无限进步，开始重启计划~~]]></description>
            <content:encoded><![CDATA[这两天重新读了一下过去写过的博客，才发现我已经很久没有继续写博客了。现在都是在公司内部分享一些技术知识，而且年度总结也已经很久没有写了，这样不好。 继续学习阮老师, 每周发布一篇博客, 无限进步，开始重启计划~~]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[codex的开发范式]]></title>
            <link>https://wuh.site/post/135</link>
            <guid>https://wuh.site/post/135</guid>
            <pubDate>Sat, 07 Mar 2026 07:54:59 GMT</pubDate>
            <description><![CDATA[codex的开发范式 在使用 Vibe Coding 的过程中，Codex 应如何规范开发范式? 一句话：在 Vibe Coding 里，Codex 不应只是“写代码机器”，而应被规范成“按契约执行的工程代理（Engineering Agent）”。 --- 1. 先定“角色边界”：Codex 负责产出，人类负责决策 把职责分清楚，效率会高很多： 人类负责：需求优先级、架构取舍、风险接受、上线决策]]></description>
            <content:encoded><![CDATA[<h2>codex的开发范式</h2>
<p>在使用 Vibe Coding 的过程中，Codex 应如何规范开发范式?</p>
<p>一句话：<strong>在 Vibe Coding 里，Codex 不应只是“写代码机器”，而应被规范成“按契约执行的工程代理（Engineering Agent）”。</strong></p>
<hr>
<h3>1. 先定“角色边界”：Codex 负责产出，人类负责决策</h3>
<p>把职责分清楚，效率会高很多：</p>
<ul>
<li><strong>人类负责</strong>：需求优先级、架构取舍、风险接受、上线决策</li>
<li><strong>Codex 负责</strong>：代码实现、重构建议、测试补全、文档更新、脚手架</li>
</ul>
<blockquote>
<p>原则：<strong>AI 可执行，关键决策必须可追溯到人。</strong></p>
</blockquote>
<hr>
<h3>2.用“任务契约”驱动每次生成（不要直接一句话让它开写）</h3>
<p>每个任务都给 Codex 一个固定输入模板（像 mini-PRD）：</p>
<ol>
<li><strong>目标</strong>（要解决什么问题）</li>
<li><strong>范围</strong>（做什么 / 不做什么）</li>
<li><strong>输入输出契约</strong>（API、类型、错误码）</li>
<li><strong>约束</strong>（性能、安全、兼容性、依赖限制）</li>
<li><strong>验收标准</strong>（可测试、可观测、可回滚）</li>
<li><strong>交付物</strong>（代码 + 测试 + 文档 + 迁移脚本）</li>
</ol>
<hr>
<h3>3. 强制“先设计后编码”</h3>
<p>要求 Codex 每次先给：</p>
<ul>
<li>方案摘要（2~3 种，含 trade-off）</li>
<li>选型理由</li>
<li>影响面清单（模块、接口、数据结构）</li>
<li>风险与回滚策略</li>
</ul>
<p>通过后再开始写代码。</p>
<blockquote>
<p>这一步能显著减少“看起来能跑但不可维护”的代码。</p>
</blockquote>
<hr>
<h3>4. 开发流程标准化（建议 6 步）</h3>
<ol>
<li><strong>Plan</strong>：Codex 输出实施计划（文件级变更列表）</li>
<li><strong>Scaffold</strong>：先生成接口 / 类型 / 测试骨架</li>
<li><strong>Implement</strong>：分小步提交（每步可运行）</li>
<li><strong>Verify</strong>：单测、集成测试、lint、类型检查</li>
<li><strong>Explain</strong>：生成变更说明 + 风险点 + 使用示例</li>
<li><strong>Review Gate</strong>：人类审批后合并</li>
</ol>
<hr>
<h3>5. 代码质量“硬门槛”（不可协商）</h3>
<p>给 Codex 明确以下 gate，没过就不算完成：</p>
<ul>
<li>✅ <code class="notranslate">lint</code> / <code class="notranslate">format</code> 全绿</li>
<li>✅ 类型检查全绿（TS / pyright / mypy 等）</li>
<li>✅ 关键路径测试覆盖达标</li>
<li>✅ 无高危安全问题（注入、密钥泄漏、越权）</li>
<li>✅ 性能回归可解释（基线对比）</li>
</ul>
<hr>
<h3>6. 提示词（Prompt）规范：固定结构，减少随机性</h3>
<p>建议团队统一 Prompt 模板：</p>
<ul>
<li>背景与目标</li>
<li>当前代码上下文</li>
<li>技术约束</li>
<li>输出格式（必须含：代码、测试、迁移、文档）</li>
<li>禁止事项（如“禁止修改公共接口”“禁止引入新依赖”）</li>
<li>验收命令（例如 <code class="notranslate">npm test &amp;&amp; npm run lint</code>）</li>
</ul>
<hr>
<h3>7. 文档与可追溯性</h3>
<p>要求 Codex 每次变更同步输出：</p>
<ul>
<li><code class="notranslate">CHANGELOG</code> 条目</li>
<li>ADR（重要架构决策记录）</li>
<li>接口文档更新</li>
<li>回滚指南</li>
</ul>
<blockquote>
<p>没有文档的 AI 代码，长期一定会变成维护负担。</p>
</blockquote>
<hr>
<h3>8. 常见反模式（要避免）</h3>
<ul>
<li>❌ 让 Codex 一次性“重写整个模块”</li>
<li>❌ 没有测试就合并</li>
<li>❌ 只看“能跑”，不看边界条件和失败路径</li>
<li>❌ 让 AI 自行决定需求边界</li>
<li>❌ 不做 code review 就上线</li>
</ul>
<hr>
<h3>9.可直接执行的团队规则（简版）</h3>
<p>你可以把下面作为团队规范：</p>
<blockquote>
<p><strong>“所有 Codex 任务必须先有任务契约；所有生成代码必须附带测试与变更说明；所有高风险改动必须有人审；未通过质量 gate 禁止合并。”</strong></p>
</blockquote>
<hr>
<p>如果你愿意，我可以进一步给你一份<strong>可直接粘贴到团队 Wiki 的《Vibe Coding + Codex 开发规范模板》</strong>（包含 Prompt 模板、PR 模板、验收清单）。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[docker配置镜像源]]></title>
            <link>https://wuh.site/post/121</link>
            <guid>https://wuh.site/post/121</guid>
            <pubDate>Sun, 20 Oct 2024 09:23:54 GMT</pubDate>
            <description><![CDATA[之前用的Docker Desktop 这个工具可以直接在面板里面改，现在换成了podman，面板里面没有位置可以编辑了，在网上找了一些方法来解决问题。 podman 这里有一个前提，我刚开始看的时候没有留意，导致查了好多资料，最后看大家都一样的写法，最后才发现了别人博客里面最开始就写了一句话: 如果/etc/docker/daemon.json这个文件有就直接改，如果没有就新增一个。 image-]]></description>
            <content:encoded><![CDATA[<p>之前用的Docker Desktop 这个工具可以直接在面板里面改，现在换成了podman，面板里面没有位置可以编辑了，在网上找了一些方法来解决问题。</p>
<h3>podman</h3>
<p>这里有一个前提，我刚开始看的时候没有留意，导致查了好多资料，最后看大家都一样的写法，最后才发现了别人博客里面最开始就写了一句话: <em>如果</em>*/etc/docker/daemon.json**这个文件有就直接改，如果没有就新增一个。*</p>
<div class="highlight highlight-source-json"><pre class="notranslate"><span class="pl-ii">// vim /etc/docker/daemon.json</span>

{
  <span class="pl-ent">"registry-mirrors"</span>: [
    <span class="pl-s"><span class="pl-pds">"</span>https://docker.m.daocloud.io<span class="pl-pds">"</span></span>
  ]
}</pre></div>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/74c7acdbd5064060760a4ef59c177ef905c0d1691d1c3475bf1be8925e42ffa6/68747470733a2f2f7372632e7775682e736974652f323032342d30392f323032342d31302d32302d3038313933392e706e67"><img src="https://camo.githubusercontent.com/74c7acdbd5064060760a4ef59c177ef905c0d1691d1c3475bf1be8925e42ffa6/68747470733a2f2f7372632e7775682e736974652f323032342d30392f323032342d31302d32302d3038313933392e706e67" alt="image-20241020161925696" data-canonical-src="https://src.wuh.site/2024-09/2024-10-20-081939.png" style="max-width: 100%;"></a></p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/1cc8c060b74caa2fecb308f94185c5bed84aef8724c14b9b2155499e3396af8d/68747470733a2f2f7372632e7775682e736974652f323032342d30392f323032342d31302d32302d3038323032302e706e67"><img src="https://camo.githubusercontent.com/1cc8c060b74caa2fecb308f94185c5bed84aef8724c14b9b2155499e3396af8d/68747470733a2f2f7372632e7775682e736974652f323032342d30392f323032342d31302d32302d3038323032302e706e67" alt="image-20241020162019965" data-canonical-src="https://src.wuh.site/2024-09/2024-10-20-082020.png" style="max-width: 100%;"></a></p>
<h3>docker run</h3>
<div class="highlight highlight-source-shell"><pre class="notranslate">docker run -d -P m.daocloud.io/docker.io/library/镜像名称

样例:
docker run -d -P m.daocloud.io/docker.io/library/mongo</pre></div>
<p>除此之外，还有另一种方法，直接用docker把容器跑起来，在镜像前面把代理的域名加上去就可以了。我就是没有把podman的代理问题解决，改用了这种方式，可以快速搞定。</p>
<p>在github上面找到了一个收集代理源[仓库<a href="https://github.com/DaoCloud/public-image-mirror">public-image-mirror</a>](<a href="https://github.com/DaoCloud/public-image-mirror">https://github.com/DaoCloud/public-image-mirror</a>)</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[使用vitepress搭建wiki]]></title>
            <link>https://wuh.site/post/92</link>
            <guid>https://wuh.site/post/92</guid>
            <pubDate>Sat, 13 Apr 2024 10:04:50 GMT</pubDate>
            <description><![CDATA[使用vitepress搭建wiki image-20240413153801040 image-20240413170409041 前面几个月一直在学习bebal、eslint这种AST相关的知识，一直没有时间去写一篇正儿八经的博客，刚好体验了一下vitepress，在这里记录一下我是如何使用vitepress将我的知识仓库blog做成wiki的。 image-20240413153919880 ]]></description>
            <content:encoded><![CDATA[<h2>使用vitepress搭建wiki</h2>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a692a8dc2ab12d3d9465167a6d52851f7f4d49ab4be75cdc1921dd50dbb3b43a/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3037333831322e706e67"><img src="https://camo.githubusercontent.com/a692a8dc2ab12d3d9465167a6d52851f7f4d49ab4be75cdc1921dd50dbb3b43a/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3037333831322e706e67" alt="image-20240413153801040" data-canonical-src="https://src.wuh.site/2024-04/2024-04-13-073812.png" style="max-width: 100%;"></a></p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/c1c9ee7652f7aff4e44b24d3074e2f602d75371e36bded0283a0b155da467c88/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3039303431322e706e67"><img src="https://camo.githubusercontent.com/c1c9ee7652f7aff4e44b24d3074e2f602d75371e36bded0283a0b155da467c88/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3039303431322e706e67" alt="image-20240413170409041" data-canonical-src="https://src.wuh.site/2024-04/2024-04-13-090412.png" style="max-width: 100%;"></a></p>
<p>前面几个月一直在学习bebal、eslint这种AST相关的知识，一直没有时间去写一篇正儿八经的博客，刚好体验了一下vitepress，在这里记录一下我是如何使用vitepress将我的知识仓库<a href="https://github.com/stack-wuh/blog">blog</a>做成<strong>wiki</strong>的。</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/7da14171092f93821c4965dc9e1be59998192bfb6bf911237e1c60b41d0ad8aa/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3037333932322e706e67"><img src="https://camo.githubusercontent.com/7da14171092f93821c4965dc9e1be59998192bfb6bf911237e1c60b41d0ad8aa/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3037333932322e706e67" alt="image-20240413153919880" data-canonical-src="https://src.wuh.site/2024-04/2024-04-13-073922.png" style="max-width: 100%;"></a></p>
<p>先展示一下我的文件目录结构，所有文件全部放在<strong>docs</strong>目录下，例如AST、Koa和工作周报这种比较特殊的文件夹是以关键字命名，剩余的博客全部是以年份，年份-月份，年份-月份-博客标题方式命名。</p>
<p>在vitepress的环境中，可以直接指定其工作目录为<strong>docs</strong>这样我们就可以快速地搭建出项目的基本目录。至于如何使用vitepress大家可以快速移步至<a href="https://vitepress.dev/zh/guide/getting-started" rel="nofollow">vitepress的官网</a>。在这里不做过多的介绍，浪费大家太多的时间，使用的步骤非常简单。</p>
<p>下面列出大家可能需要解决的问题:</p>
<ol>
<li>vitepress不会自动生成菜单和导航，需要你自己维护</li>
<li>vitepress采用约定式路由，文件路径即为路由地址</li>
<li>分组维护菜单时必须保证其键值与路由匹配</li>
<li>配合github actoins自动构建发布wiki</li>
</ol>
<h3>一、如何自动生成菜单</h3>
<p>为了处理上述的问题一与问题二，我们必须先认识一下vitepress的配置文件。在我们执行vitepress的init指令后，会在工作目录下生成<strong>index.md</strong>文件与**.vitepress**目录。</p>
<p>其中，<strong>index.md</strong>文件就是项目的首页，在这个页面中可以维护这个网站的快捷入口、标题、描述，具体的就是下面这个样式，这个模板就是vitepress提供的默认模板。</p>
<div class="highlight highlight-text-md"><pre class="notranslate"><span class="pl-s">---</span>
<span class="pl-c"><span class="pl-c">#</span> https://vitepress.dev/reference/default-theme-home-page</span>
<span class="pl-ent">layout</span>: <span class="pl-s">home</span>

<span class="pl-ent">hero</span>:
  <span class="pl-ent">name</span>: <span class="pl-s"><span class="pl-pds">"</span>shadow's wiki<span class="pl-pds">"</span></span>
  <span class="pl-ent">text</span>: <span class="pl-s"><span class="pl-pds">"</span>Fragmented knowledge manager<span class="pl-pds">"</span></span>
  <span class="pl-ent">tagline</span>: <span class="pl-s">碎片化知识管理库</span>
  <span class="pl-ent">image</span>: <span class="pl-s">https://user-images.githubusercontent.com/34117238/117454439-b7d41d00-af78-11eb-8b6f-7e4be67d9aa8.jpeg</span>
  <span class="pl-ent">actions</span>:
    - <span class="pl-ent">theme</span>: <span class="pl-s">brand</span>
      <span class="pl-ent">text</span>: <span class="pl-s">主站点 - wuh.site</span>
      <span class="pl-ent">link</span>: <span class="pl-s">https://wuh.site</span>
    - <span class="pl-ent">theme</span>: <span class="pl-s">alt</span>
      <span class="pl-ent">text</span>: <span class="pl-s">开放API - api.wuh.site</span>
      <span class="pl-ent">link</span>: <span class="pl-s">https://api.wuh.site/v2</span>
    - <span class="pl-ent">theme</span>: <span class="pl-s">alt</span>
      <span class="pl-ent">text</span>: <span class="pl-s">文档站点 - docs.wuh.site</span>
      <span class="pl-ent">link</span>: <span class="pl-s">https://docs.wuh.site</span>

<span class="pl-ent">features</span>:
  - <span class="pl-ent">title</span>: <span class="pl-s">搬山计划</span>
    <span class="pl-ent">icon</span>: <span class="pl-s">{ src: 'https://user-images.githubusercontent.com/34117238/117454439-b7d41d00-af78-11eb-8b6f-7e4be67d9aa8.jpeg', width: '120px', height: '120px' }</span>
    <span class="pl-ent">details</span>: <span class="pl-s">自我修养,犹如搬山</span>
    <span class="pl-ent">link</span>: <span class="pl-s">/$blog/2021/2021-05/搬山计划.html</span>
  - <span class="pl-ent">title</span>: <span class="pl-s">2022年度总结</span>
    <span class="pl-ent">details</span>: <span class="pl-s">每年年中发布一次的年度总结</span>
    <span class="pl-ent">icon</span>: <span class="pl-s">{ src: 'https://src.wuh.site/2022-02/2022-02-08-124302.png', width: '120px', height: '120px' }</span>
    <span class="pl-ent">link</span>: <span class="pl-s">/$blog/2022/2022-01/2022年度总结.html</span>
  - <span class="pl-ent">title</span>: <span class="pl-s">进取</span>
    <span class="pl-ent">icon</span>: <span class="pl-s">{ src: 'https://src.wuh.site/2022-09/20220901.001.png', width: '120px', height: '120px' }</span>
    <span class="pl-ent">details</span>: <span class="pl-s">静以修身，俭以养德。勿以善小而不为，勿以恶小而为之。</span>
    <span class="pl-ent">link</span>: <span class="pl-s">/$blog/2022/2022-09/进取.html</span>
<span class="pl-s">---</span>
</pre></div>
<p>另外，在**.vitepress<strong>中存在一个</strong>config.mjs**文件，它就是整个项目的配置文件了，下面是我的配置文件：</p>
<div class="highlight highlight-source-js"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">defineConfig</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'vitepress'</span>
<span class="pl-k">import</span> <span class="pl-s1">navGenerator</span> <span class="pl-k">from</span> <span class="pl-s">'../../plugins/nav-ganerator'</span>

<span class="pl-c">// https://vitepress.dev/reference/site-config</span>
<span class="pl-k">export</span> <span class="pl-k">default</span> <span class="pl-en">defineConfig</span><span class="pl-kos">(</span><span class="pl-kos">{</span>
  <span class="pl-c1">title</span>: <span class="pl-s">"shadow's wiki"</span><span class="pl-kos">,</span>
  <span class="pl-c1">description</span>: <span class="pl-s">"Fragmented knowledge manager"</span><span class="pl-kos">,</span>
  <span class="pl-c1">titleTemplate</span>: <span class="pl-s">'吴尒红'</span><span class="pl-kos">,</span>
  <span class="pl-c1">head</span>: <span class="pl-kos">[</span>
    <span class="pl-kos">[</span><span class="pl-s">'link'</span><span class="pl-kos">,</span> <span class="pl-kos">{</span> <span class="pl-c1">rel</span>: <span class="pl-s">'icon'</span><span class="pl-kos">,</span> <span class="pl-c1">href</span>: <span class="pl-s">'https://wuh.site/_next/image?url=%2Ficons%2F64.png&amp;w=64&amp;q=75'</span> <span class="pl-kos">}</span><span class="pl-kos">]</span><span class="pl-kos">,</span>
    <span class="pl-kos">[</span>
      <span class="pl-s">'script'</span><span class="pl-kos">,</span>
      <span class="pl-kos">{</span> <span class="pl-c1">async</span>: <span class="pl-s">''</span><span class="pl-kos">,</span> <span class="pl-c1">src</span>: <span class="pl-s">'https://www.googletagmanager.com/gtag/js?id=G-X4ZVBQXW9E'</span> <span class="pl-kos">}</span>
    <span class="pl-kos">]</span><span class="pl-kos">,</span>
    <span class="pl-kos">[</span>
      <span class="pl-s">'script'</span><span class="pl-kos">,</span>
      <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span>
      <span class="pl-s">`window.dataLayer = window.dataLayer || [];</span>
<span class="pl-s">      function gtag(){dataLayer.push(arguments);}</span>
<span class="pl-s">      gtag('js', new Date());</span>
<span class="pl-s">      gtag('config', 'G-X4ZVBQXW9E');`</span>
    <span class="pl-kos">]</span>
  <span class="pl-kos">]</span><span class="pl-kos">,</span>
  <span class="pl-c1">themeConfig</span>: <span class="pl-kos">{</span>
    <span class="pl-c">// https://vitepress.dev/reference/default-theme-config</span>
    <span class="pl-c1">nav</span>: <span class="pl-kos">[</span>
      <span class="pl-kos">{</span> <span class="pl-c1">text</span>: <span class="pl-s">'首页'</span><span class="pl-kos">,</span> <span class="pl-c1">link</span>: <span class="pl-s">'/'</span> <span class="pl-kos">}</span>
    <span class="pl-kos">]</span><span class="pl-kos">,</span>

    <span class="pl-c1">sidebar</span>: <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span>

    <span class="pl-c1">socialLinks</span>: <span class="pl-kos">[</span>
      <span class="pl-kos">{</span> <span class="pl-c1">icon</span>: <span class="pl-s">'github'</span><span class="pl-kos">,</span> <span class="pl-c1">link</span>: <span class="pl-s">'https://github.com/stack-wuh/blog/tree/gh-page'</span> <span class="pl-kos">}</span><span class="pl-kos">,</span>
      <span class="pl-kos">{</span> <span class="pl-c1">icon</span>: <span class="pl-s">'twitter'</span><span class="pl-kos">,</span> <span class="pl-c1">link</span>: <span class="pl-s">'https://twitter.com/wuh131420'</span> <span class="pl-kos">}</span>
    <span class="pl-kos">]</span><span class="pl-kos">,</span>

    <span class="pl-c1">footer</span>: <span class="pl-kos">{</span>
      <span class="pl-c1">message</span>: <span class="pl-s">'MIT License.'</span><span class="pl-kos">,</span>
      <span class="pl-c1">copyright</span>: <span class="pl-s">'Copyright ©2023. shadow'</span>
    <span class="pl-kos">}</span><span class="pl-kos">,</span>

    <span class="pl-c1">editLink</span>: <span class="pl-kos">{</span>
      <span class="pl-c1">text</span>: <span class="pl-s">'去github编辑'</span><span class="pl-kos">,</span>
      <span class="pl-c1">pattern</span>: <span class="pl-s">'https://github.com/stack-wuh/blog/blob/master/docs/:path'</span>
    <span class="pl-kos">}</span><span class="pl-kos">,</span>

    <span class="pl-c1">lastUpdatedText</span>: <span class="pl-s">'最后更新于'</span><span class="pl-kos">,</span>
    <span class="pl-c1">lastUpdated</span>: <span class="pl-c1">true</span><span class="pl-kos">,</span>

    <span class="pl-c1">search</span>: <span class="pl-kos">{</span>
      <span class="pl-c1">provider</span>: <span class="pl-s">'local'</span>
    <span class="pl-kos">}</span><span class="pl-kos">,</span>

    <span class="pl-c1">outline</span>: <span class="pl-kos">{</span>
      <span class="pl-c1">level</span>: <span class="pl-kos">[</span><span class="pl-c1">2</span><span class="pl-kos">,</span> <span class="pl-c1">3</span><span class="pl-kos">]</span>
    <span class="pl-kos">}</span><span class="pl-kos">,</span>

    <span class="pl-c1">logo</span>: <span class="pl-kos">{</span>
      <span class="pl-c1">src</span>: <span class="pl-s">'https://wuh.site/_next/image?url=%2Ficons%2F64.png&amp;w=64&amp;q=75'</span>
    <span class="pl-kos">}</span>
  <span class="pl-kos">}</span><span class="pl-kos">,</span>
  <span class="pl-c1">vite</span>: <span class="pl-kos">{</span>
    <span class="pl-c1">plugins</span>: <span class="pl-kos">[</span><span class="pl-en">navGenerator</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">]</span>
  <span class="pl-kos">}</span><span class="pl-kos">,</span>
  <span class="pl-c1">markdown</span>: <span class="pl-kos">{</span>
  <span class="pl-kos">}</span><span class="pl-kos">,</span>
  <span class="pl-c1">sitemap</span>: <span class="pl-kos">{</span>
    <span class="pl-c1">hostname</span>: <span class="pl-s">'https://wuh.site'</span>
  <span class="pl-kos">}</span><span class="pl-kos">,</span>
  <span class="pl-c1">rewrites</span>: <span class="pl-kos">{</span>
    <span class="pl-s">'([0-9]{4})/(.*)'</span>: <span class="pl-s">'$blog/([0-9]{4})/(.*)'</span>
  <span class="pl-kos">}</span><span class="pl-kos">,</span>
  <span class="pl-c1">outDir</span>: <span class="pl-s">'wiki.wuh.site'</span><span class="pl-kos">,</span>
  <span class="pl-c1">srcExclude</span>: <span class="pl-kos">[</span><span class="pl-s">'**/README.md'</span><span class="pl-kos">,</span> <span class="pl-s">'**/*.js'</span><span class="pl-kos">,</span> <span class="pl-s">'**/*.png'</span><span class="pl-kos">,</span> <span class="pl-s">'**/*.assets'</span><span class="pl-kos">]</span><span class="pl-kos">,</span>
  <span class="pl-c1">metaChunk</span>: <span class="pl-c1">true</span><span class="pl-kos">,</span>
  <span class="pl-c1">base</span>: <span class="pl-s">'/blog/'</span><span class="pl-kos">,</span>
  <span class="pl-c1">ignoreDeadLinks</span>: <span class="pl-c1">true</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span></pre></div>
<p>如果不出意外的话，在你执行<strong>pnpm init</strong>以及 <strong>pnpm dev</strong>之后，会出现由vitepress提供的默认页面。但是你的工作目录下的文件并没有自动生成菜单。</p>
<p>现在我们回到刚刚开始的问题，如何自动生成菜单和导航？</p>
<p>有两种方法，第一种是常规方法，把路由一个个在<strong>themeConfig.sidebar</strong>中维护进去，还有一种是利用其编译时的能力，写一个vite插件，直接在编译时改掉vite上下文中的配置文件。</p>
<p>大家回到<strong>config.mjs</strong>文件中，仔细看一下我在<strong>vite</strong>中配置了一个插件<a href="https://github.com/stack-wuh/blog/blob/gh-page/plugins/nav-ganerator/index.js">navGenerator</a>。在这个插件中我们只需要将文件路径转化为路由地址就可以了，然后在vite的上下文中的themeConfig.sidebar 改写为我们重新生成的对象就可以了。</p>
<div class="highlight highlight-source-js"><pre class="notranslate"><span class="pl-k">const</span> <span class="pl-en">navGenerator</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
  <span class="pl-k">return</span> <span class="pl-kos">{</span>
    <span class="pl-c1">name</span>: <span class="pl-s">'nav-generator'</span><span class="pl-kos">,</span>
    <span class="pl-en">configResolved</span><span class="pl-kos">(</span><span class="pl-s1">config</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
      <span class="pl-k">const</span> <span class="pl-kos">{</span> vitepress <span class="pl-kos">}</span> <span class="pl-c1">=</span> <span class="pl-s1">config</span>
      <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-c1">!</span><span class="pl-s1">vitepress</span><span class="pl-kos">)</span> <span class="pl-k">return</span>
      <span class="pl-k">const</span> <span class="pl-s1">menus</span> <span class="pl-c1">=</span> <span class="pl-en">slideGroupByHead</span><span class="pl-kos">(</span><span class="pl-s1">vitepress</span><span class="pl-kos">.</span><span class="pl-c1">pages</span><span class="pl-kos">)</span>

      <span class="pl-v">Reflect</span><span class="pl-kos">.</span><span class="pl-en">deleteProperty</span><span class="pl-kos">(</span><span class="pl-s1">menus</span><span class="pl-kos">,</span> <span class="pl-s">'序言'</span><span class="pl-kos">)</span>
      <span class="pl-s1">vitepress</span><span class="pl-kos">.</span><span class="pl-c1">userConfig</span><span class="pl-kos">.</span><span class="pl-c1">themeConfig</span><span class="pl-kos">.</span><span class="pl-c1">sidebar</span> <span class="pl-c1">=</span> <span class="pl-s1">menus</span>
      <span class="pl-s1">vitepress</span><span class="pl-kos">.</span><span class="pl-c1">userConfig</span><span class="pl-kos">.</span><span class="pl-c1">themeConfig</span><span class="pl-kos">.</span><span class="pl-c1">nav</span> <span class="pl-c1">=</span> <span class="pl-en">transformToNav</span><span class="pl-kos">(</span><span class="pl-s1">menus</span><span class="pl-kos">)</span>
    <span class="pl-kos">}</span>
  <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<p><strong>vite</strong>中的插件开发极其简单，利用<strong>vite</strong>提供的能力在其对应的钩子中，直接改其上下文的对象，就可以满足我们的需求。</p>
<p>我在刚开始做的时候也去找了一些博客看了一下前人是怎么做的，后来我发现他们的做法有点没必要。他们在利用<strong>vite</strong>的钩子在生成正常的路由地址后，去改写了<strong>config.mjs</strong>源文件中的<strong>sidebar</strong>对象，可实际上完全没有必要。</p>
<p>无论是<strong>webpack</strong>还是<strong>vite</strong>它们的构建流程都是链式的，它代表着上一个插件如果改动了上下文中的配置是会影响下一个插件的。所以完全不用去改源文件，只需要改动钩子中暴露出来的<strong>vitepress context</strong>，就可以满足需求。</p>
<p>但是有一个大前提，这个过程必须发生在最终生成文件的的插件之前。我们已知vitepress将md文件转换成html是利用的<strong>markdown-it</strong>插件，所以必须将<strong>navGenerator</strong>插件的触发时间提前。</p>
<p>正好<strong>configResolved</strong>钩子是最前面的那个入口，而且在此时vitepress已经完成了目录的解析，在此时我们已经可以非常完整地拿到工作目录下全部的md文件了。</p>
<p>插件的全部代码可以点击链接在github中的<a href="https://github.com/stack-wuh/blog/blob/gh-page/plugins/nav-ganerator/index.js">plugins/nav-generator</a>查看。</p>
<h3>二、如何正确维护多分组sidebar</h3>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/00c83c8f8e6dcaf2ea8b8fbefd1322b294ada604714cc8f4a7f87233a9148fb0/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3038353433392e706e67"><img src="https://camo.githubusercontent.com/00c83c8f8e6dcaf2ea8b8fbefd1322b294ada604714cc8f4a7f87233a9148fb0/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3038353433392e706e67" alt="image-20240413165436184" data-canonical-src="https://src.wuh.site/2024-04/2024-04-13-085439.png" style="max-width: 100%;"></a></p>
<p>但是分组多侧边栏这里有一个小坑要注意一下。</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/1fd943db92a8d51b7165d19ff03e89075450dfd43b469b783b12d2aef3fc673e/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3038353634352e706e67"><img src="https://camo.githubusercontent.com/1fd943db92a8d51b7165d19ff03e89075450dfd43b469b783b12d2aef3fc673e/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3038353634352e706e67" alt="image-20240413165641489" data-canonical-src="https://src.wuh.site/2024-04/2024-04-13-085645.png" style="max-width: 100%;"></a></p>
<p>以我的项目文档为例，最后生成的文档对应的对象应该是下面这样，我只截取一部分展示。</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/2f89c79f615cf7fbb82bf399c98b99695c43e4adfe759924c7073b722f84a1e1/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3039303135302e706e67"><img src="https://camo.githubusercontent.com/2f89c79f615cf7fbb82bf399c98b99695c43e4adfe759924c7073b722f84a1e1/68747470733a2f2f7372632e7775682e736974652f323032342d30342f323032342d30342d31332d3039303135302e706e67" alt="image-20240413170148327" data-canonical-src="https://src.wuh.site/2024-04/2024-04-13-090150.png" style="max-width: 100%;"></a></p>
<p>与生成sidebar同理，nav的数据也可以继续利用这种方法生成。最后的效果就是大家看到的封面图那样。</p>
<h3>三、自动构建发布</h3>
<p>继续利用我们之前介绍过的<strong>github actions</strong>帮助我们自动构建发布。同样在vitepress的官网中提供的一个<a href="https://vitepress.dev/zh/guide/deploy#github-pages" rel="nofollow">工作流示例</a>。</p>
<p><strong>github actions</strong>是一个非常友好的工作流，在之前我已经写了一篇博客，详细地介绍了我是如何actions实现了我的博客项目自动构建发布，可以移步原文<a href="https://wuh.site/post/Github%20Actions%20%E8%87%AA%E5%8A%A8%E9%83%A8%E7%BD%B2%E5%BA%94%E7%94%A8" rel="nofollow">Github Actions 自动部署应用</a>。</p>
<p>下面是我的<a href="https://github.com/stack-wuh/blog/blob/gh-page/.github/workflows/deploy.yaml">工作流配置文件</a>:</p>
<div class="highlight highlight-source-yaml"><pre class="notranslate"><span class="pl-c"><span class="pl-c">#</span> 构建 VitePress 站点并将其部署到 GitHub Pages 的示例工作流程</span>
<span class="pl-c"><span class="pl-c">#</span></span>
<span class="pl-ent">name</span>: <span class="pl-s">Deploy VitePress site to Pages</span>

<span class="pl-ent">on</span>:
  <span class="pl-c"><span class="pl-c">#</span> 在针对 `main` 分支的推送上运行。如果你</span>
  <span class="pl-c"><span class="pl-c">#</span> 使用 `master` 分支作为默认分支，请将其更改为 `master`</span>
  <span class="pl-ent">push</span>:
    <span class="pl-ent">branches</span>: <span class="pl-s">[gh-page]</span>

  <span class="pl-c"><span class="pl-c">#</span> 允许你从 Actions 选项卡手动运行此工作流程</span>
  <span class="pl-ent">workflow_dispatch</span>:

<span class="pl-c"><span class="pl-c">#</span> 设置 GITHUB_TOKEN 的权限，以允许部署到 GitHub Pages</span>
<span class="pl-ent">permissions</span>:
  <span class="pl-ent">contents</span>: <span class="pl-s">read</span>
  <span class="pl-ent">pages</span>: <span class="pl-s">write</span>
  <span class="pl-ent">id-token</span>: <span class="pl-s">write</span>

<span class="pl-c"><span class="pl-c">#</span> 只允许同时进行一次部署，跳过正在运行和最新队列之间的运行队列</span>
<span class="pl-c"><span class="pl-c">#</span> 但是，不要取消正在进行的运行，因为我们希望允许这些生产部署完成</span>
<span class="pl-ent">concurrency</span>:
  <span class="pl-ent">group</span>: <span class="pl-s">pages</span>
  <span class="pl-ent">cancel-in-progress</span>: <span class="pl-c1">false</span>

<span class="pl-ent">jobs</span>:
  <span class="pl-c"><span class="pl-c">#</span> 构建工作</span>
  <span class="pl-ent">build</span>:
    <span class="pl-ent">runs-on</span>: <span class="pl-s">ubuntu-latest</span>
    <span class="pl-ent">steps</span>:
      - <span class="pl-ent">name</span>: <span class="pl-s">Checkout</span>
        <span class="pl-ent">uses</span>: <span class="pl-s">actions/checkout@v4</span>
        <span class="pl-ent">with</span>:
          <span class="pl-ent">fetch-depth</span>: <span class="pl-c1">0</span> <span class="pl-c"><span class="pl-c">#</span> 如果未启用 lastUpdated，则不需要</span>
      - <span class="pl-ent">uses</span>: <span class="pl-s">pnpm/action-setup@v3 </span><span class="pl-c"><span class="pl-c">#</span> 如果使用 pnpm，请取消注释</span>
        <span class="pl-ent">with</span>:
          <span class="pl-ent">version</span>: <span class="pl-c1">8</span>
      <span class="pl-c"><span class="pl-c">#</span> - uses: oven-sh/setup-bun@v1 # 如果使用 Bun，请取消注释</span>
      - <span class="pl-ent">name</span>: <span class="pl-s">Setup Node</span>
        <span class="pl-ent">uses</span>: <span class="pl-s">actions/setup-node@v4</span>
        <span class="pl-ent">with</span>:
          <span class="pl-ent">node-version</span>: <span class="pl-c1">20</span>
          <span class="pl-ent">cache</span>: <span class="pl-s">pnpm </span><span class="pl-c"><span class="pl-c">#</span> 或 pnpm / yarn</span>
      - <span class="pl-ent">name</span>: <span class="pl-s">Setup Pages</span>
        <span class="pl-ent">uses</span>: <span class="pl-s">actions/configure-pages@v4</span>
      - <span class="pl-ent">name</span>: <span class="pl-s">Install dependencies</span>
        <span class="pl-ent">run</span>: <span class="pl-s">pnpm install </span><span class="pl-c"><span class="pl-c">#</span> 或 pnpm install / yarn install / bun install</span>
      - <span class="pl-ent">name</span>: <span class="pl-s">Build with VitePress</span>
        <span class="pl-ent">run</span>: <span class="pl-s">pnpm run docs:build </span><span class="pl-c"><span class="pl-c">#</span> 或 pnpm docs:build / yarn docs:build / bun run docs:build</span>
      - <span class="pl-ent">name</span>: <span class="pl-s">Upload artifact</span>
        <span class="pl-ent">uses</span>: <span class="pl-s">actions/upload-pages-artifact@v3</span>
        <span class="pl-ent">with</span>:
          <span class="pl-ent">path</span>: <span class="pl-s">docs/wiki.wuh.site</span>

  <span class="pl-c"><span class="pl-c">#</span> 部署工作</span>
  <span class="pl-ent">deploy</span>:
    <span class="pl-ent">environment</span>:
      <span class="pl-ent">name</span>: <span class="pl-s">github-pages</span>
      <span class="pl-ent">url</span>: <span class="pl-s">${{ steps.deployment.outputs.page_url }}</span>
    <span class="pl-ent">needs</span>: <span class="pl-s">build</span>
    <span class="pl-ent">runs-on</span>: <span class="pl-s">ubuntu-latest</span>
    <span class="pl-ent">name</span>: <span class="pl-s">Deploy</span>
    <span class="pl-ent">steps</span>:
      - <span class="pl-ent">name</span>: <span class="pl-s">Deploy to GitHub Pages</span>
        <span class="pl-ent">id</span>: <span class="pl-s">deployment</span>
        <span class="pl-ent">uses</span>: <span class="pl-s">actions/deploy-pages@v4</span>
</pre></div>
<p>在使用<strong>vitepress</strong>搭建wiki库之前，先切一个<strong>gh-page</strong>分支出来，后期我们做自动构建主要是以这个分支为主，而不是master分支。</p>
<p>注意，我们只需要改几处就可以使github正常工作了:</p>
<ol>
<li>构建分支，将其改为gh-page</li>
<li>将打包器改为使用pnpm</li>
<li>将指令也改为pnpm配套指令</li>
<li>更改产出文件目录</li>
</ol>
<p>与此同时，项目内部的<strong>config.mjs</strong>文件产出目录也需要改一下，因为github pages的部署路由会自动在前面加上/blog/，所以项目的basePath也需要加一个前缀/blog/。</p>
<p>到此为止，全部的流程就介绍完啦~~~</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[如何使用zLibrary]]></title>
            <link>https://wuh.site/post/65</link>
            <guid>https://wuh.site/post/65</guid>
            <pubDate>Sun, 11 Jun 2023 03:23:39 GMT</pubDate>
            <description><![CDATA[如何使用zLibrary 昨天下了一天的雨，闲着实在没什么事情做，就在微信阅读里面找了一本书《长安的荔枝》来读，读的正在兴头上呢，结果出现了下面这个恶心的东西。不得不说，现在的微信读书没有之前好用了，腾讯总是习惯于把一款优秀的产品做成垃圾。 <img src="https://src.wuh.site/2023-04/2023-06-11-WechatIMG104.jpeg" width="400]]></description>
            <content:encoded><![CDATA[<h2>如何使用zLibrary</h2>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/54f09a6a7b5d412fe021e23747bfc6f20520725bf5109349934df4110244cf9d/68747470733a2f2f7372632e7775682e736974652f323032332d30362f323032332d30362d31312d3033303631322e706e67"><img src="https://camo.githubusercontent.com/54f09a6a7b5d412fe021e23747bfc6f20520725bf5109349934df4110244cf9d/68747470733a2f2f7372632e7775682e736974652f323032332d30362f323032332d30362d31312d3033303631322e706e67" alt="" data-canonical-src="https://src.wuh.site/2023-06/2023-06-11-030612.png" style="max-width: 100%;"></a></p>
<p>昨天下了一天的雨，闲着实在没什么事情做，就在微信阅读里面找了一本书《长安的荔枝》来读，读的正在兴头上呢，结果出现了下面这个恶心的东西。不得不说，现在的微信读书没有之前好用了，腾讯总是习惯于把一款优秀的产品做成垃圾。</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/aa46189b3439f25102b33be6e0045402c75d233a589532384e3b83fdaa01c208/68747470733a2f2f7372632e7775682e736974652f323032332d30342f323032332d30362d31312d576563686174494d473130342e6a706567"><img src="https://camo.githubusercontent.com/aa46189b3439f25102b33be6e0045402c75d233a589532384e3b83fdaa01c208/68747470733a2f2f7372632e7775682e736974652f323032332d30342f323032332d30362d31312d576563686174494d473130342e6a706567" width="400px" data-canonical-src="https://src.wuh.site/2023-04/2023-06-11-WechatIMG104.jpeg" style="max-width: 100%;"></a></p>
<p>实在是受不了这种，我是提倡知识付费的，但是白嫖还是快乐的。我想起了之前看到的一个视频里面提到过一个免费的在线图书馆，资源贼拉多，它就是大名鼎鼎的<a href="https://lib-boxnfxb7fl57xmwt2nd5h4gx.1lib.fr/" rel="nofollow">zlibrary</a></p>
<p>这个网站的官网已经被封了，一些镜像站也被封了。我是在telegram里面找到了一些组群，在里面找到了一个可以使用的机器人：<a href="https://t.me/fbooki" rel="nofollow">https://t.me/fbooki</a></p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6a1ad6a16dd653c57fa3411ff2420014a8c8ee4b6cfff957fb96525682b2f9a5/68747470733a2f2f7372632e7775682e736974652f323032332d30362f323032332d30362d31312d3033313234362e706e67"><img src="https://camo.githubusercontent.com/6a1ad6a16dd653c57fa3411ff2420014a8c8ee4b6cfff957fb96525682b2f9a5/68747470733a2f2f7372632e7775682e736974652f323032332d30362f323032332d30362d31312d3033313234362e706e67" alt="" data-canonical-src="https://src.wuh.site/2023-06/2023-06-11-031246.png" style="max-width: 100%;"></a></p>
<p>这个机器人会发一些可以使用链接地址，大家可以自行点击使用。下面我贴一个镜像站：<a href="https://lib-boxnfxb7fl57xmwt2nd5h4gx.1lib.fr/%E3%80%82" rel="nofollow">https://lib-boxnfxb7fl57xmwt2nd5h4gx.1lib.fr/。</a></p>
<p>除此之外，还有一种的暗网访问的方法，这里贴一个教程链接:<a href="https://www.youtube.com/watch?v=pK3LzbT271E" rel="nofollow">https://www.youtube.com/watch?v=pK3LzbT271E</a></p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a3affd395ce8a1a6675822443c49eeb6c450725357270fa667538a2c56d991a9/68747470733a2f2f7372632e7775682e736974652f323032332d30362f323032332d30362d31312d3033313634302e706e67"><img src="https://camo.githubusercontent.com/a3affd395ce8a1a6675822443c49eeb6c450725357270fa667538a2c56d991a9/68747470733a2f2f7372632e7775682e736974652f323032332d30362f323032332d30362d31312d3033313634302e706e67" alt="" data-canonical-src="https://src.wuh.site/2023-06/2023-06-11-031640.png" style="max-width: 100%;"></a></p>
<p>其使用方式是一样一样的，点击书籍链接进入明细就可以看到下载按钮了，我们直接使用默认的下载类型，然后在微信读书里面导入本地文件，就也可以继续阅读了。</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e131b7a34f27e6549d9bf96fc97d6b3a9b929d75b9bc3da3152be4a3834ef8e3/68747470733a2f2f7372632e7775682e736974652f323032332d30362f323032332d30362d31312d3033313930382e706e67"><img src="https://camo.githubusercontent.com/e131b7a34f27e6549d9bf96fc97d6b3a9b929d75b9bc3da3152be4a3834ef8e3/68747470733a2f2f7372632e7775682e736974652f323032332d30362f323032332d30362d31312d3033313930382e706e67" alt="" data-canonical-src="https://src.wuh.site/2023-06/2023-06-11-031908.png" style="max-width: 100%;"></a></p>
<p><strong>注意</strong>:</p>
<ol>
<li>使用zlibrary有一个大前提，准备好科学上网</li>
</ol>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[复杂函数设计与治理-分治法]]></title>
            <link>https://wuh.site/post/41</link>
            <guid>https://wuh.site/post/41</guid>
            <pubDate>Sun, 29 Jan 2023 09:30:40 GMT</pubDate>
            <description><![CDATA[复杂函数设计与治理-分治法 在项目中，经常会遇到的一种情况，就是大段大段的逻辑代码，各种各样的逻辑全局集中到了主函数的内部，在上一次的【盘活运力】的review中，我给大家描述了一下，我是怎么处理大段逻辑代码的，这里是记录性质的博客。 对于可复用性要求较高的公共函数、公共文件的封装或者是逻辑的处理。可以对比去看一下重构之前的代码，大段的逻辑代码集中在一个函数内部，导致一个函数的逻辑杂糅，一个方法甚]]></description>
            <content:encoded><![CDATA[<h2>复杂函数设计与治理-分治法</h2>
<p>在项目中，经常会遇到的一种情况，就是大段大段的逻辑代码，各种各样的逻辑全局集中到了主函数的内部，在上一次的【盘活运力】的review中，我给大家描述了一下，我是怎么处理大段逻辑代码的，这里是记录性质的博客。</p>
<p>对于可复用性要求较高的公共函数、公共文件的封装或者是逻辑的处理。可以对比去看一下重构之前的代码，大段的逻辑代码集中在一个函数内部，导致一个函数的逻辑杂糅，一个方法甚至有几百行，这都是需要优化的地方。</p>
<p>从我个人的经验来看，对于公共方法，有以下几个关键点:<br>
高灵活性<br>
高区块性<br>
高责任性</p>
<p>高灵活性<br>
其实，灵活性就是可扩展性。</p>
<p>我们在写公共的函数的时候，无论是mixin文件还是utils工具文件，都要做到的一点就是，一定要操作空间，一旦这个函数内部的逻辑不满足了，还可以通过外部传入的逻辑，来实现我的最终目的，而不用改动公共函数的内部代码。</p>
<p>怎么留足操作空间呢？就是在关键的逻辑位置提供一个函数，可以满足外部传入的需求。</p>
<p>高区块性<br>
区块性，就是说将一大段函数逻辑，拆分为若干个逻辑片段。</p>
<p>既然是逻辑杂糅了，那就必然可以将一段杂糅的逻辑切片成多段逻辑，然后按照执行的顺序依次执行，到了最后的结果就是将多段函数的执行结果就是大函数的执行结果，这种方法就叫分治。</p>
<p>高责任性<br>
指函数职责的单一性要强，对于公共函数一定要做到，一个方法一个功能。</p>
<p>对于单一职责我们一直放在嘴边，但是完成的力度一直不够，我们可以回想一下现在的代码，很多情景下，对于多个地方用到了相同的公共函数的位置，经常会来新声明一个函数。而这个新函数的作用就是将多个公共函数放在一起，然后在页面上就调用这个新函数。</p>
<p>对于这个新函数，你能说它职责单一了吗？</p>
<p>一旦遇到了特殊场景，大部分开发人员就会在新函数内部，使用各种if/else的逻辑判断来执行对应的函数，随着迭代次数越来越多，逻辑越来越复杂，新函数就成了另一个杂糅函数，换一句话说，它不纯了。</p>
<p>分治法<br>
分治法，是一种经典的数学算法，取分而治之之意。目的是将一个复杂的问题划分为多个相同或者是相似的小问题，分开解决。</p>
<p>对于具体实现可以看下图代码，<a href="http://gitlab.ky-tech.com.cn/kuasheng-h5/coo-quality-monitor/blob/test/src/components/rank-table/mixins/base.mixin.js" rel="nofollow">文件地址</a>：</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/34117238/215317409-efec7a0d-9b3c-4dd4-8b93-0cf8f99244a7.png"><img src="https://user-images.githubusercontent.com/34117238/215317409-efec7a0d-9b3c-4dd4-8b93-0cf8f99244a7.png" alt="image" style="max-width: 100%;"></a><br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/34117238/215317422-278c5ed5-8a13-4bf4-bd7d-cc1bb134d1f7.png"><img src="https://user-images.githubusercontent.com/34117238/215317422-278c5ed5-8a13-4bf4-bd7d-cc1bb134d1f7.png" alt="image (1)" style="max-width: 100%;"></a></p>
<p>这一段是我们经常遇到的处理表格的请求逻辑，它是一个非常典型的、可复用性要求较高的、可分治的案例。</p>
<p>它是一个mixin混入文件，给9个表格业务组件提供了全部的逻辑支持，基本没有出现什么问题。分治点已经标出，大家可以点击文件链接进去自由查看细节。</p>
<p>大家可以仔细看一下，上面说的三点在这个案例中已经全部体现出来了。</p>
<p>对于一个区块内部的逻辑，可以将它分成两块，一部分由函数体内部提供一个默认函数，同时支持外部传入函数，留足了操作空间，灵活性极佳。</p>
<p>每一个区块内部的函数，只管理它负责部分的功能，可以清晰的看到我标出的5个分治点，就是其区块性最好的体现。</p>
<p>现在项目中有大量的业务代码，可以提供给大家练手的机会，可以尽情地去使用分治法。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[通过axios请求反查文件路径]]></title>
            <link>https://wuh.site/post/40</link>
            <guid>https://wuh.site/post/40</guid>
            <pubDate>Sun, 29 Jan 2023 09:27:35 GMT</pubDate>
            <description><![CDATA[通过axios请求反查文件路径 现在ERP以及支持通过Element面板查找使用组件的文件地址，但是还有一种情景是比较特殊的。在批量改完cooHttp优化需求之后，还可以通过发起的ajax的请求来查找发起请求的文件路径。 如下图，即为ERP支持的使用组件的文件地址: image 如何通过ajax反查调用文件位置 打开NewWork面板中的Initiator栏 image (1) 鼠标移入对应的接口]]></description>
            <content:encoded><![CDATA[<h2>通过axios请求反查文件路径</h2>
<p>现在ERP以及支持通过Element面板查找使用组件的文件地址，但是还有一种情景是比较特殊的。在批量改完cooHttp优化需求之后，还可以通过发起的ajax的请求来查找发起请求的文件路径。</p>
<p>如下图，即为ERP支持的使用组件的文件地址:<br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/34117238/215317269-bf8dd78b-856b-453c-977d-d376164205de.png"><img src="https://user-images.githubusercontent.com/34117238/215317269-bf8dd78b-856b-453c-977d-d376164205de.png" alt="image" style="max-width: 100%;"></a></p>
<p>如何通过ajax反查调用文件位置<br>
打开NewWork面板中的Initiator栏<br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/34117238/215317285-90b67cf8-550c-4515-98f3-42617f1b3769.png"><img src="https://user-images.githubusercontent.com/34117238/215317285-90b67cf8-550c-4515-98f3-42617f1b3769.png" alt="image (1)" style="max-width: 100%;"></a></p>
<p>鼠标移入对应的接口处，就可以看到请求的执行栈，标红处是依次触发的方法，标蓝处是执行方法时对应的文件位置<br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/34117238/215317288-c56e8146-8111-48d7-960f-66eac5679908.png"><img src="https://user-images.githubusercontent.com/34117238/215317288-c56e8146-8111-48d7-960f-66eac5679908.png" alt="image (2)" style="max-width: 100%;"></a></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[进取]]></title>
            <link>https://wuh.site/post/39</link>
            <guid>https://wuh.site/post/39</guid>
            <pubDate>Sun, 25 Sep 2022 09:12:40 GMT</pubDate>
            <description><![CDATA[进取 吾日三省吾身，为人谋而不忠乎？与朋友交而不信乎？传不习乎？ 最近，准确地说是昨天，发生了一件值得让我深思的事情。“我并没有学会珍惜”是我思考之后得出的结果。 我想我必须把这件事情记录一下，留给未来的自己，值得反复体会。 回忆 首先我家没什么底子，就是一个普普通通的农民家庭，从小到大没什么依仗都是靠我爸一个人独立奋斗，唯二的就是我的亲人们时不时地帮助，什么帮助呢？帮你做一做耗时的农活。 棉花，]]></description>
            <content:encoded><![CDATA[<h2>进取</h2>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3ae7db1fa8b78b8db0154e240c22ea70c0f065b8e937351fd305965525cc9fe0/68747470733a2f2f7372632e7775682e736974652f323032322d30392f32303232303930312e3030312e706e67"><img src="https://camo.githubusercontent.com/3ae7db1fa8b78b8db0154e240c22ea70c0f065b8e937351fd305965525cc9fe0/68747470733a2f2f7372632e7775682e736974652f323032322d30392f32303232303930312e3030312e706e67" alt="" data-canonical-src="https://src.wuh.site/2022-09/20220901.001.png" style="max-width: 100%;"></a></p>
<blockquote>
<p>吾日三省吾身，为人谋而不忠乎？与朋友交而不信乎？传不习乎？</p>
</blockquote>
<p>最近，准确地说是昨天，发生了一件值得让我深思的事情。“我并没有学会珍惜”是我思考之后得出的结果。</p>
<p>我想我必须把这件事情记录一下，留给未来的自己，值得反复体会。</p>
<h3>回忆</h3>
<p>首先我家没什么底子，就是一个普普通通的农民家庭，从小到大没什么依仗都是靠我爸一个人独立奋斗，唯二的就是我的亲人们时不时地帮助，什么帮助呢？帮你做一做耗时的农活。</p>
<p>棉花，一种经济作物，但是现在它越来越廉价。</p>
<p>种棉花是体力活，它不像插秧苗直接插到水地里，唯一的相同点就是人必须得弯着腰，直到把家里的棉花田里全部种满，那个时候人才能得到一点点的休息时间。</p>
<p>有一种棉是开心棉，很好摘。还有一种，是闭口的，是淋了雨，打了风，掉落在地里的。这种不好搞，要暴晒之后，才能剥开外壳。</p>
<p>我印象中最深刻的画面就是我爸一个人，一台电视机，两大筐闭口棉，干到半夜1点过，而明天他早上5点又要早起下地了。</p>
<p>太阳，太炽热了，太残酷了，太无情了。</p>
<p>我能体会到，那段时间，准确地说是那十年，他很累。</p>
<p>现在，他在工厂里面上班，他终于脱离了面朝黄土背朝天的日子。</p>
<h3>虚无的狂欢</h3>
<p>我陷入了一种虚无，精神上的虚无。整整一个月的迟到，扣了2000块。刚开始我觉得没什么，就这样，它只是我工资的一小部分，更何况那段时间我确实感觉很累很累，早上睡不醒，晚上睡不着，头痛胃痛啥都有。</p>
<p>想吃啥吃啥，想买啥买啥，美其名曰太累，身体不好，需要搞点好东西改善一下伙食，现在回过头去看，简直是浪费，我忘记了过去的我了，我忘记了那一段艰苦的日子。</p>
<p>我很久没有读书了，最近沉迷上了王者荣耀，连胜让我觉得很过瘾，吸引我一直打下去。唯一让我欣慰地是，我没有染上一些陋习。</p>
<p>不赌不嫖不偷不抢，我还是我。迷失的是那个积极进取、热爱读书的我，我已经不再是我了。</p>
<p>我可以把一些客观因素归结到繁重的工作吗？是天天加班导致的心力交瘁？还是自己已经甘于沉沦了，甘愿躲在过去的阴影里面？</p>
<h3>求己</h3>
<p>我忘记了我们小学时候学过的《论语》了，还记得“吾日三省吾身”吗？还记得《劝学》吗？</p>
<p>我怎么可以把一切问题推给外部环境，我忘记了“适者生存”的丛林法则。如果我一直抱着这种想法，我终将被社会淘汰，成为优秀的猎食者的晚餐。</p>
<p>我突然之间想起了《人间失格》，自我催眠的弱者，怎么可能在这个社会丛林正常的生存。如果能在一些文学作品中看到自己的样子，说明这本小说是一个最基础的社会缩影，它是现实的。</p>
<p>试着通过这类文学作品去反推，看看自己在小说中的结局，它又是魔幻的。</p>
<p>魔幻的现实是残酷的，是悲剧的，是无法挣脱的枷锁，是禁锢自己的牢笼。</p>
<p>不去主动地打破牢笼，就失去了向上的阶梯， 将被永远的困在地牢，不见光明。失去了阳光，失去了一切。</p>
<p>我想起了刚上班的那段时间，上下班的路上我在思考怎么写那些业务代码，因为我接手的第一个项目就做砸了，当时是我的领导保了我，给了我第二次机会。那真是一段令人无法忘记的回忆。</p>
<h3>变化</h3>
<p>我觉得思考的东西应该是要深刻的。写到这里我想先停一下。</p>
<p><strong>越是深刻的东西越是简单</strong>。归根究底，就是我进入社会的这5年，过的太顺利了。顺利到我还没有学会<strong>珍惜</strong>。</p>
<p>回顾了一下我的履历，我是在大佬的爱护下成长的。我的每一位领导年龄都比我大，我想他们对我的这种爱护之情，包容之心和同事之谊，就是我成长的养分。</p>
<p>我听说，我的同事们私底下对我的评价还不错，他们都认为我的业务水平还可以，但其实我已经在原地踏步了一年，我想我已经很落后了。</p>
<p><strong>技术</strong>是个很容易替代的东西。我认为，一个刚毕业的大学生，给他足够的训练时间，外加一位负责的领导指导一下，可以极大地减少他上手的代价。</p>
<p>所以，遇到了这样的一个领导或者是同事，他又愿意和你交流，愿意给你分析一些问题，是一件幸福的事情，是一件值得珍惜的事情。同时，这样的同事是值得深交的，很多事情，有高人指点可以免去你很多的时间。</p>
<p>同时，在自己力所能及的范围内，对后浪们也要有一种<strong>爱护</strong>之情。他们只是比我入行晚而已，我的优势只是比他们提前入行，见过的东西多一些，见过套路多一些，除此以外，我没有任何<strong>优势</strong>。</p>
<h3>启程</h3>
<p>无言是最好的告白。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[丧钟为谁而鸣]]></title>
            <link>https://wuh.site/post/38</link>
            <guid>https://wuh.site/post/38</guid>
            <pubDate>Mon, 04 Apr 2022 12:48:43 GMT</pubDate>
            <description><![CDATA[丧钟为谁而鸣 勿忧勿忧，欲说还休；不休不休，至死方休。 此刻，我一边喝着酒，一边想着一些骚话。 先来一口，开摆。 也不知道为什么起了这样一个标题，可能是忽然之间想起来这么一本书，内容是什么一时之间也想不起更多了。既然内容想不起来了，那就直接用他的标题吧。 如果非要给未来定义一个颜色，那我的未来一定是灰色，或者我认为我的未来是灰色。矛盾的对立统一在我的身上得到了相当明显的体现，换一种描述，那就叫它“]]></description>
            <content:encoded><![CDATA[<h2>丧钟为谁而鸣</h2>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/4fe21eca145ab39d2cd0bf79f8d8203d96e157e3fa201edcbc2a99d0bee8ae32/68747470733a2f2f7372632e7775682e736974652f323032322d30322f323032322d30342d30342d2545352542302538312545392539442541322545352539422542452e6b65792e3030312e706e67"><img src="https://camo.githubusercontent.com/4fe21eca145ab39d2cd0bf79f8d8203d96e157e3fa201edcbc2a99d0bee8ae32/68747470733a2f2f7372632e7775682e736974652f323032322d30322f323032322d30342d30342d2545352542302538312545392539442541322545352539422542452e6b65792e3030312e706e67" alt="" data-canonical-src="https://src.wuh.site/2022-02/2022-04-04-%E5%B0%81%E9%9D%A2%E5%9B%BE.key.001.png" style="max-width: 100%;"></a></p>
<blockquote>
<p>勿忧勿忧，欲说还休；不休不休，至死方休。</p>
</blockquote>
<p>此刻，我一边喝着酒，一边想着一些骚话。</p>
<p>先来一口，开摆。</p>
<p>也不知道为什么起了这样一个标题，可能是忽然之间想起来这么一本书，内容是什么一时之间也想不起更多了。既然内容想不起来了，那就直接用他的标题吧。</p>
<p>如果非要给未来定义一个颜色，那我的未来一定是灰色，或者我认为我的未来是灰色。矛盾的对立统一在我的身上得到了相当明显的体现，换一种描述，那就叫它“纠缠”吧，再也不用纠结它的实体了，它就是抽象的存在。</p>
<p>我的生活轨迹就像是齿轮一样，每天都在重复做一样的事情，我很想改变他，最后却发现我无能为力，能改变的只能是我自己。</p>
<p>就这样，我在生活的漩涡中越陷越深。同时，我很庆幸我还没有结婚，还没有孩子，还没有家庭，还没有那如山的压力。我毫不怀疑，我可以想象出这样的一种压力可以把一个家庭家长的腰彻底压垮。</p>
<p>我已在漩涡中，挣扎太久，我变老了，我想我快失去动力了。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[2022年度总结]]></title>
            <link>https://wuh.site/post/37</link>
            <guid>https://wuh.site/post/37</guid>
            <pubDate>Tue, 08 Feb 2022 12:59:52 GMT</pubDate>
            <description><![CDATA[2022年度总结 cover 不要停步不前，每一天都要做出一点改变。 在开始分析之前，我想是不是需要一些数据模型来判断，在2021-2022年度我的个人发展是曲折向前的呢，还是原地踏步呢？ 回头一想，还是算了吧。没有必要这么卷自己了，下面的数据留给阅读者自己对比吧。 阅读不是打发时间的一种消遣 书本之外的世界很大，书本之内的乾坤也不小。 现在，我不爱和人谈论的内容之一就是: ”你最爱看什么的类型的]]></description>
            <content:encoded><![CDATA[<h2>2022年度总结</h2>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f2f3e6b5c69372e759e61287dcbe9b5cd30515de83ee79e90ba1316becee419d/68747470733a2f2f7372632e7775682e736974652f323032322d30322f323032322d30322d30382d3132343330322e706e67"><img src="https://camo.githubusercontent.com/f2f3e6b5c69372e759e61287dcbe9b5cd30515de83ee79e90ba1316becee419d/68747470733a2f2f7372632e7775682e736974652f323032322d30322f323032322d30322d30382d3132343330322e706e67" alt="cover" data-canonical-src="https://src.wuh.site/2022-02/2022-02-08-124302.png" style="max-width: 100%;"></a></p>
<blockquote>
<p>不要停步不前，每一天都要做出一点改变。</p>
</blockquote>
<p>在开始分析之前，我想是不是需要一些数据模型来判断，在2021-2022年度我的个人发展是曲折向前的呢，还是原地踏步呢？</p>
<p>回头一想，还是算了吧。没有必要这么卷自己了，下面的数据留给阅读者自己对比吧。</p>
<h3>阅读不是打发时间的一种消遣</h3>
<blockquote>
<p>书本之外的世界很大，书本之内的乾坤也不小。</p>
</blockquote>
<p>现在，我不爱和人谈论的内容之一就是: ”你最爱看什么的类型的小说“，那没有意义。</p>
<p>和去年一样的套路，还是先列出今年的书单吧：</p>
<ol>
<li>这个女人不寻常呀! -- 《两京十五日》</li>
<li>有的时候一本书名就是一段故事 -- 《西线无战事》</li>
<li>有很多人喜欢项羽，也有很多人喜欢刘邦，我不一样，我喜欢曹操~~ -- 《楚汉双雄》</li>
<li>三等、三惹的韵脚诗美如画，一如中国画的留白是留给观者的遐想，它的意境无边 --《天青色等烟雨》</li>
<li>《三国演义》是中国的，也是世界的，更是小日本学不会的~~ --《银河英雄传说》</li>
<li>狄仁杰的魅力，吸引了老外创作了这一系列的案件故事，尽管后面我看不太下去了 --《大唐狄公传》</li>
<li>完全放弃了，不看了 --《剑来》</li>
<li>孤独是自己最大的敌人吗？或许没有希望才是！--《一个叫欧维的男人决定去死》</li>
<li>我记得之前的系列原名是叫“谋杀官员”，在两年之前我很喜欢看，这次是重温。 --《高智商犯罪》</li>
<li>敲了一段话还是删除了，算了，他们是历史 --《五代十国的枭雄们》</li>
<li>等待与希望 --《基督山伯爵》</li>
<li>一半是吃人恶魔，一半是救赎神明，我应该怎么说呢，还是大家自己去看吧。 --《沉默的羔羊》</li>
<li>我看了后记才知道，这一部小说的诞生前后经过了四十年，小半个世纪打磨出来的故事集确实比《后续》看起来更有感觉，更精彩。 --《夜谭十记》</li>
</ol>
<p>人的耐心是有上限的，每一个人的上限都会不一样，甚至于每一个人在不同的时期面对相同的事情，其承受度都是弹性的，比如我对《剑来》的耐心已经消磨完了，越来越看不下去了。作者对其热门IP产品的消费是不是也要考虑一下读者的想法呀。</p>
<p>我喜欢古龙小说，他对笔下的大侠们战斗方式和战斗场景的描写极其简洁，往往几十个字就描述了一段精彩的战斗或者是前文铺下的伏笔，最后揭露出来让人大呼过瘾，只叫痛快。武侠小说和这种玄幻小说又有什么差异呢？这么多年过去了，我们仍然爱着古龙，和他笔下的那些大侠。</p>
<hr>
<p>《基督山伯爵》是本年度内耗时最长的小说。不得不说作者设计的一些场景起承转合，尤其是终章善有善报的结局，当然最让人受不了的是那种不缺钱的“钞能力”。</p>
<p><strong>等待和希望</strong>就是基督山伯爵一生的写照，他的上半生经历了被人陷害入狱，陷入了无尽的黑暗与等待之中。出狱后获得宝藏，在逍遥与复仇之间抉择，当然最后的结局是他复仇成功，那他呢？在复仇过程中，他的得与失孰多孰少呢？他失去的是那一个青春与活力的少年之心，在谋划复仇的过程中已经不复存在了。那他得到的呢？</p>
<p>所以他的上半生的关键字就是<strong>等待</strong>。</p>
<p>伯爵是一个善良的人，他宽恕了他的敌人，他原谅了背叛了他的恋人，他挽救了另一对陷入热恋的男女，同时他也拯救了自己。他不敢想一个高高在上的公主居然爱上了自己，当他带着公主乘帆远航，又回到了无边无垠的大海时，他在上半生失去的，在下半生重新得到了。人世间最幸福的事情莫过于失而复得。</p>
<p>所以他的下半生的关键字就是<strong>希望</strong>。</p>
<p>朋友们不要失去希望。</p>
<hr>
<p>我发现日本特别喜欢三国题材，不信你去看一看《银河英雄传说》。其故事架构计划和《三国演义》一毛一样，不同的是，其故事时代在未来，将战场嫁接到了太空。</p>
<p>刚开始我想看一看他能写出什么不一样的故事，现在一看，还是别了吧。看《三国演义》更加的原滋原味。</p>
<p>我在看电视剧”一计害三贤“时，看到了一条弹幕，汉大将军起于卫青而终于姜维，姜维别号“天水麒麟”。多酷啊！！！</p>
<p>而在姜维出场之前，那么多的风流人物，俱往矣！</p>
<hr>
<p>我在回乡的途中读完了《夜谭十记》的前五篇，在返程途中读完了后五篇。是巧合还是有意为之？我说不清了。</p>
<p>一群科员，一群无所事事的科员，一群喜欢乱扯谈的无所事事的科员，一群喜欢乱扯谈的无所事事的坐冷板凳的科员，最后留下了十记。</p>
<p>他们是那一个时代的缩影，流水的官老爷，铁打的科员，一成不变的是底层的劳苦大众。</p>
<p>峨眉山人，羌江钓徒，三家村夫，无是楼主，不第秀才，山城走卒，巴陵野老，穷通道士，野狐禅师，砚耕斋主，我还记得的这些科员的雅号，仿佛我也身在其中，他们带着令人心酸的往事，迎面走进屋来。</p>
<p>各位，容我先泡上一壶茶，再摆上几条板凳。来吧，开摆。</p>
<h3>一撇一捺之谓道</h3>
<p>“搬山计划”一直在继续，但是执行地断断续续。</p>
<p>我越来越喜欢写字，但是字一直写得不好看，以前信笺上的开头都写一句“见字如面”，如果我给一些朋友写信，那他们一定看不下去。</p>
<p>我知道字的结构要合理了，字才好看。用笔的力与势要中庸协调了，字的结构就合理了。但是我却不得入其门，我反思得出结果，其一练得少，其二路子野，其三任意潇洒，不循其法，不得其势，其四我再也没有那么多的精力了。</p>
<blockquote>
<p>过刚者易折，善柔者不败。</p>
</blockquote>
<p>“搬山计划”的最终目的就是训练自己，以中正平和为目标，磨练自己的冲动随性。半年多过去了，我个人认为还是得到了一些矫正。</p>
<p>一阴一阳之谓道，典出《易经》。我仔细地回味了一下，决定随意发挥一下，一撇一捺之谓道，以合方寸之间。</p>
<p>动与静两种态，就好比是阴与阳。互相成就，互相印证，此消彼长。</p>
<p>在自我磨练的过程中，我的“动”态慢慢地被消磨掉了，我的熵变低了，变得相对稳定了，换一句话说，我变得成熟了。</p>
<p>今年，我27岁了，我还很年轻，还有很多人没有遇见，很多事情没有经历。或许在不久的将来，我会选择冲冠一怒，也会选择沉默不言。</p>
<p>这是合理的，人不可能永远是被理性武装的动物，“动”和“静”就是一种动态的，互相侵略和互相抵御的过程。我甚至认为理性都是动态存在的状态，时间的不同，空间的不同，场景的不同，都是影响调整状态的元素。</p>
<h3>用到了哪些新玩意</h3>
<p><strong>redux</strong>家族中又多了一个新朋友，<strong>rematch</strong>。结构与dva相似，打算在三月份做一个音乐播放页面，与网页顶部的播放器控件联动。</p>
<p><strong>express</strong>阵营中也多了一个新朋友，<strong>nest</strong>。号称是nodejs化的spring boot，用了一下确实很简单，让我想起了很久之前的用过的angular，写起来很爽。</p>
<p>简单的玩了一下<strong>AST</strong>转换，玩的很吃力，不是很懂，有很多的问题没有解决，最大的问题就是，有很多问题出现了都不知道怎么描述我的问题，这才是最大的问题。</p>
<p><strong>github actions</strong>让自动化变得简单。github官网有一个菜单入口<strong>MarketPlace</strong>，没错里面就是一些常用的actions，具体怎么玩可以看github官方案例，也可以先看一下我的<a href="https://wuh.site/post/Github%20Actions%20%E8%87%AA%E5%8A%A8%E9%83%A8%E7%BD%B2%E5%BA%94%E7%94%A8" rel="nofollow">Github Actions 自动部署应用</a>实现笔记。</p>
<p><strong>docker</strong>让我的应用部署起飞。我的应用已经全部docker化了，可以先看一下我的实现<a href="https://wuh.site/post/Docker%E9%95%9C%E5%83%8F%E7%9A%84%E5%88%B6%E4%BD%9C%E4%B8%8E%E4%BD%BF%E7%94%A8" rel="nofollow">Docker镜像的制作与使用</a>。</p>
<p><a href="https://binaryify.github.io/NeteaseCloudMusicApi/#/?id=neteasecloudmusicapi" rel="nofollow">NeteaseCloudMusicApi</a>支持了音乐播放列表的后台动态控制。我的播放列表终于不再裸奔了。</p>
<p><strong>mongodb</strong>嗯~~暂时没什么，好像有一些新的比较好玩的工具，没时间去试。</p>
<p><strong>nginx-js</strong>可以用js的语法去配置nginx了，这个不错，我想去尝试一下，刚好马上证书到期了，这个很期待。</p>
<p><strong>webComponent</strong>仔细看了一下github的网站结构，我发现好像github早就开始使用webcomponent了。试玩了一下，感觉还是很不错的，要是可以把组件的事件代理解决一下就好啦，如同react一般地去使用component元素。</p>
<p><strong>preact</strong>相当舒服的react mini版本，基本适配了react的全部api，使用起来还是不错的。</p>
<p><strong>webpack5 和 vite</strong>今年的明星项目莫过于vite了，试了一下vite+v3感觉相当棒，顺便把之前搭过的微前端的基座应用换成了vite。</p>
<h3>B站的神奇之旅</h3>
<p>在去年我看过了一系列的纪录片，相比现在的一些电视剧，纪录片都比他们有意思。</p>
<p>卸载抖音我丝毫没有犹豫，那没什么大不了，但是b站让我很难做呀。</p>
<p>我几乎每天都混迹在B站，今年的我很幸运见证了最快百万王冰冰，可鸽可泣小约翰。美食主播盗月社带给我的是快乐，嘉佑生宣让我重新认识了那些课本上的熟人，想不到短短的一句“庆历四年春”的背后居然还有这么多的波折。</p>
<p>我喜欢《深夜食堂》，也说不出究竟喜欢哪里，或者是喜欢哪一些人，可能只是喜欢日剧夏日午后般的平淡。尤其推荐《凡人修仙传》，看的很爽，真的很不错。</p>
<p>《肖申克的救赎》是我每年必看的电影，他提醒着我无论在什么环境里面生活多久也不要放弃自己，永远保持希望，不要被环境同化。</p>
<p>《怦然心动》令人触动，我到哪里去找我的初恋，你大爷的，为什么我现在才看这部电影~~</p>
<p>《戚继光》短短的六集所展现的内容比起那些三十几集连续剧强太多了，我觉得电视剧可以退出历史舞台了，把钱拿来做方块人都比他强。</p>
<p>《湮灭》根据《遗落的南境》改编，说实话很受震撼，但是电影最后所表达的究竟是什么意思，顶塔消失了，他们已经是新人类了，始终牢记<strong>是反射而非复制</strong>。</p>
<p>更离谱的是，可以看到各大高校的课程分享，我已经在b站看过了好几个up主赏析《红楼梦》，而且讲起来条条是道，很感慨啊，我还是书读少了。因为我看到的和别人看到的差别居然这么大。</p>
<p>除此之外，我还在b站里面找到了一些专业相关的课程，有关于nodejs、mongodb、flutter和docker。学习的平台已经搭建好了，接下来就是看自己寻找靠谱的视频资料了。</p>
<p>直播行业将会是这个娱乐时代中最普遍的职业，门槛最低的职业。只需要一台手机就可以开启直播了，我试着玩了一下B站的直播，操作很简单。当然有时间会看一下游戏直播，我最近很喜欢iwanna这个游戏，注意可不是喜欢玩，是喜欢看别人玩。</p>
<h3>我在云村待了2000多个小时</h3>
<p>如何开启自我保护机制？如果没有答案，那就戴上耳机。</p>
<p>每天早上醒来，已经形成了一种习惯，首先打开网抑云，随机播放。出门时戴上耳机，隔绝了整个世界。如果可以打字交流，绝不会说话。</p>
<p>我沉迷在《金风玉露》当中无法自拔，它真的很好听~~</p>
<p>笛声悠长，散播这无尽的缠绵哀怨。我看到歌名就联想到了“金风玉露一相逢，便胜却人间无数”，不知道他在期待着什么，只知道留下的是如汉水般连绵不绝的遗憾。</p>
<p>来，拿出笛子，先来一个欢快的，让大家折磨一下。</p>
<h3>反思</h3>
<blockquote>
<p>遇到问题不暴露，暴露之后不解决，解决之后不总结，总结之后不记录，等于这个问题我从未经历，从未解决。</p>
</blockquote>
<p>各位客官，别太相信自己的大脑，它的存储保质期真不太长~~</p>
<p>现在真实地发生在自己的身上，是因为年纪的关系还是其他的问题，很多东西长时间不去接触，再次谈论的时候我发现，我的记忆是一片空白。很多问题分明之前已经解决过，但是一些重复问题还是需要去google一下。</p>
<p>所以现在遇到问题了，我会提前把问题截图记下来，相关的google链接也会记下来，提供一些简洁的描述，增强自己的记忆力。它就是我的问题检索索引，最重要的是，不要简单的复制粘贴，手敲一下增强记忆。</p>
<p><strong>不求甚解的理念不适合专业性较强的问题</strong>。业务相关、专业相关的文章应该持有的态度应该是庖丁解牛，而不是不求甚解。我现在一直在看之前群里发的一些面试题，并不是为了准备面试，而是面试题里面体现出了很多我不会，不了解的东西。我认为<strong>专精</strong>是专业性文章的指导方针。</p>
<p><strong>记录和归类</strong>貌似也是一个学科，每一个人都需要自己去寻找适合自己的方式，以达到构建一种高效的储存和读取模型。我曾经在b站上面看到了一个分类的视频，看到之后很受教育，但是我现在仍然在摸索的过程中。</p>
<p>分析问题，一般我是围绕几个点：“是什么？为什么？怎么做？”。首先分析一下他是什么类型的问题，我之前是不是也遇到过类似的问题，那样就可以快速检索得出结果。既然是与之前遇到的问题相似，那它为什么表现出了一个独有的特性，那是不是也有其他的问题延伸出来。最后已经列出了全部的线索，结合一下前期的遇到的问题和解答步骤，尝试做出针对当前问题的解答。</p>
<p><strong>从分析问题到得出结果</strong>，它是一个过程。能不能分析出什么问题是前期准备，能不能解决问题是最终结果。也许很多情景下，我的能力不足以解决我分析出来的结果，但是我相信一定有解决这些问题的高人存在。所以，有没有解决问题的能力，并不影响我尝试分析问题。</p>
<h3>阶段性结束了</h3>
<p>又一年结束了。</p>
<p>我还是一如往常，没有长进，没有改变，孤身一人。</p>
<p>乾上坤下，又一年开始了。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Github Actions 自动部署应用]]></title>
            <link>https://wuh.site/post/36</link>
            <guid>https://wuh.site/post/36</guid>
            <pubDate>Mon, 13 Dec 2021 13:59:22 GMT</pubDate>
            <description><![CDATA[认识Github Actions 什么是github actions? 我们可以使用github actions做一些什么事情?怎么实现我们的需求? 我们在日常的开发过程中，部署过程中或者是更新日志的发布，一定会重复地执行一些操作。我一直在寻找和尝试一些简单、自动化的操作，免去一些重复操作。 首先说明，我也是头一次运用github actions，不过是在完成了一些操作之后，反过头来记录一些我是怎]]></description>
            <content:encoded><![CDATA[<h2>认识Github Actions</h2>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/ab14701461467128ac467b5798454c479efcdb1bd6f3a5eb6d4439033ef8c79c/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d31332d3133353533312e706e67"><img src="https://camo.githubusercontent.com/ab14701461467128ac467b5798454c479efcdb1bd6f3a5eb6d4439033ef8c79c/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d31332d3133353533312e706e67" alt="" data-canonical-src="https://src.wuh.site/2021-12/2021-12-13-135531.png" style="max-width: 100%;"></a></p>
<blockquote>
<p>什么是github actions? 我们可以使用github actions做一些什么事情?怎么实现我们的需求?</p>
</blockquote>
<p>我们在日常的开发过程中，部署过程中或者是更新日志的发布，一定会重复地执行一些操作。我一直在寻找和尝试一些简单、自动化的操作，免去一些重复操作。</p>
<p>首先说明，我也是头一次运用github actions，不过是在完成了一些操作之后，反过头来记录一些我是怎么做的。</p>
<h3>什么是Github Actions</h3>
<p>先从一个简单的配置文件，来认识一下github actions。<code class="notranslate">.github/workflows/first.yml</code></p>
<div class="highlight highlight-source-yaml"><pre class="notranslate"><span class="pl-ent">name</span>: <span class="pl-s">Github Actions Demo First</span>

<span class="pl-ent">on</span>: <span class="pl-s">[push]</span>

<span class="pl-ent">jobs</span>:
  <span class="pl-ent">logger</span>: 
    <span class="pl-ent">runs-on</span>: <span class="pl-s">ubuntu-latest</span>
    <span class="pl-ent">steps</span>:
      - <span class="pl-ent">name</span>: <span class="pl-s"><span class="pl-pds">'</span>runner task1<span class="pl-pds">'</span></span>
        <span class="pl-ent">run</span>: <span class="pl-s">echo 'actions runner task1'</span>
      - <span class="pl-ent">run</span>: <span class="pl-s">echo 'actions runner task2'</span>
      - <span class="pl-ent">run</span>: <span class="pl-s">echo 'actions runner task3'</span></pre></div>
<p>首先文件的类型是<code class="notranslate">yaml</code>,文件名的后缀是<code class="notranslate">yml</code>，所以我们需要提前学习一下yml的语法。其语法和json的语法很相似，上面的yml文件翻译为json，其实就是这个样子:</p>
<div class="highlight highlight-source-json"><pre class="notranslate">{
  <span class="pl-ent">"name"</span>: <span class="pl-s"><span class="pl-pds">"</span>Github Actinon Demo First<span class="pl-pds">"</span></span>,
  <span class="pl-ent">"on"</span>: [<span class="pl-s"><span class="pl-pds">"</span>push<span class="pl-pds">"</span></span>],
  <span class="pl-ent">"jobs"</span>: {
    <span class="pl-ent">"logger"</span>: {
      <span class="pl-ent">"runs-on"</span>: <span class="pl-s"><span class="pl-pds">"</span>ubuntu-latest<span class="pl-pds">"</span></span>,
      <span class="pl-ent">"steps"</span>: [
        {
          <span class="pl-ent">"name"</span>: <span class="pl-s"><span class="pl-pds">"</span>runner task1<span class="pl-pds">"</span></span>,
          <span class="pl-ent">"run"</span>: <span class="pl-s"><span class="pl-pds">"</span>echo 'actions runner task1'<span class="pl-pds">"</span></span>
        },
        {
          <span class="pl-ent">"run"</span>: <span class="pl-s"><span class="pl-pds">"</span>echo 'actions runner task1'<span class="pl-pds">"</span></span>
        },
				{
          <span class="pl-ent">"run"</span>: <span class="pl-s"><span class="pl-pds">"</span>echo 'actions runner task1'<span class="pl-pds">"</span></span>
        }        
      ]
    }
  }
}</pre></div>
<p>只要是在语句之前加上<code class="notranslate">-</code>号，那么它的父级会自动解析为数组，在<code class="notranslate">:</code>之后的换行符会解析为对象。其语法对比json来讲，<strong>更加简单</strong>。</p>
<p>不出意外，其执行成功的结果就是这个样子:</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/53c43a67ad516b59d6e0b38d32d8fba679e9041eb824756704621cb709b46b2f/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30362d3132353432312e6a7067"><img src="https://camo.githubusercontent.com/53c43a67ad516b59d6e0b38d32d8fba679e9041eb824756704621cb709b46b2f/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30362d3132353432312e6a7067" alt="" data-canonical-src="https://src.wuh.site/2021-12/2021-12-06-125421.jpg" style="max-width: 100%;"></a></p>
<h3>文件的结构分析</h3>
<p>在github官方对github actions的文档描述看，上面的例子虽然简单，但是已经包含了事件流的基础元素。</p>
<ul>
<li>Workflows</li>
<li>Events</li>
<li>Jobs</li>
<li>Actions</li>
<li>Runners</li>
</ul>
<p>现在对比这么一看，跟我之前写过的<code class="notranslate">docker-compose</code>的文件结构相当相似。要知道上面的元素全部是复数，就是说可以有一个也可以有多个，只要保证了基本的结构不错误，那你可以随意的加多个任务。</p>
<p>需要注意的是，对于Jobs下的子任务，都需要指定<code class="notranslate">runs-on</code>属性，要不然会抛出：</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">ERRO[0000] <span class="pl-s"><span class="pl-pds">'</span>runs-on<span class="pl-pds">'</span></span> key not defined <span class="pl-k">in</span> Github Actions Demo First/tags </pre></div>
<p><strong>Workflows</strong>文件一般是放在<code class="notranslate">.github/workflows</code>文件夹内部，我看了好几个开源项目的文件目录大致上都是这样，因为github会自动拉取这个文件目录下以<code class="notranslate">.yml</code>结尾的文件作为任务文件。</p>
<p><strong>Events</strong>是一个关键点。下面我贴上两个地址，是github actions的官网上对于Events模块的描述。<a href="https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows">events-that-trigger-workflows</a>,<a href="https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#on">workflow-syntax-for-github-actions</a>。<code class="notranslate">on</code>可以监听到的事件官网上提供了一个索引表，和它们相关的写法和用法。同时不单单是对事件，还可以针对单独的分支进行事件监听。</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">name: Github Actions Demo Second

on: 
  push: 
    branches: 
      - main
  pull_request:
    branches:
      - main
  schedule:
    - cron: <span class="pl-s"><span class="pl-pds">'</span>/5 * * *<span class="pl-pds">'</span></span>
    
  jobs:
  	reb:
    	runs-on: ubuntu-latest
      steps: 
        - name: <span class="pl-s"><span class="pl-pds">'</span>push main<span class="pl-pds">'</span></span>
          run: <span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">'</span>push events<span class="pl-pds">'</span></span></pre></div>
<p><strong>Events.schedule</strong>定时任务。在文档上<code class="notranslate">schedule</code>出现的位相当靠前，估计这个是一个相当不错的特性。它的函数是一些云函数，官网给出了一些例子，但是我目前还没有测试，所以先放着，以后再玩。</p>
<p><strong>Jobs</strong>和它的一系列参数列表和<a href="https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#jobsjob_idenv">语法</a>。Jobs是Github Actions最核心的元素了吧，我们需要的各种各样的功能能不能实现，怎么实现就依赖于怎么实现。其中，我们比较关注就是一些环境变量问题。<code class="notranslate">env</code>提供了这一功能，另外<code class="notranslate">use</code>和<code class="notranslate">with</code>成对使用。</p>
<p>其中，介绍Jobs任务的另一种使用，串联。任务可以是单行，也可以是多个任务有先后、互相依赖的关系存在。所以在github actions中提供了<strong>jobs.jobs_id.needs</strong> 这个配置项，让我们可以个性化配置。使用方法也简单</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">jobs: 
	reb: 
		runs-on: ubuntu-latest
		needs:
			- first_task
			_ second_task
		steps:
			- name: <span class="pl-s"><span class="pl-pds">'</span>thrid_task<span class="pl-pds">'</span></span>
			- run: <span class="pl-c1">echo</span> <span class="pl-s"><span class="pl-pds">'</span>thrid_task<span class="pl-pds">'</span></span></pre></div>
<p><code class="notranslate">jobs.jobs_id.runs-on</code>表示在哪一个虚拟机执行任务。 现在提供的虚拟机，可能我们日常使用<code class="notranslate">ubuntu</code>就够了。它有以下几个选项，可能以后还会再增加。</p>
<markdown-accessiblity-table><table role="table">
<thead>
<tr>
<th>虚拟机</th>
<th>Runs-on label</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows Server 2022</td>
<td>windows-2022</td>
</tr>
<tr>
<td>Windows Server 2019</td>
<td>windows-latest</td>
</tr>
<tr>
<td>Windows Server 2016</td>
<td>windows-2016</td>
</tr>
<tr>
<td>Ubuntu 20.04</td>
<td>ubuntu-latest</td>
</tr>
<tr>
<td>Ubunti 18.04</td>
<td>ubuntu-18.04</td>
</tr>
<tr>
<td>macOS Big Sur 11</td>
<td>macos-11</td>
</tr>
<tr>
<td>macOS Catalina 10.15</td>
<td>macos-latest</td>
</tr>
</tbody>
</table></markdown-accessiblity-table>
<h3>Docker 的自动编译和推送</h3>
<blockquote>
<p>这是一个github 官方提供的例子，actions来自于docker官方仓库。</p>
<p>可以查看我的<a href="https://github.com/stack-wuh/wuh.site/blob/main/.github/workflows/docker-push.yml">编译yml</a>，也可以直接查看github<a href="https://docs.github.com/cn/actions/publishing-packages/publishing-docker-images#">官方例子</a>。对比一下，你就会发现没有什么太大的差异，只是我换成了分支名，而github例子使用的hashid。</p>
</blockquote>
<p>因为需要上传Docker Image镜像到Docker Hub，可以使用由docker官方提供的几个actions：</p>
<ol>
<li>docker/login-action 登录用，只有登录状态下才可以推送镜像</li>
<li>docker/metadata-actions 配置image的版本号</li>
<li>docker/build-push-action 编译和推送</li>
</ol>
<h3>Release 版本更新</h3>
<p>不得不承认，有很多的工作或者是操作都可以用一些工具自动生成。比如，我现在项目的更新日志，仍然还是在本地执行脚本，生成日志之后再推送到github。</p>
<p>每一次更新，发布release版本时，还需要把本次更新的内容复制粘贴到版本说明中。看起来可以改变一下，让这一步工作自动化进行。</p>
<p>版本更新CI/CD的一个重要环节，其价值不可谓不巨大，可以极其方便地控制一些手动操作。尽管现在有一个工具可以了本地测试，但是还是有缺陷，在我做完了全部的流程之后，我回过头来仔细分析了一下。我之所以干的这么不顺利，就是因为我不太了解linux和虚拟机相关的知识。</p>
<p>另外不太熟悉github actions相关的进阶的玩法。由此可见，我这种英语不行，我看着文档上面例子，也可以做出一些我想要的功能。另外也归功于github actions的仓库，里面准备了一大批日常使用的功能，完全解决了我的需求。</p>
<p>现在介绍使用的actions:</p>
<pre class="notranslate"><code class="notranslate">+ **actions/checkout@v2** 应该算是使用频率最高的actions了吧，只要是涉及到了git相关的操作就离不开它
+ actions/setup-node@v1 选择任务执行的nodejs版本，可以控制其版本号
+ **release-drafter/release-drafter@v5** 生成release notes的一个actions，可以生成这个版本号的提交日志
+ **appleboy/ssh-action@master** 服务器登录执行指令
</code></pre>
<p>注意:</p>
<pre class="notranslate"><code class="notranslate">1. 在使用release-drafter的过程中，必须使用pull_request 这样才可以拿到每一次分支的更新日志
1. appleboy/ssh-action 需要注意的是有多种连接服务器的方式，如果使用的用户名+密码，需要查看远程服务器的是否支持连接
1. 如果条件允许，还是拆分多个任务比较好，不要都写在一个任务里面
</code></pre>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ClashX 的使用记录]]></title>
            <link>https://wuh.site/post/35</link>
            <guid>https://wuh.site/post/35</guid>
            <pubDate>Sun, 05 Dec 2021 02:17:45 GMT</pubDate>
            <description><![CDATA[ClashX 的使用记录 我之前用的是google浏览器的的一个插件，大多数朋友可能都有用过，就是GHelper。 但是在公司内部，不知道出于什么原因，也可能是内网的原因，这个插件失效了。所以只能另寻他法，终于找到了一个几乎不用啥配置的工具，但是也需要交一点点保护费。 Mac上使用 在Mac上使用特别的简单，因为我本身的意愿就是找一个零配置的工具。 而且，Clashx在Mac上的表现也相当的亮眼，]]></description>
            <content:encoded><![CDATA[<h2>ClashX 的使用记录</h2>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/ce5b9ef314ce17515db58ad78bd65ff4ffcfc02c7940ab3a9702f1d289ddcaaf/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30352d3032313530372e706e67"><img src="https://camo.githubusercontent.com/ce5b9ef314ce17515db58ad78bd65ff4ffcfc02c7940ab3a9702f1d289ddcaaf/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30352d3032313530372e706e67" alt="" data-canonical-src="https://src.wuh.site/2021-12/2021-12-05-021507.png" style="max-width: 100%;"></a></p>
<p>我之前用的是google浏览器的的一个插件，大多数朋友可能都有用过，就是<strong>GHelper</strong>。</p>
<p>但是在公司内部，不知道出于什么原因，也可能是内网的原因，这个插件失效了。所以只能另寻他法，终于找到了一个几乎不用啥配置的工具，但是也需要交一点点保护费。</p>
<h3>Mac上使用</h3>
<p>在Mac上使用特别的简单，因为我本身的意愿就是找一个零配置的工具。</p>
<p>而且，Clashx在Mac上的表现也相当的亮眼，颜值还是不错的。</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/2621c95f5370fa5707afb64b5490c01b518d0c2026162a9970888207efeb7095/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d31322d30342d3032303031382e6a7067"><img src="https://camo.githubusercontent.com/2621c95f5370fa5707afb64b5490c01b518d0c2026162a9970888207efeb7095/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d31322d30342d3032303031382e6a7067" alt="clashx-setting" data-canonical-src="https://src.wuh.site/2021-08/2021-12-04-020018.jpg" style="max-width: 100%;"></a></p>
<p>在交保护费的平台拿到clash的订阅地址，点开菜单栏的猫头，找到配置-&gt;托管配置-&gt;管理。把你的clash订阅地址贴到管理里面，之后更新列表，不出意外的话，在成功更新之后就可以看到代理服务器列表了。<br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/1eb03d468dd6c1f9f3e67a412f5f6a5b48a3b04b9c4c3f5137ae31d48824e958/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d3032343135372e706e67"><img src="https://camo.githubusercontent.com/1eb03d468dd6c1f9f3e67a412f5f6a5b48a3b04b9c4c3f5137ae31d48824e958/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d3032343135372e706e67" alt="dingyue" data-canonical-src="https://src.wuh.site/2021-12/2021-12-04-024157.png" style="max-width: 100%;"></a></p>
<p>我选择的代理模式是规则模式，打开系统代理之后。打开浏览器输入google看一下，不出意外现在是可以访问外网了。</p>
<p>这是不同于win10平台的一点，在window平台上，用浏览器的无痕模式可以直接访问外网，但是普通模式不可以，还需要用switchomage这个插件做转发。<strong>真是有够麻烦的</strong>。</p>
<p>最后，附上clashx在Mac平台的下载地址<a href="https://github.com/yichengchen/clashX/releases/download/1.72.0/ClashX.dmg" title="clashX">clashX</a>，版本号是<strong>1.72.0</strong>。在Mac上使用极其简单的，几乎没有费什么工夫就成功的跑了起来，完全没有遇到什么麻烦。</p>
<h3>window10 的 Clash</h3>
<p>在window平台的上的客户端不再是ClashX，而是Clash。都是一家人没什么太大的区别，都是基于go。不得不说，在各个平台使用Clash都很简单，几乎没遇到什么大的麻烦，但是在window上需要多处理一点。</p>
<p>在这里附上Clash的下载地址<a href="https://github.com/Fndroid/clash_for_windows_pkg/releases" title="clash_for_window">clash_for_window</a>(<a href="https://github.com/Fndroid/clash%5C_for%5C_windows%5C_pkg/releases)%E3%80%82%E8%AF%A5%E9%93%BE%E6%8E%A5%E6%9D%A5%E8%87%AA%E4%BA%8E**github**%EF%BC%8C%E8%AF%B7%E6%94%BE%E5%BF%83%E4%BD%BF%E7%94%A8%E3%80%82">https://github.com/Fndroid/clash\_for\_windows\_pkg/releases)。该链接来自于**github**，请放心使用。</a></p>
<p>首先，如果没有买飞机票，请先交保护费买一张飞机票，获得clash的订阅地址。另外还需要给google浏览器装一个插件<strong>Proxy switchyOmega</strong>。</p>
<h3>Android 的 Clash</h3>
<p>第一贴上<a href="https://github.com/Kr328/ClashForAndroid/releases">clash_for_android</a>的下载地址：<a href="https://github.com/Kr328/ClashForAndroid/releases%EF%BC%8C%E8%AF%A5%E9%93%BE%E6%8E%A5%E6%9D%A5%E8%87%AA%E4%BA%8E**github**%EF%BC%8C%E8%AF%B7%E6%94%BE%E5%BF%83%E4%BD%BF%E7%94%A8%E3%80%82">https://github.com/Kr328/ClashForAndroid/releases，该链接来自于**github**，请放心使用。</a></p>
<p>在进入app之后，引入眼帘还是熟悉的一个黑色的猫头。接下来的操作还是老一套，点开配置进入配置页之后，点击右上角**+**图标，会进入“创建配置”页。进入”创建配置”页之后，选择从URL导出，这里就是贴上clash的订阅地址。</p>
<p>完成这些操作之后，回到首页。看一下第一栏的文案，如果是“运行中”就表示已经正常代理了，打开手机浏览器，进入google浏览器，或者是打开twitter。如果是“已停止”就点击启动，不出什么以外，就可以正常使用了。</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/ede2f24ac14d9ec06716241e3e0688b1c2ec755b7d05fff4fb806d2736abaecc/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d323032313132303430322e706e67"><img src="https://camo.githubusercontent.com/ede2f24ac14d9ec06716241e3e0688b1c2ec755b7d05fff4fb806d2736abaecc/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d323032313132303430322e706e67" alt="" data-canonical-src="https://src.wuh.site/2021-12/2021-12-04-2021120402.png" style="max-width: 100%;"></a><br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/fed7357fc182cfae24611a031750d7eae20700853aa6aa283e2cd44aed234e26/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d32303231313132303430332e706e67"><img src="https://camo.githubusercontent.com/fed7357fc182cfae24611a031750d7eae20700853aa6aa283e2cd44aed234e26/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d32303231313132303430332e706e67" alt="" data-canonical-src="https://src.wuh.site/2021-12/2021-12-04-20211120403.png" style="max-width: 100%;"></a></p>
<p>手机上的操作就这么简单。</p>
<p>只要是找的源足够靠谱，那代理的网速应该差不了多少，足够日常的使用。</p>
<p>注意：</p>
<ol>
<li><a href="https://merlinblog.xyz/wiki/ClashX.html" title="macos clashx教程" rel="nofollow">clashx教程 — MacOS</a></li>
<li><a href="https://youyun222.net/user" title="优云666" rel="nofollow">飞机场-优云666</a></li>
<li><a href="http://www.caogon.com/auth/register" title="飞世云" rel="nofollow"> 飞机场-飞世云 </a></li>
</ol>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[基于TS重构NextJs应用]]></title>
            <link>https://wuh.site/post/34</link>
            <guid>https://wuh.site/post/34</guid>
            <pubDate>Sun, 05 Dec 2021 01:54:06 GMT</pubDate>
            <description><![CDATA[基于TS重构NextJs应用 在10.1的假期中，我用TS重写了博客的前端应用，同时用sass改写了全部组件的样式。对于组件的设计，全部借鉴于ant design的设计规范和开发规范。 第一版的Next应用写的不是很满意，很多代码和实现都不是我喜欢的那一种。恰逢10.1假期，改写了大部分的组件的代码。 由于近期的工作比较多，平时又很累一直没顾得上写一写，这一次重写遇到的问题和解决的方法，以及对整个]]></description>
            <content:encoded><![CDATA[<h2>基于TS重构NextJs应用</h2>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/a2d45de7095cfbf8f0af5f1f56dffa7958c0b0c61de888f654ac01da28ab075c/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30352d3032303133362e706e67"><img src="https://camo.githubusercontent.com/a2d45de7095cfbf8f0af5f1f56dffa7958c0b0c61de888f654ac01da28ab075c/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30352d3032303133362e706e67" alt="" data-canonical-src="https://src.wuh.site/2021-12/2021-12-05-020136.png" style="max-width: 100%;"></a></p>
<blockquote>
<p>在10.1的假期中，我用TS重写了博客的前端应用，同时用sass改写了全部组件的样式。对于组件的设计，全部借鉴于ant design的设计规范和开发规范。</p>
</blockquote>
<p>第一版的Next应用写的不是很满意，很多代码和实现都不是我喜欢的那一种。恰逢10.1假期，改写了大部分的组件的代码。</p>
<p>由于近期的工作比较多，平时又很累一直没顾得上写一写，这一次重写遇到的问题和解决的方法，以及对整个应用工程化的实现的记录。现在已经过去了两个多月了，很多细节已经回想不起来了。没办法，只能看着现在的代码总结一下。</p>
<h3>提交规范</h3>
<p>无论我们做任何项目，从规范代码提交的开始，应该是对整个项目最负责的体现。</p>
<p>不知道从什么时候开始，我个人的提交规范就朝着angular的方向了。我记得当时我看了一篇讲述angular提交规范的博客，当时一看顿时觉得仪式感太强了，整个commit记录一眼看过去相当整齐，从那时起，我觉得angular规范是最好的。</p>
<p>代码规范和样式美化选用 <em>prettier</em> 和 <em>eslint</em> 。代码的样式美化和统一，给我的感觉就是舒服。这里没什么可以说的，网上的教程一大把，但是我偷懒了，对于react的规则，我用是的umijs的规则，当然nextjs自身的规则继续保留。</p>
<p>只要是严格的遵循了angular的提交规范，就可以使用<a href="https://github.com/conventional-changelog/conventional-changelog"> conventional-changelog </a>工具，快速的生成本次发布的版本的更新日志，这里附上近期的<a href="https://github.com/stack-wuh/wuh.site/releases">更新日志</a>。大致就是以下这个样子，看起来就是满满的仪式感。</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/41e9f979fd56cd57e4fa824499c45d2dfc285e589d604c6fb9247d662225b33a/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d3037303330362e706e67"><img src="https://camo.githubusercontent.com/41e9f979fd56cd57e4fa824499c45d2dfc285e589d604c6fb9247d662225b33a/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d3037303330362e706e67" alt="" data-canonical-src="https://src.wuh.site/2021-12/2021-12-04-070306.png" style="max-width: 100%;"></a></p>
<p>Github Actions用起来的感觉就是一个字，太爽了！之前一直不会用，一直在研究到底是怎么玩的。直到看阮老师的博客，发现了一个新的工具<a href="https://github.com/nektos/act">act</a>，可以在本地调试github actions。可以监听git的merge事件，来做一些自动化操作。</p>
<p>所以现在，我把主分支切成了main分支，每一次的功能迭代改为pull merge操作。因为我看到了next的官网上有推荐，可以使用Vercel持续集成应用，这样每一次的merge操作就可以自动发布到Vercel容器了。</p>
<h3>组件的设计规范</h3>
<p>基本原则还是遵照我之前的总结<a href="https://wuh.site/post/%E6%B5%85%E5%B0%9D%E4%B8%80%E4%B8%8BUI%E8%AE%BE%E8%AE%A1" rel="nofollow">《浅尝一下UI设计》</a>，基本的设计不变，但是改成sass函数之后，整个css部分变得更加地整齐、清晰和简单。</p>
<p>这里不得不提到sass中的函数和混入，在sass的官网中有它们相当具体地实现。这次我用到的基本都是Map类型操作。我举一个相当简单的用例，实现组件的大中小三种类型的样式函数。文件名: <code class="notranslate">vars.scss</code></p>
<div class="highlight highlight-source-css-scss"><pre class="notranslate">	<span class="pl-v">$font-size-base</span>: <span class="pl-c1">14<span class="pl-k">px</span></span>;
	<span class="pl-v">$compose-base</span>: <span class="pl-c1">8<span class="pl-k">px</span></span>;
	<span class="pl-v">$sizes</span>: ((<span class="pl-c1">small</span>, <span class="pl-v">$font-size-base</span> <span class="pl-k">-</span> <span class="pl-c1">2<span class="pl-k">px</span></span>, <span class="pl-v">$compose-base</span> <span class="pl-k">*</span> <span class="pl-c1">0.5</span>),
	  (<span class="pl-c1">middle</span>, <span class="pl-v">$font-size-base</span>, <span class="pl-v">$compose-base</span>),
	  (<span class="pl-c1">large</span>, <span class="pl-v">$font-size-base</span><span class="pl-c1">+</span><span class="pl-c1">2<span class="pl-k">px</span></span>, <span class="pl-v">$compose-base</span><span class="pl-c1">*</span><span class="pl-c1">2</span>));
	
	<span class="pl-k">@mixin</span> <span class="pl-en">getSizes</span> () {
	
	  <span class="pl-k">@each</span> <span class="pl-v">$size</span>,
	  <span class="pl-v">$fontsize</span>,
	  <span class="pl-v">$padding</span> <span class="pl-k">in</span> <span class="pl-v">$sizes</span> {
	    <span class="pl-e">.is-<span class="pl-v">#{<span class="pl-v">$size</span>}</span></span> {
	      <span class="pl-c1"><span class="pl-c1">font-size</span></span>: <span class="pl-v">$fontsize</span>;
	      <span class="pl-c1"><span class="pl-c1">padding</span></span>: <span class="pl-v">$padding</span>;
	    }
	  }
	}</pre></div>
<p>在调用<code class="notranslate">getSizes</code>函数之后，自动生成了<code class="notranslate">.is-small</code>、<code class="notranslate">.is-middle</code>和<code class="notranslate">.is-large</code>三个类名，对应着组件的三种大小状态。在此处我只定义了<code class="notranslate">基本字体大小</code>和<code class="notranslate">内边距</code>两种属性，如果需要扩展属性，可以很轻易地实现。</p>
<p>其编译之后的代码如下:<br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/fca685e6ce01f731288fe1e2e2e810fa0b5f409864c595b6056177205ff5d95d/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d3037333531362e706e67"><img src="https://camo.githubusercontent.com/fca685e6ce01f731288fe1e2e2e810fa0b5f409864c595b6056177205ff5d95d/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d3037333531362e706e67" alt="" data-canonical-src="https://src.wuh.site/2021-12/2021-12-04-073516.png" style="max-width: 100%;"></a></p>
<p>所以，我们可以借助类似的一些自定义函数，在快速完成我们样式代码的同时减少代码量。less和sass其功能相差不大，基本功能都可以实现，具体的一些功能函数可以参考ant的代码实现。</p>
<h3>React.FC 全函数式组件的实现</h3>
<p>完全舍弃类组件，全部由函数式组件构建。</p>
<p>如果你不会做，不妨跟着会做的人做。</p>
<p>在本次更新中，很多组件都是模仿ant。包括对于TS的使用下的React组件定义，自定义组件对外暴露的属性，组件内部classname的命名和条件渲染，大量代码都借鉴了ant。</p>
<p>Hooks的实现借鉴了另一个开源库<code class="notranslate">ahooks</code>，但是hooks文件的规则还是沿用umijs。</p>
<p>当然，audio的单例模式实现也发生了一些变化。在init阶段的单例实现没有任何变化，但是我创建了一个全局的context，在这个context中新建了ref来保存audio实例。整个应用似乎不太需要Redux。</p>
<p>主题切换，不再使用原方案，改为使用脚本实现。同时，bubble泡泡也改为使用脚本实现。你可以直接下载<a href="https://src.wuh.site/scripts/bubble.js" rel="nofollow">bubble.js</a>(<a href="https://src.wuh.site/scripts/bubble.js)%E8%84%9A%E6%9C%AC%E4%BD%93%E9%AA%8C%E3%80%82" rel="nofollow">https://src.wuh.site/scripts/bubble.js)脚本体验。</a></p>
<h3>Accessibility 可访问性指标</h3>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/d923c21f7405672a34bed82029d6b910195c959d4416dc9d609a0ed5ca75c6b5/68747470733a2f2f7372632e7775682e736974652f636f6d6d6f6e2f617269612e706e67"><img src="https://camo.githubusercontent.com/d923c21f7405672a34bed82029d6b910195c959d4416dc9d609a0ed5ca75c6b5/68747470733a2f2f7372632e7775682e736974652f636f6d6d6f6e2f617269612e706e67" alt="aria" data-canonical-src="https://src.wuh.site/common/aria.png" style="max-width: 100%;"></a><br>
在google浏览器的性能测试工具中，就有_Accessibility_指标。在ant的实现中，我们可以发现<code class="notranslate">role</code> 和 <code class="notranslate">aria-*</code> 属性。当时我看到的时候很迷惑，因为我从来没见到过html元素的属性中，有role这个属性。知道后来我听了_《重学前端》_这门课，我才知道原来是这么回事。</p>
<p>除此之外，还有键盘的可访问性，那就是<code class="notranslate">tabindex</code>。其对应的键盘按件就是<code class="notranslate">tab</code>键。在对一个html元素配置该属性之后，就可以通过<code class="notranslate">tab</code>键聚焦。如下:</p>
<div class="highlight highlight-text-html-basic"><pre class="notranslate">  <span class="pl-kos">&lt;</span><span class="pl-ent">div</span> <span class="pl-c1">tabindex</span>='<span class="pl-s">0</span>'<span class="pl-kos">&gt;</span>hello<span class="pl-kos">&lt;/</span><span class="pl-ent">div</span><span class="pl-kos">&gt;</span></pre></div>
<p>我们都知道在Html5 中发布了一些新的元素，这一些元素被称之为<strong>语义化标签</strong>，我记得在当时，我是完全用section标签取代了div标签。现在我的大部分标签改回了div。</p>
<p>所以，在一轮的重构中，我对大量的元素加入了role属性，覆盖率大致上达到了80%。只要是涉及到了可交互的组件基本上完成了配置。页面可点击按钮全部换成button元素和a元素。</p>
<p>或许，我们都应该听老师傅的话：<strong>如果你不知道的怎么用语义化标签，那就不要用</strong></p>
<h3>App的结构化优化，NextSEO和点击事件上报</h3>
<p>一个网站的SEO是极其重要的，所以在一次的重构中，我选择了<strong>NextSEO</strong>库，不再使用自己维护的代码实现。这个仓库满足了我全部的需求，因为我只需要配置google结构化，标题，关键字这一些属性，其他的我并不需要。</p>
<p>Gtag可以完成满足我的上报需求。首先我对页面的按钮进行了基础分类，大致分为了: <code class="notranslate">normal</code>、<code class="notranslate">loadmore</code>、<code class="notranslate">link</code>、<code class="notranslate">share</code>、<code class="notranslate">behivor</code>。这样，我完成了页面点击按钮的事件上报。</p>
<p>接口上报还没来得及做，现在我的服务后台换成了Nest应用，很多功能都没有时间去做。</p>
<h3>自动化构建，Docker的一键更新和发布</h3>
<p>我优化了<code class="notranslate">Dockerfile</code>构建文件，改成了分段式构建。</p>
<p>早前我是自己的野路子，能把应用跑起来就行，但是后来我发现，构建的Docker镜像太大了，感觉告诉我有点点不太对劲儿。在我改了构建文件之后，现在我的镜像的体积是91M。</p>
<p>构建步骤大致分为三个阶段: 初始化、构建、运行时。</p>
<p>本次升级内容，如下：</p>
<ol>
<li>不再使用npm, 改为使用yarn</li>
<li>改为使用ali的镜像源，减少依赖的安装时间</li>
<li>安装<code class="notranslate">jq</code>一个json文件的解析工具</li>
<li>改为分段式构建</li>
</ol>
<div class="highlight highlight-source-shell"><pre class="notranslate"><span class="pl-c"><span class="pl-c">#</span> ==================== Deps ==============</span>
<span class="pl-c"><span class="pl-c">#</span> =================== 安装依赖 =============</span>
FROM mhart/alpine-node as deps
LABEL maintainer = <span class="pl-s"><span class="pl-pds">"</span>shadow [wuh131420@gmail.com][6]<span class="pl-pds">"</span></span>
  
WORKDIR /usr/src/app
  
COPY package.json yarn.lock /usr/src/app/
  
RUN apk add jq
RUN npm config <span class="pl-c1">set</span> registry https://registry.npmmirror.com
RUN yarn
  
<span class="pl-c"><span class="pl-c">#</span> ================== Builder ================</span>
<span class="pl-c"><span class="pl-c">#</span> ================= 编译 ===================</span>
FROM mhart/alpine-node as builder
WORKDIR /usr/src/app
  
.......
  
<span class="pl-c"><span class="pl-c">#</span> ============== Runing ===========</span>
<span class="pl-c"><span class="pl-c">#</span> ============== 运行时 ===========</span>
FROM mhart/alpine-node AS runner
WORKDIR /usr/src/app
  
ENV NODE<span class="pl-cce">\_</span>ENV production
...
  
USER nextjs
  
EXPOSE 3000
  
CMD [<span class="pl-s"><span class="pl-pds">"</span>yarn<span class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>start<span class="pl-pds">"</span></span>]</pre></div>
<p>更为关键的是自动构建，自动升级和镜像清理脚本，结合一下<code class="notranslate">github actions</code>，一切都变得舒服了起来。</p>
<h3>RSS 聚合内容的实现</h3>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/0e67b62a158b2aa3d105f478e916dc28247656278d7af7765b4af349720eedfa/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d3038343031312e706e67"><img src="https://camo.githubusercontent.com/0e67b62a158b2aa3d105f478e916dc28247656278d7af7765b4af349720eedfa/68747470733a2f2f7372632e7775682e736974652f323032312d31322f323032312d31322d30342d3038343031312e706e67" alt="" data-canonical-src="https://src.wuh.site/2021-12/2021-12-04-084011.png" style="max-width: 100%;"></a></p>
<p>只要有优质的订阅源，我们完全可以只使用RSS阅读器，就可以享受阅读的乐趣。它是一个信息板，只关注我想关注的内容。所以现在的一些博客网站都会配置rss。</p>
<p>怎么配置实现rss就成了我们需要关注的内容啦。</p>
<p>在这里我推荐使用<a href="https://www.npmjs.com/package/feed" title="rss-feed" rel="nofollow">Feed</a>(<a href="https://www.npmjs.com/package/feed)%EF%BC%8C%E4%BD%86%E6%98%AF%E6%88%91%E7%9A%84%E5%AE%9E%E7%8E%B0%E7%A8%8D%E6%9C%89%E4%B8%8D%E5%90%8C%E3%80%82" rel="nofollow">https://www.npmjs.com/package/feed)，但是我的实现稍有不同。</a></p>
<p>每一次更新博客之后，都需要更新维护的rss.xml文件，只有这个文件更新了，订阅器才会有更新推送。现在我把这个功能集成到了我的管理后台，我的feed文件托管在aliOSS，这个是我的<a href="https://src.wuh.site/common/rss.xml" rel="nofollow">feed</a>(<a href="https://src.wuh.site/common/rss.xml)%E3%80%82" rel="nofollow">https://src.wuh.site/common/rss.xml)。</a></p>
<h3>web worker 来一个自动切换主题的实现</h3>
<p>我用 <code class="notranslate">web worker</code> 注册一个时间轮询器，用于获取当前设备的时间。白天使用<code class="notranslate">light</code>模式，晚上变为<code class="notranslate">dark</code>模式。怎么让它们实现自动切换就是一个比较指的研究的问题啦。</p>
<p>难道要在主线程中加一个定时器，让它每次30分钟更新一次吗？</p>
<p>或许，有别的方式。在不占用主线程的资源的前提下，还可以监听时间的变化，在满足条件的前提下，切换网站的主题模式。</p>
<p>这是一个相当简单的实现，如果你感兴趣，可以在项目的源文件里面，查看相关的代码。</p>
<h3>图片格式转化为webp</h3>
<p><code class="notranslate">.webp</code>类型的文件，从文件可压缩的大小的比率上看具有相当的优势。但是目前为止，我只是把网站的背景图片和部分封面图改为了<code class="notranslate">.wepb</code>，大部分文件仍然还是<code class="notranslate">.png</code>。</p>
<p>来自于h5的新标签<code class="notranslate">picture</code>，提供了非常棒的降级方案，只需要配置额外的基础源就可以放心使用webp文件。</p>
<p>看起来，需要实现一下转换图片的格式了</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ssh免密登录]]></title>
            <link>https://wuh.site/post/33</link>
            <guid>https://wuh.site/post/33</guid>
            <pubDate>Sun, 07 Nov 2021 10:07:17 GMT</pubDate>
            <description><![CDATA[ssh免密登录 免密登录，用到的就是一个公钥。如果之前在机器上用过github的ssh模式，那你一定还记得，秘钥生成到哪一个文件夹下了。 用到的指令就一个 ssh-keygen,几乎可以不用加任何参数，直接生成一对key文件: idrsa.pub和idrsa 在本地生成的key文件，推送到服务器上，在此之前先看下服务器的ssh服务开启了没有，没有开启的打开就好。 在服务器上，设置一下.ssh和au]]></description>
            <content:encoded><![CDATA[<h2>ssh免密登录</h2>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/3cb10b2f7527edfea3870771769b01a7eb45800d8da2032589d2ff5711fea062/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d31312d30372d7373682e3030312e6a706567"><img src="https://camo.githubusercontent.com/3cb10b2f7527edfea3870771769b01a7eb45800d8da2032589d2ff5711fea062/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d31312d30372d7373682e3030312e6a706567" alt="" data-canonical-src="https://src.wuh.site/2021-08/2021-11-07-ssh.001.jpeg" style="max-width: 100%;"></a></p>
<p>免密登录，用到的就是一个公钥。如果之前在机器上用过github的ssh模式，那你一定还记得，秘钥生成到哪一个文件夹下了。</p>
<p>用到的指令就一个 <strong>ssh-keygen</strong>,几乎可以不用加任何参数，直接生成一对key文件: <code class="notranslate">id_rsa.pub</code>和<code class="notranslate">id_rsa</code></p>
<div class="highlight highlight-source-shell"><pre class="notranslate">ssh-keygen --help

 usage: ssh-keygen [-q] [-b bits] [-C comment] [-f output_keyfile] [-m format]
                  [-N new_passphrase] [-t dsa <span class="pl-k">|</span> ecdsa <span class="pl-k">|</span> ed25519 <span class="pl-k">|</span> rsa]
       ssh-keygen -p [-f keyfile] [-m format] [-N new_passphrase]
                   [-P old_passphrase]
       ssh-keygen -i [-f input_keyfile] [-m key_format]
       ssh-keygen -e [-f input_keyfile] [-m key_format]
       ssh-keygen -y [-f input_keyfile]
       ssh-keygen -c [-C comment] [-f keyfile] [-P passphrase]
       ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]
       ssh-keygen -B [-f input_keyfile]
       ssh-keygen -D pkcs11
       ssh-keygen -F hostname [-lv] [-f known_hosts_file]
       ssh-keygen -H [-f known_hosts_file]
       ssh-keygen -R hostname [-f known_hosts_file]
       ssh-keygen -r hostname [-g] [-f input_keyfile]
       ssh-keygen -G output_file [-v] [-b bits] [-M memory] [-S start_point]
       ssh-keygen -f input_file -T output_file [-v] [-a rounds] [-J num_lines]
                  [-j start_line] [-K checkpt] [-W generator]
       ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider]
                  [-n principals] [-O option] [-V validity_interval]
                  [-z serial_number] file ...
       ssh-keygen -L [-f input_keyfile]
       ssh-keygen -A [-f prefix_path]
       ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]
                  file ...
       ssh-keygen -Q -f krl_file file ...
       ssh-keygen -Y check-novalidate -n namespace -s signature_file
       ssh-keygen -Y sign -f key_file -n namespace file ...
       ssh-keygen -Y verify -f allowed_signers_file -I signer_identity
       		-n namespace -s signature_file [-r revocation_file]</pre></div>
<p>在本地生成的key文件，推送到服务器上，在此之前先看下服务器的ssh服务开启了没有，没有开启的打开就好。</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">systemctl status sshd

systemctl start sshd</pre></div>
<p>在服务器上，设置一下<code class="notranslate">.ssh</code>和<code class="notranslate">authorized_keys</code>的权限，网上有部分资料将权限设置之后，免密登录才生效。</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">sudo chmod 700 <span class="pl-k">~</span>/.ssh
sudo chmod 600 <span class="pl-k">~</span>/.ssh/authrized_keys</pre></div>
<p>设置之后，测试一下，看看免密登录能不能成功：</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">ssh root@101.101.101.101</pre></div>
<p>只要可以正常进入云服务器就说明已经生效了，如果还不行的，检查一下配置文件的路径或者是权限。</p>
<p>最后，在本地的<code class="notranslate">.ssh</code>目录，新增一个配置文件<code class="notranslate">config</code>:</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">Host aliyun
    user root 
    hostname 192.168.1.1 
    IdentityFile /Users/root/.ssh/id_rsa  (密钥的本地地址)</pre></div>
<p>执行一下<code class="notranslate">ssh aliyun</code>，就可以登录云服务器了。</p>
<h3>参考资料</h3>
<ul>
<li><a href="https://segmentfault.com/a/1190000021000360" rel="nofollow">SSH 免密登陆配置</a></li>
</ul>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Docker镜像的制作与使用]]></title>
            <link>https://wuh.site/post/32</link>
            <guid>https://wuh.site/post/32</guid>
            <pubDate>Sun, 29 Aug 2021 12:21:43 GMT</pubDate>
            <description><![CDATA[Docker镜像的制作与使用 docker-hub 最近github的速度是在是太慢了，实在是受不了了，所以只好把镜像的编译工作放在了本地。在本地制作镜像然后托管到dockerhub，直接使用线上镜像了。 换一句话描述: 之前在服务器做镜像，现在在本地做镜像。 首先，介绍一下，这次更换部署方式用了哪一些工具和一些使用的坑，更多关于使用的方法和问题的排查。 1. docker 2. docker-c]]></description>
            <content:encoded><![CDATA[<h2>Docker镜像的制作与使用</h2>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/2287238bd41ec4ff2bbda58b46d8dce43e024685769c2f4c8d951883e3a2bc68/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3039303231302e706e67"><img src="https://camo.githubusercontent.com/2287238bd41ec4ff2bbda58b46d8dce43e024685769c2f4c8d951883e3a2bc68/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3039303231302e706e67" alt="docker-hub" data-canonical-src="https://src.wuh.site/2021-08/2021-08-29-090210.png" style="max-width: 100%;"></a></p>
<p>最近github的速度是在是太慢了，实在是受不了了，所以只好把镜像的编译工作放在了本地。在本地制作镜像然后托管到dockerhub，直接使用线上镜像了。</p>
<p><strong>换一句话描述: 之前在服务器做镜像，现在在本地做镜像。</strong></p>
<p>首先，介绍一下，这次更换部署方式用了哪一些工具和一些使用的坑，更多关于使用的方法和问题的排查。</p>
<ol>
<li>docker</li>
<li>docker-compose</li>
<li>Bash</li>
<li>Npm/yarn &amp; nodejs</li>
</ol>
<p><strong>基本的流水线节点</strong>大致如下:</p>
<ol>
<li>第一步: 本地制作镜像文件</li>
<li>第二步: 推送镜像文件至远程dockerhub</li>
<li>第三步：服务器更新镜像文件</li>
<li>第四步：重启容器，重启nginx</li>
</ol>
<p>连接服务器更新的脚本，没有提交到github，但是我会讲一下我是怎么做的。</p>
<h3>docker</h3>
<p>具体的安装流程可以百度一下，网上有很多的教程，就不具体说了。这里贴上一些我经常查看的一些网站，当然都是关于docker的。</p>
<ol>
<li><a href="https://docs.docker.com/engine/reference/run/" rel="nofollow">docker-run</a>(docker的官方文档，当前链接为docker的指令集文档)</li>
<li><a href="https://yeasy.gitbook.io/docker_practice/compose/introduction" rel="nofollow">docker从入门到实践</a>(中文版的docker文档，文档很详细，基本满足我现在的需求)</li>
<li><a href="https://www.coder.work/article/41485" rel="nofollow">docker.restart</a>(镜像更新后，不断机更新重启容器)</li>
</ol>
<p>简单列一下我用的比较多的一些docker的指令：</p>
<ul>
<li>docker build</li>
<li>docker run</li>
<li>docker pull</li>
<li>docker push</li>
<li>docker tag</li>
<li>docker create</li>
<li>docker login</li>
<li>docker config</li>
<li>docker restart</li>
</ul>
<p>首先，我是将镜像文件托管到了docker的官方网站，所以login是必须要执行的一个指令。除此之外，每一个镜像文件必须指定两个版本号。<code class="notranslate">docker pull</code>指令默认拉取的是<code class="notranslate">docker-image:latest</code>版本号，同时我必须将这个镜像打上另一个版本号，以防需要镜像回退。</p>
<p>首先，在bin目录下创建一个脚本文件，执行环境指定为node，<code class="notranslate">docker-push.sh</code></p>
<div class="highlight highlight-source-shell"><pre class="notranslate"><span class="pl-c"><span class="pl-c">#!</span>/usr/src/env node</span></pre></div>
<p>所以，在<code class="notranslate">push</code>操作之前必须先只执行一句代码，<code class="notranslate">docker-push.sh</code>:</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">docker build -t <span class="pl-s"><span class="pl-pds">'</span>shadowu/wuh.site:latest<span class="pl-pds">'</span></span> -t <span class="pl-s"><span class="pl-pds">'</span>shadowu/wuh.site:1.6.0<span class="pl-pds">'</span></span></pre></div>
<p>另外一个版本号，来自于<code class="notranslate">package.json</code>文件中的<code class="notranslate">version</code>字段，基于这个文件可以更新一下执行的脚本:</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">version=<span class="pl-s"><span class="pl-pds">$(</span>node -e <span class="pl-s"><span class="pl-pds">"</span>(function () { </span></span>
<span class="pl-s"><span class="pl-s">  const path = require('path')</span></span>
<span class="pl-s"><span class="pl-s">  const pathname = path.resolve(__dirname, './package.json')</span></span>
<span class="pl-s"><span class="pl-s">  console.log(require(pathname).version) </span></span>
<span class="pl-s"><span class="pl-s">})()<span class="pl-pds">"</span></span><span class="pl-pds">)</span></span>

docker build -t <span class="pl-s"><span class="pl-pds">'</span>shadowu/wuh.site:latest<span class="pl-pds">'</span></span> -t <span class="pl-s"><span class="pl-pds">'</span>shadowu/wuh.site:<span class="pl-pds">'</span></span><span class="pl-smi">$version</span></pre></div>
<p>执行环境指定为node了，我才可以用nodejs的模块化拿到<code class="notranslate">package.json</code>文件里面的<code class="notranslate">version</code>，这个是我能想到的最简单的方法。</p>
<p>然后，在脚本指令里面，为这个脚本加上一个命令:</p>
<div class="highlight highlight-source-json"><pre class="notranslate"><span class="pl-ent">"scripts"</span>: {
  <span class="pl-ent">"build:docker"</span>: <span class="pl-s"><span class="pl-pds">"</span>./bin/docker-push.sh<span class="pl-pds">"</span></span>
}</pre></div>
<p>镜像的文件制作``Dockerfile`，可以访问我之前写过的一篇<a href="https://wuh.site/post/2021-03/%E4%BB%8EPM2%E5%88%B0docker%2C%20%E7%A6%BB%E4%B8%8D%E5%BC%80%E7%9A%84Nginx" rel="nofollow">《从PM2到docker, 离不开的Nginx》</a>,不用写太多，简单制作一下，先让项目可以用docker跑起来，其他的再说。</p>
<h3>docker-compose</h3>
<p>在很早之前，docker-compose还是需要单独安装，但在更新了docker的版本后，docker-cli里面集成了docker-compose，相当的方便，我在<a href="https://github.com/stack-wuh/react-router-config#readme">项目的描述文件</a>里面记录了两种启动容器的方式。一种是用docker-compose 启动本地制作镜像，另一种是docker启动线上镜像。</p>
<p>在项目里面我提供了<a href="https://github.com/stack-wuh/react-router-config/blob/v2/docker-compose.yml">docker-compose.yml</a>的执行文件，在不考虑其他优化的前提下，用docker-compose启动镜像是如此的简单。</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">version: <span class="pl-s"><span class="pl-pds">"</span>3<span class="pl-pds">"</span></span>

services: 
  nextjs_app:
    build: ./
    ports: 
      - 3100:3000</pre></div>
<p>必须的配置项就这么简单，但是优化镜像体积的时候，我们还需要用到volume挂载卷，和一个针对<code class="notranslate">node_modules</code>的专用镜像。两个镜像之间的网络桥接用到的<code class="notranslate">network</code>。</p>
<p>目前我遇到的，比较麻烦的处理就是这两个，因为也是头一次做，很多东西都需要慢慢摸索。</p>
<p>在之前版本中，服务器端的执行脚本是这个样子:</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">   frontend:
    build: frontend
    ports:
      - 3100:3000
    stdin_open: <span class="pl-c1">true</span>
    container_name: frontend
    restart: always
    depends_on:
      - backend
    networks:
     - react-express
     - mongo-db</pre></div>
<p>现在不用执行编译过程，简单改一下，直接使用镜像名+版本号就好:</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">   frontend:
    image: shadowu/wuh.site:latest
    ports:
      - 3100:3000
    stdin_open: <span class="pl-c1">true</span>
    container_name: frontend
    restart: always
    depends_on:
      - backend
    networks:
     - react-express
     - mongo-db</pre></div>
<p>你永远可以使用且可以只使用 <strong>docker-compose up -d</strong> 指令，来完成你想完成的任意操作。</p>
<h3>Bash</h3>
<p>首先，介绍一下有关bash相关的教程<a href="https://wangdoc.com/bash/startup.html#%E7%99%BB%E5%BD%95-session" rel="nofollow">《Bash脚本教程》</a>(来自于阮老师的教程，链接地址直接指向了Bash的启动环境篇)。</p>
<p>教程的内容很多，但是我用到的实际上也没几个，目前只会写一点相当基础的语句，但是没关系，可以慢慢来。</p>
<p>现在使用的一个<code class="notranslate">source</code>指令，用来加载同步服务器端的脚本文件。</p>
<div class="highlight highlight-source-shell"><pre class="notranslate">ssh username@ip

$ password</pre></div>
<p>这个是比较常用，使用ssh登录服务器的操作。输入密码是必不可少的一步。只有密码正确了才可以操作服务器，我已经将一些基础的操作全部做成了脚本，现在只差服务器的同步。那么有没有一种方法可以免去输密码的这一步？</p>
<p>我用到的一种就是安全证书。</p>
<p>能力有限，原理我说不清，感兴趣的自己百度。</p>
<h3>执行结果与相关问题</h3>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/291b8a62e5b0c4a3f9ff0b54ff7418ca5a1a9a65cbdd8756ccd73ebd0d19ae37/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3130313734372e706e67"><img src="https://camo.githubusercontent.com/291b8a62e5b0c4a3f9ff0b54ff7418ca5a1a9a65cbdd8756ccd73ebd0d19ae37/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3130313734372e706e67" alt="result" data-canonical-src="https://src.wuh.site/2021-08/2021-08-29-101747.png" style="max-width: 100%;"></a></p>
<p>执行脚本之后，能够正常的走到这一步说明没有遇到问题，而且已经推送成功了。</p>
<p>初次制作的时间还是比较长的达到了43s，别慌，之后的编译过程会快一点。有些问题需要注意下：</p>
<ol>
<li>
<p>看一下执行过程，停留在yarn install 的过程特别耗时，有一次达到了200s+，只好改成淘宝的镜像源了。</p>
</li>
<li>
<p>volume的配置项，不管我放到哪里都感觉没用，也不知道是不是使用的姿势不对。但是我看到了一种方法就是将node_modules单独做成一个镜像，容器之间是可以互相访问，免去每一次制作镜像都需要安装依赖，后面实践一下。</p>
</li>
<li>
<p>启动容器一定要加上端口号，容器默认端口是3000，但是浏览器访问3000端口是没办法正常访问的，这里我测试过，加上一个映射端口就没问题。</p>
</li>
<li>
<p>docker-hub设置为公共源，才可以用到一些三方工具，比如生成一个icon标签放在github上面。</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/078cc01590104fbeb97927f762fb0c22a9c90ac7ca22652466b1e0886cf02844/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3130323933352e706e67"><img src="https://camo.githubusercontent.com/078cc01590104fbeb97927f762fb0c22a9c90ac7ca22652466b1e0886cf02844/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3130323933352e706e67" alt="github-icon-label" data-canonical-src="https://src.wuh.site/2021-08/2021-08-29-102935.png" style="max-width: 100%;"></a></p>
</li>
<li>
<p>注意nodejs的版本，有很多莫名的问题是nodejs的版本带来的问题，linux上可以使用的<code class="notranslate">n</code>这个工具，可以快速的切换nodejs的版本</p>
</li>
<li>
<p><code class="notranslate">WORKDIR</code>是指app在容器里面运行的文件目录，在遇到镜像可以正常制作，但是容器死活起不来的时候，可以减掉一些镜像文件的配置项，进入到容器里面去看一下究竟遇到了什么问题</p>
</li>
</ol>
<h3>docker的常用指令</h3>
<p><code class="notranslate">docker container ls -a</code> <code class="notranslate">docker image ls -a</code> <code class="notranslate">docker stop container_id &amp; rm contaier_id``docker image rm image_id</code></p>
<p>以上是使用的频率最多的一些指令，同时我们还会用到 <code class="notranslate">docker container inspect</code>和<code class="notranslate">docker image inspect</code>，它们是用来查询容器和镜像的基础配置的。</p>
<ul>
<li>必须删除容器才可以删除创建容器的镜像，而再此之前，必须先停掉容器，才可以删除容器。</li>
<li><code class="notranslate">docker run</code>和<code class="notranslate">docker create</code> 在创建容器的时候，其实没啥大区别，需要注意的是，最好加上<code class="notranslate">-it</code>配置，在容器正常启动后会返回容器id</li>
<li>就用<code class="notranslate">docker-compose</code>，有了它一切变得简单起来</li>
</ul>
<h3>工具</h3>
<p><a href="https://www.docker.com/products/docker-desktop" rel="nofollow">Docker Desktop</a></p>
<p>官方提供的一个工具，使用简单。</p>
<ol>
<li>可以相当直观的看到现在使用的机器上面镜像和运行的容器</li>
</ol>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/8b59f5663b6168bee5df9691d90595b30a43c713ab9b744eac235c73cedb8f20/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3130353931302e706e67"><img src="https://camo.githubusercontent.com/8b59f5663b6168bee5df9691d90595b30a43c713ab9b744eac235c73cedb8f20/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3130353931302e706e67" alt="desktop-about" data-canonical-src="https://src.wuh.site/2021-08/2021-08-29-105910.png" style="max-width: 100%;"></a></p>
<ol start="2">
<li>
<p>可以看到目前容器占用的资源</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/7bfd25d3066831bbee64d97216a951833df68902e328e0c0f3509d5b3e03990e/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3131303032302e706e67"><img src="https://camo.githubusercontent.com/7bfd25d3066831bbee64d97216a951833df68902e328e0c0f3509d5b3e03990e/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3131303032302e706e67" alt="desktop-memery" data-canonical-src="https://src.wuh.site/2021-08/2021-08-29-110020.png" style="max-width: 100%;"></a></p>
</li>
<li>
<p>可以看到一些变量属性</p>
</li>
</ol>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/d59890495f42c2ad2a48d9b4307a28536815234de9283b39b5fd6f062be5f1f8/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3131303033352e706e67"><img src="https://camo.githubusercontent.com/d59890495f42c2ad2a48d9b4307a28536815234de9283b39b5fd6f062be5f1f8/68747470733a2f2f7372632e7775682e736974652f323032312d30382f323032312d30382d32392d3131303033352e706e67" alt="desktop-env" data-canonical-src="https://src.wuh.site/2021-08/2021-08-29-110035.png" style="max-width: 100%;"></a></p>
<ol start="4">
<li>可以快速布置k8s任务</li>
</ol>
<p>======= 分割线以下是更新内容 =======</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ javascript中的继承]]></title>
            <link>https://wuh.site/post/31</link>
            <guid>https://wuh.site/post/31</guid>
            <pubDate>Mon, 10 May 2021 11:35:15 GMT</pubDate>
            <description><![CDATA[<img width="969" alt="prototype" src="https://user-images.githubusercontent.com/34117238/117653161-a6805000-b1c6-11eb-946d-fbd351e80665.png"> javascript中的继承 这两天没事，在翻看红宝书的继承篇，重新学习了一下下js中的继承。出于对Es6中clas]]></description>
            <content:encoded><![CDATA[<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/34117238/117653161-a6805000-b1c6-11eb-946d-fbd351e80665.png"><img width="969" alt="prototype" src="https://user-images.githubusercontent.com/34117238/117653161-a6805000-b1c6-11eb-946d-fbd351e80665.png" style="max-width: 100%;"></a></p>
<h2>javascript中的继承</h2>
<p>这两天没事，在翻看红宝书的继承篇，重新学习了一下下js中的继承。出于对Es6中class 类继承的好奇，我在babel里面试了一下，才发现Es6中class类继承，实际是寄生组合式继承，也是一下我要介绍的一种， 如图：</p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/23af0d2190fda3625dd632fa1538205244d9ec3e800434047ac78edbcc80faf2/68747470733a2f2f7372632e7775682e736974652f323032312d30352f323032312d30352d31302d3038313930382e706e67"><img src="https://camo.githubusercontent.com/23af0d2190fda3625dd632fa1538205244d9ec3e800434047ac78edbcc80faf2/68747470733a2f2f7372632e7775682e736974652f323032312d30352f323032312d30352d31302d3038313930382e706e67" alt="" data-canonical-src="https://src.wuh.site/2021-05/2021-05-10-081908.png" style="max-width: 100%;"></a></p>
<p>在红宝书中主要介绍了一下5中实现继承的方案：</p>
<ol>
<li>原型链继承</li>
<li>构造函数继承</li>
<li>组合式继承</li>
<li>原型继承</li>
<li>寄生继承</li>
<li>寄生组合式继承</li>
</ol>
<p>以下，我只记录它们主要的设计代码和基本原理，其实例可以访问我的<a href="https://codesandbox.io/dashboard/all/js%20--%20%E7%BB%A7%E6%89%BF?workspace=ebfb60f6-b82b-4dbb-95e9-2e5ecd4a085d" rel="nofollow">codesandbox</a>，里面有继承篇详细的例子。</p>
<h3>原型链继承</h3>
<p>基本思想就是利用原型，让一个引用类型可以继承使用另一个引用类型的属性和方法。详情查看例子<a href="https://codesandbox.io/s/inherit--prototype-kgvtr" rel="nofollow">原型链继承</a>。</p>
<p>每一个构造函数都有一个原型对象，而原型对象中又有一个指针指向构造函数，实例也有一个指向原型对象的指针。如果让原型对象等于另一个类型的实例，那该原型对象就具有了另一个类型的构造函数。如果另一个类型的原型中又有另一个类型的实例，层层递进就有形成了一种实例与原型的链条，这个就是<strong>原型链</strong>。</p>
<p>对SubType的原型对象改写为SuperType的实例，此时可以将SuperType称之为SubType的超类，那SubType即为子类。</p>
<p>如果继续将SubType作为超类，对SubType2改写原型对象，那SubType2将同时具有SuperType和SubType的属性和方法，尽管SupType2不是SuperType的子类。这个就是继承链最简单的一个实现例子，</p>
<div class="highlight highlight-source-js"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-v">SuperType</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span> <span class="pl-c1">=</span> <span class="pl-s">'super'</span>
<span class="pl-kos">}</span>
<span class="pl-v">SuperType</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">.</span><span class="pl-en">getName</span> <span class="pl-c1">=</span> <span class="pl-k">function</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span> <span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-v">SubType</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span> <span class="pl-c1">=</span> <span class="pl-s">'sub'</span>
<span class="pl-kos">}</span>

<span class="pl-v">Sub</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">SuperType</span><span class="pl-kos">(</span><span class="pl-kos">)</span>
<span class="pl-v">Sub</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">.</span><span class="pl-en">getSubName</span> <span class="pl-c1">=</span> <span class="pl-k">function</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">return</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span> <span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-v">SubType2</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span><span class="pl-kos">}</span>
<span class="pl-v">SubType2</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">SubType</span><span class="pl-kos">(</span><span class="pl-kos">)</span></pre></div>
<p><strong>注意：</strong></p>
<ol>
<li>需要注意的是，重写子类原型的语句必须出现在声明Sub原型方法之前，原因很简单，因为原型链继承是重写子类原型对象。重写语句后自定义子类的属性和方法会被删除。</li>
<li>在使用原型链继承时，如果超类中声明了复杂类型，后代子类会共享复杂类型的值。比如：子类1 更新了一个数组的元素的值，本次更新会反应到子类2。<strong>这个也是原型链继承的一个最大的缺点。</strong></li>
<li>不能使用对象字面量去创建原型方法，因为使用字面量后实质上是重写了原型链，切断与原来的原型链之间的联系。</li>
<li>没有办法在不影响全部实例的前提下，给超类的构造函数传递参数。</li>
</ol>
<h3>借用构造函数</h3>
<p>在子类的构造函数中调用超类的构造函数，即为借用构造函数的基本原理。详情查看例子<a href="https://codesandbox.io/s/inherit--constructor-dhlco" rel="nofollow">借用构造函数</a></p>
<p>借用构造函数方法的出现就是为了解决原型链继承的复杂类型值会被各个实例共享的问题。</p>
<p>在子类的构造函数中调用超类的构造函数，其实质是将超类绑定在this中的属性复制到子类中，只有在调用子类的构造函数时，才执行超类的构造函数，所以现在的参数问题得以解决，各个实例间不会互相影响。</p>
<div class="highlight highlight-source-js"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-v">Super</span> <span class="pl-kos">(</span><span class="pl-s1">name</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
	<span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span> <span class="pl-c1">=</span> <span class="pl-s">'super'</span> <span class="pl-c1">||</span> <span class="pl-s1">name</span>
<span class="pl-kos">}</span>
<span class="pl-k">function</span> <span class="pl-v">Sub</span> <span class="pl-kos">(</span><span class="pl-s1">name</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
	<span class="pl-v">Super</span><span class="pl-kos">.</span><span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">,</span> <span class="pl-s1">name</span><span class="pl-kos">)</span>

	<span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span> <span class="pl-c1">=</span> <span class="pl-s">'sub'</span>	
<span class="pl-kos">}</span></pre></div>
<p><strong>注意：</strong></p>
<ol>
<li>相对于原型链继承，借用构造函数可以单独给实例传递参数了。因为属性是绑定在this上面的，只有在创建实例的时候才赋值，所以实例之间互不影响。</li>
<li>但是，在解决了复杂类型值的问题之后，借用构造函数又有一个新的问题。属性和方法全部绑定在this上面，所以只能在构造函数内部继续声明其他的属性和方法，<strong>扩展性降低。</strong></li>
<li>同时，在超类的原型中定义的属性，对于子类而言不可见。<strong>所以必须在超类的构造函数中声明属性。</strong></li>
</ol>
<h3>组合继承</h3>
<p>利用原型链实现方法的基础和利用构造函数实现属性的继承，就是组合继承的基本原理。结合了原型链和借用构造函数的技术，同时具有了它们的优点。详情查看例子<a href="https://codesandbox.io/s/inherit--combine-scxx5" rel="nofollow">组合继承</a></p>
<p>在重写了子类的原型后，Sup.prototype实质上指向了Super.prototype，在Sup.prototype.constructor指向的是Super，这里需要手动将Sub.prototype.constructor指向Sub。</p>
<div class="highlight highlight-source-js"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-v">Super</span> <span class="pl-kos">(</span><span class="pl-s1">name</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
	<span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span> <span class="pl-c1">=</span> <span class="pl-s">'super'</span> <span class="pl-c1">||</span> <span class="pl-s1">name</span>
<span class="pl-kos">}</span>
<span class="pl-v">Super</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">.</span><span class="pl-en">getName</span> <span class="pl-c1">=</span> <span class="pl-k">function</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
	<span class="pl-k">return</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span>
<span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-v">Sub</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
	<span class="pl-v">Super</span><span class="pl-kos">.</span><span class="pl-en">call</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">)</span>
	
	<span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">age</span> <span class="pl-c1">=</span> <span class="pl-c1">20</span>
<span class="pl-kos">}</span>

<span class="pl-v">Sub</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">Super</span><span class="pl-kos">(</span><span class="pl-kos">)</span>
<span class="pl-v">Sub</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">.</span><span class="pl-c1">constructor</span> <span class="pl-c1">=</span> <span class="pl-v">Sub</span></pre></div>
<p><strong>注意：</strong></p>
<ol>
<li>组合继承唯一一点就是需要两次调用超类的构造函数。</li>
</ol>
<h3>原型式继承</h3>
<p>没有严格意义的构造函数，原型可以基于一个对象生成另外一个新对象，还不必为它声明额外的类型，就是原型式继承的基本原理。详情查看例子<a href="https://codesandbox.io/s/inherit--prototypal-c1o3b" rel="nofollow">原型式继承</a></p>
<p>原型继承的实现特别简单，公式一样的三句代码。object函数实际是创建基于传入的对象o，生成的副本。每一个由object函数生成的副本，其状态都被共享了。</p>
<p>但是对实例进行扩展的方法是不会被共享的。</p>
<div class="highlight highlight-source-js"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-en">object</span> <span class="pl-kos">(</span><span class="pl-s1">o</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">function</span> <span class="pl-v">F</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span><span class="pl-kos">}</span>
  <span class="pl-v">F</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span> <span class="pl-c1">=</span> <span class="pl-s1">o</span>
  <span class="pl-k">return</span> <span class="pl-k">new</span> <span class="pl-v">F</span><span class="pl-kos">(</span><span class="pl-kos">)</span>
<span class="pl-kos">}</span></pre></div>
<p><strong>注意：</strong></p>
<ol>
<li>其复杂类型值的问题，如同原型链继承一样，会被每一个实例共享</li>
<li>在es5中，已经新增Object.create方法，其效果与object方法一致</li>
</ol>
<h3>寄生式继承</h3>
<p>寄生式继承，是与原型式继承密不可分的。其基本思想就是，仅创建一个封装继承过程的函数，在函数内部对新对象进行增强，然后返回增强的新对象。详情查看例子<a href="https://codesandbox.io/s/inherit--parasitic-49rq6" rel="nofollow">寄生式继承</a></p>
<p>就这样在由寄生式继承的函数返回的新对象，就有了源对象origin的属性和增强的属性。</p>
<p>所以，寄生式继承和原型式继承，主要针对的是对象间的继承，而非自定义构造函数。</p>
<div class="highlight highlight-source-js"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-en">inherit</span> <span class="pl-kos">(</span><span class="pl-s1">o</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">var</span> <span class="pl-s1">prototype</span> <span class="pl-c1">=</span> <span class="pl-en">object</span><span class="pl-kos">(</span><span class="pl-s1">o</span><span class="pl-kos">)</span>
  <span class="pl-s1">prototype</span><span class="pl-kos">.</span><span class="pl-en">getName</span> <span class="pl-c1">=</span> <span class="pl-k">function</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-k">return</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span>
  <span class="pl-kos">}</span>
  
  <span class="pl-k">return</span> <span class="pl-s1">prototype</span>
<span class="pl-kos">}</span></pre></div>
<p><strong>注意：</strong></p>
<ol>
<li>其缺点与借用构造函数相似，其封装过程全部在函数内部，没有办法进行扩展。</li>
</ol>
<h3>寄生组合式继承</h3>
<p>寄生组合式继承，指的是通过构造函数来继承属性，通过原型链的混成形式来继承方法。其基本原理就是： 不必为了子类的原型而调用超类的构造函数，我们需要的就是超类原型的一个副本。详情查看例子<a href="https://codesandbox.io/s/inheritance--parasitic-combination-9pytp" rel="nofollow">寄生组合式继承</a></p>
<p>可以使用寄生式继承来继承超类的原型，再将结果指定给子类的原型。</p>
<p>寄生组合继承，可以减去一步在子类外部调用的超类构造函数，避免将一些额外的属性附加到子类的原型链中，而且原型链还能保持不变。</p>
<p>所以，开发人员一致认为寄生组合式继承是引用类型最理想的继承范式。</p>
<div class="highlight highlight-source-js"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-v">Super</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
	<span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span> <span class="pl-c1">=</span> <span class="pl-s">'super'</span>
<span class="pl-kos">}</span>
<span class="pl-v">Super</span><span class="pl-kos">.</span><span class="pl-c1">ptototype</span><span class="pl-kos">.</span><span class="pl-en">getName</span> <span class="pl-c1">=</span> <span class="pl-k">function</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>  <span class="pl-k">return</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">name</span> <span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-v">Sub</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
	<span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">subname</span> <span class="pl-c1">=</span> <span class="pl-s">'sub'</span>
<span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-en">inherit</span> <span class="pl-kos">(</span><span class="pl-s1">subType</span><span class="pl-kos">,</span> <span class="pl-v">SuperType</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
	<span class="pl-k">const</span> <span class="pl-s1">prototype</span> <span class="pl-c1">=</span> <span class="pl-en">object</span><span class="pl-kos">(</span><span class="pl-v">SuperType</span><span class="pl-kos">)</span>
	<span class="pl-s1">prototype</span><span class="pl-kos">.</span><span class="pl-c1">constructor</span> <span class="pl-c1">=</span> <span class="pl-s1">subType</span>
	<span class="pl-s1">subType</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span> <span class="pl-c1">=</span> <span class="pl-s1">prototype</span>
	
	<span class="pl-k">return</span> <span class="pl-s1">prototype</span>
<span class="pl-kos">}</span>

<span class="pl-en">inherit</span><span class="pl-kos">(</span><span class="pl-v">Sub</span><span class="pl-kos">,</span> <span class="pl-v">Super</span><span class="pl-kos">)</span></pre></div>
<p>可以回到顶部，查看babel对class类继承实现的转换，仔细一对比，发现其实没有什么大的不同，从实现思想上来说是一致的。</p>
<h3>Class 与 extends</h3>
<p>ES6终于实现了基于类继承的方案了。可以使用extends关键字实现继承, 但是在子类中必须使用super关键字，因为子类没有自己的this。</p>
<div class="highlight highlight-source-js"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-v">Parent</span> <span class="pl-kos">{</span><span class="pl-kos">}</span>

<span class="pl-k">class</span> <span class="pl-v">Child</span> <span class="pl-k">extends</span> <span class="pl-v">Parent</span> <span class="pl-kos">{</span>
  <span class="pl-en">constructor</span> <span class="pl-kos">(</span><span class="pl-s1">props</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-smi">super</span><span class="pl-kos">(</span><span class="pl-s1">props</span><span class="pl-kos">)</span>
  <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<p>在ES5中，先创建的是子类的this，然后将父类的属性和方法添加到子类的this当中。 在ES6中，先将父类的实例添加到this中，然后用子类的构造函数去修改this。</p>
<p><strong>注意：</strong></p>
<ol>
<li>在大多数浏览器的Es5的实现中，都存在一个<code class="notranslate">__proto__</code>属性，该属性总是指向对应构造函数的prototype属性。Class作为构造函数的语法糖，同时具有<code class="notranslate">__proto__</code>与<code class="notranslate">prototype</code>两条继承链。</li>
<li>子类的<code class="notranslate">__proto__</code>属性，表示构造函数的继承，总是指向父类。 <code class="notranslate">Child.__proto__ === Parent</code></li>
<li>子类prototype中的<code class="notranslate">__proto__</code>，表示方法的继承，总是指向父类的<code class="notranslate">prototype</code>。  <code class="notranslate">Child.prototype.__proto__ === Parent.prototype</code></li>
<li>在类继承中，<code class="notranslate">__proto__</code>属性是层层嵌套的，子类的<code class="notranslate">__proto__</code>属性的<code class="notranslate">__proto__</code>属性指向的就是父类的<code class="notranslate">__proto__</code>。 <code class="notranslate">Child.__proto__.__proto__ === Parent.__proto__</code></li>
</ol>
<h3>总结</h3>
<ol>
<li>在js中主要通过原型链实现继承。原型链的继承就是通过将一个类型的实例赋值给另一个构造函数的原型。这样，子类就可以访问超类中的属性和方法，与基于类继承相似。</li>
<li>原型链继承的问题是所有对象实例共享所有继承的属性和方法，因此不适于单独使用，而解决的方法就是借用构造函数。</li>
<li>借用构造函数，是通过在子类的构造函数中调用超类的构造函数。这样就可以做到，每一个实例有自己的属性和方法，但是也限制了子类的扩展，因为只能在构造函数内部声明其他属性和方法。</li>
<li>使用最多的继承模式是组合继承，集成了原型链继承和借用构造函数优势。通过原型链来扩展方法，通过借用构造函数来实现实例属性的继承，唯一缺点就是必须调用两次超类构造函数，造成效率缺失。</li>
<li>原型式继承，是针对于对象的一种继承模式，如非必要声明构造函数，可以使用该模式，其实质就是对源对象进行浅复制。</li>
<li>寄生式继承, 与原型式继承息息相关。基于源对象生成一个新对象，然后对这个新对象增强后返回。</li>
<li>寄生组合继承，为了解决组合继承会两次调用超类构造函数而造成效率低下的问题，将寄生式继承和组合式继承结合。通过寄生式继承对超类原型对象浅复制，然后对子类原型的构造函数修正，最后改写子类的原型对象。</li>
</ol>
<p><strong>寄生组合继承是基于类型继承最有效的方式。</strong></p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[搬山计划]]></title>
            <link>https://wuh.site/post/30</link>
            <guid>https://wuh.site/post/30</guid>
            <pubDate>Fri, 07 May 2021 13:11:21 GMT</pubDate>
            <description><![CDATA[banshanjihua 001 搬山计划 在过去的一年时间里，自己除了工作就是工作，现在是时候做出一点点改变了，去玩生活去。 每一个人的身上的优缺点都是他的“闪光点“，而这些”闪光点“无时无刻不在反映着他自身。他永远也无法将这些闪光点抹除，不管它是积极的还是消极的。虽然无法消除消极的闪光点，但是可以隐藏，这一个过程给它命名一个名字——"搬山计划"。 整个人体，我把它当做一座山。你的优点和缺点全部]]></description>
            <content:encoded><![CDATA[<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/34117238/117454439-b7d41d00-af78-11eb-8b6f-7e4be67d9aa8.jpeg"><img src="https://user-images.githubusercontent.com/34117238/117454439-b7d41d00-af78-11eb-8b6f-7e4be67d9aa8.jpeg" alt="banshanjihua 001" style="max-width: 100%;"></a></p>
<h2>搬山计划</h2>
<p>在过去的一年时间里，自己除了工作就是工作，现在是时候做出一点点改变了，去玩生活去。</p>
<p>每一个人的身上的优缺点都是他的“闪光点“，而这些”闪光点“无时无刻不在反映着他自身。他永远也无法将这些闪光点抹除，不管它是积极的还是消极的。虽然无法消除消极的闪光点，但是可以隐藏，这一个过程给它命名一个名字——"搬山计划"。</p>
<p>整个人体，我把它当做一座山。你的优点和缺点全部集中在山顶之中，所以它们最显眼。而山脚处，则是普通地不能普通的个人因素。</p>
<blockquote>
<p>现在我加入一个思考，当你接触观察一个新朋友的时候，你首先是关注他的优点，还是缺点？或者说，彻底影响你对他的个人看法是什么因素？</p>
</blockquote>
<p>所以，我说：”自身修炼改造，犹如搬山。你永远也不能彻底改变沉疴旧疾，只能将山顶最引人注目的土往下搬。“</p>
<p>对于我而言，普普通通，平平凡凡，无人关注才是一种最佳的生活状态。</p>
<p>写字，读书，玩一些从未接触过的事物。</p>
<p>每一晚，我给自己安排了200字的控笔。</p>
<p>每一天，我给自己安排了2小时的阅读。</p>
<p>每一周，我给自己安排去逛一逛周边。</p>
<p>每一月，我给自己安排去见一见我在深圳的朋友。</p>
<p>现在，我对无人机很感兴趣，我似乎应该去买一个无人机玩一玩。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[浅尝一下UI设计]]></title>
            <link>https://wuh.site/post/28</link>
            <guid>https://wuh.site/post/28</guid>
            <pubDate>Sun, 18 Apr 2021 10:49:16 GMT</pubDate>
            <description><![CDATA[浅尝一下UI设计 uithumb 博客的前端项目基础功能终于做的差不多了，现在就差设计一下UI了，奈何我不是专业的UI，只能从前端的角度和一些专业的设计文档，去实现一下。 布局基础值 在我们构建网站的布局时，前端会依据设计图，设计网页。在开始工作前，我们会确认一些基础值，比如： 1. 间距相关 2. 色彩相关 3. 动效相关 4. 交互相关 5. 布局相关 我对设计的初级了解主要来自于[Antd ]]></description>
            <content:encoded><![CDATA[<h2>浅尝一下UI设计</h2>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/34117238/115142719-cc0db400-a075-11eb-860b-9e08c8acecda.png"><img src="https://user-images.githubusercontent.com/34117238/115142719-cc0db400-a075-11eb-860b-9e08c8acecda.png" alt="ui_thumb" style="max-width: 100%;"></a></p>
<p>博客的前端项目基础功能终于做的差不多了，现在就差设计一下UI了，奈何我不是专业的UI，只能从前端的角度和一些专业的设计文档，去实现一下。</p>
<h3>布局基础值</h3>
<p>在我们构建网站的布局时，前端会依据设计图，设计网页。在开始工作前，我们会确认一些基础值，比如：</p>
<ol>
<li>间距相关</li>
<li>色彩相关</li>
<li>动效相关</li>
<li>交互相关</li>
<li>布局相关</li>
</ol>
<p>我对设计的初级了解主要来自于<a href="https://ant.design/docs/spec/introduce-cn" rel="nofollow">Antd Design</a>的设计思想，它们的组件设计的真的很棒！现在先用原生css实现一次，以后再考虑改为less。如下:</p>
<div class="highlight highlight-source-css"><pre class="notranslate"><span class="pl-kos">:</span><span class="pl-c1">root</span> {
  <span class="pl-c">/** Base **/</span>
  <span class="pl-s1">--padding-base</span><span class="pl-kos">:</span> <span class="pl-c1">8<span class="pl-smi">px</span></span>;
  <span class="pl-s1">--margin-base</span><span class="pl-kos">:</span> <span class="pl-c1">8<span class="pl-smi">px</span></span>;
  <span class="pl-s1">--font-size-base</span><span class="pl-kos">:</span> <span class="pl-c1">14<span class="pl-smi">px</span></span>;
  <span class="pl-s1">--border-radius-base</span><span class="pl-kos">:</span> <span class="pl-c1">3<span class="pl-smi">px</span></span>;
  <span class="pl-s1">--line-height-base</span><span class="pl-kos">:</span> <span class="pl-c1">22<span class="pl-smi">px</span></span>;
  <span class="pl-s1">--transition-base</span><span class="pl-kos">:</span> all <span class="pl-c1">.5<span class="pl-smi">s</span></span> ease;
}</pre></div>
<p>基础属性可以配置的有很多，我先定义一些比较常规的属性，然后根据这些基础属性，延伸出一些派生属性, 比如:</p>
<div class="highlight highlight-source-css"><pre class="notranslate"><span class="pl-kos">:</span><span class="pl-c1">root</span> {
  <span class="pl-c">/** Base **/</span>
  <span class="pl-s1">--padding-base</span><span class="pl-kos">:</span> <span class="pl-c1">8<span class="pl-smi">px</span></span>;
  <span class="pl-s1">--margin-base</span><span class="pl-kos">:</span> <span class="pl-c1">8<span class="pl-smi">px</span></span>;
  <span class="pl-s1">--font-size-base</span><span class="pl-kos">:</span> <span class="pl-c1">14<span class="pl-smi">px</span></span>;
  <span class="pl-s1">--border-radius-base</span><span class="pl-kos">:</span> <span class="pl-c1">3<span class="pl-smi">px</span></span>;
  <span class="pl-s1">--line-height-base</span><span class="pl-kos">:</span> <span class="pl-c1">22<span class="pl-smi">px</span></span>
  <span class="pl-s1">--transition-base:</span> all <span class="pl-c1">.5<span class="pl-smi">s</span></span> ease;
  
  <span class="pl-c">/** Margin **/</span>
  <span class="pl-s1">--margin-base-2</span><span class="pl-kos">:</span> <span class="pl-en">calc</span>(<span class="pl-c1">2</span> <span class="pl-c1">*</span> <span class="pl-en">var</span>(<span class="pl-s1">--margin-base</span>));
  <span class="pl-s1">--margin-base-3</span><span class="pl-kos">:</span> <span class="pl-en">calc</span>(<span class="pl-c1">3</span> <span class="pl-c1">*</span> <span class="pl-en">var</span>(<span class="pl-s1">--margin-base</span>));
  
  <span class="pl-c">/** Border **/</span>
  <span class="pl-s1">--border-radius-2</span><span class="pl-kos">:</span> <span class="pl-en">calc</span>(<span class="pl-c1">2</span> <span class="pl-c1">*</span> <span class="pl-en">var</span>(<span class="pl-s1">--border-radius-base</span>));
  
  <span class="pl-c">/** Line Height **/</span>
  <span class="pl-s1">--line-height-default</span><span class="pl-kos">:</span> <span class="pl-en">calc</span>(<span class="pl-en">var</span>(<span class="pl-s1">--font-size-base</span>) <span class="pl-c1">+</span> <span class="pl-c1">10</span> <span class="pl-c1">-</span> <span class="pl-c1">2</span>);
}</pre></div>
<p>将这些派生属性的配置全部集中到一个样式文件当中，不要在组件中再次定义样式，以保证全局样式的整体一致性。</p>
<p>当然，派生属性的倍数设置，并不是简简单单的乘于整数倍这么简单，有一些相关的公式或者是规范，详情可以参考<a href="https://ant.design/docs/spec/introduce-cn" rel="nofollow">antd的设计文档</a>。例如:</p>
<ol>
<li>
<p><strong>字号</strong>: 对于字号与行高的定义就是：lineHeight = fontSize+10 - 2。在Antd中基础字号为14px，相对应的行高为 22px，字阶的选择在3-5中之间，保持克制原则。</p>
</li>
<li>
<p><strong>字重</strong>: 对于字重只建议使用3种，400、500和600。具体效果可以研究Antd的<strong>Typograph</strong>组件，赶紧按照标准实现一下自己的个人网站试一试。</p>
</li>
<li>
<p><strong>字体颜色</strong>：个人认为，色彩还是很考验一个设计师的，因为需要搭配，整体搭配的色彩需要<strong>和谐</strong>，所以对于色彩的把握才是整个体系的重中之重。对于不同的应用场景有不一样的实现，需要提前定义好网站的主，次，辅助和标题的色彩。<br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/34117238/115142764-1a22b780-a076-11eb-9a65-a7a7cbf2b370.png"><img src="https://user-images.githubusercontent.com/34117238/115142764-1a22b780-a076-11eb-9a65-a7a7cbf2b370.png" alt="system-color" style="max-width: 100%;"></a></p>
</li>
<li>
<p><strong>暗黑模式</strong>: 给自己的网站准备一套暗黑模式是一个不错的功能，现在我们可以直接使用Antd的工具生成，站在巨人的肩膀上扩展，让我们的自定义开发快乐起来。</p>
</li>
</ol>
<p>现在先试着将我们的基础属性定义出来。就用Antd官网的工具生成一系列的色彩值，在此之前我们需要定义一个主题色彩。除此之外，需要定义字体色彩、border色彩和shadow阴影。</p>
<p>最为关键的是，我们需要设计一下页面的布局结构。从我个人的项目来分，可以分为：</p>
<ol>
<li>顶级标题</li>
<li>二级标题</li>
<li>三级标题</li>
<li>一级菜单</li>
<li>二级菜单</li>
<li>基础描述文案 -- Description</li>
<li>基本文案 -- Pages</li>
<li>背景色对比</li>
<li>Border外边框</li>
<li>Strong 增强类型文案</li>
<li>Small 渐弱类型文案</li>
<li>Shadow阴影</li>
<li>Hover类型色彩</li>
</ol>
<p>从「克制」方向来看，我做出了调整:</p>
<ol>
<li>页面上不需要太多的Heading标签，最多三级H1,H3,H6；</li>
<li>准备左侧菜单入口或者是顶部菜单入口，不需要太多的菜单，最多两级；</li>
<li>针对文章详情样式的调整，不用大的样式调整，需要多加一些细节调整；</li>
<li>页面内只准备三级字重，Strong等于600、Normal等于500以及Small等于400；</li>
<li>手势的交互增强，对比色彩、BorderRadius...</li>
<li>渐进动画，全部使用transition增强页面交互</li>
</ol>
<h3>确认基础设置值</h3>
<ol>
<li>Padding 和 Margin的基数定为 8px, 整数倍增加，为8的倍数；</li>
<li>BorderRadius的基数为 3px，增加不限制；</li>
<li>FontSize的基数为14px，增加不限制；</li>
<li>LineHeight的基数为22px，与FontSize成线性关系，LineHeight = Fontsize + 10 - 2；</li>
<li>定义主题色，中性色和主题色的反色；</li>
<li>定义背景色，可能需要好几组对应的背景色，以对应不同的使用场景；</li>
<li>定义Border边框色彩，不需要太多，可以直接使用主题色或者是中性色；</li>
<li>定义动画函数，常见的函数是ease，linear，可以自己定义贝塞尔曲线；</li>
<li>定义辅助色，辅助色彩丰富一点点</li>
</ol>
<h3>定义Heading类型主题</h3>
<p>首先给文案类配置主题，达到一种视觉上的层次感。可以通过主题色系的不同的色彩，然后在页面上使用。</p>
<p>在上图中，定义了8种类型，可以按照上面的顺序，将生成的色彩，一一对应。但是可能也会有特殊的位置需要额外定义。比如我们现在需要生成两套文字色，以对应light和dark模式下的展示。</p>
<div class="highlight highlight-source-css"><pre class="notranslate"><span class="pl-c">/** Light **/</span>
  <span class="pl-s1">--color-base</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>ebecec</span>;
  <span class="pl-s1">--color-base-10</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>0d0d0d</span>;
  <span class="pl-s1">--color-base-9</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>333333</span>;
  <span class="pl-s1">--color-base-8</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>595959</span>;
  <span class="pl-s1">--color-base-7</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>808080</span>;
  <span class="pl-s1">--color-base-6</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>a6a6a6</span>;
  <span class="pl-s1">--color-base-5</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>b3b3b3</span>;
  <span class="pl-s1">--color-base-4</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>bfbfbf</span>;
  <span class="pl-s1">--color-base-3</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>cccccc</span>;
  <span class="pl-s1">--color-base-2</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>d9d9d9</span>;
  <span class="pl-s1">--color-base-1</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>e6e6e6</span>;

<span class="pl-c">/** Dark **/</span>
  <span class="pl-s1">--color-base</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>141313</span>;
  <span class="pl-s1">--color-base-10</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>fafafa</span>;
  <span class="pl-s1">--color-base-9</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>f8f8f8</span>;
  <span class="pl-s1">--color-base-8</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>f3f3f3</span>;
  <span class="pl-s1">--color-base-7</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>e8e7e7</span>;
  <span class="pl-s1">--color-base-6</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>dcdcdc</span>;
  <span class="pl-s1">--color-base-5</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>adacac</span>;
  <span class="pl-s1">--color-base-4</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>7e7d7d</span>;
  <span class="pl-s1">--color-base-3</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>5b5a5a</span>;
  <span class="pl-s1">--color-base-2</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>454545</span>;
  <span class="pl-s1">--color-base-1</span><span class="pl-kos">:</span> <span class="pl-pds"><span class="pl-kos">#</span>2c2b2b</span>;</pre></div>
<p>一共生成10种色彩，用于对应上图的8种类型，可用的色彩还是比较多的，先定义一套最普通的样式出来。</p>
<div class="highlight highlight-source-css"><pre class="notranslate"><span class="pl-s1">--title-normal-color</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--color-base-10</span>);
<span class="pl-s1">--title-less-color</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--color-base-9</span>);

<span class="pl-s1">--text-primary-color</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--color-base-8</span>);
<span class="pl-s1">--text-less-color</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--color-base-7</span>);

<span class="pl-s1">--text-second-color</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--color-base-6</span>);

<span class="pl-s1">--disabled-color</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--color-base-5</span>);

<span class="pl-s1">--border-color</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--color-base-4</span>);

<span class="pl-s1">--divider-color</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--color-base-3</span>);

<span class="pl-s1">--background-color</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--color-base-2</span>);

<span class="pl-s1">--table-head-color</span><span class="pl-kos">:</span> <span class="pl-en">var</span>(<span class="pl-s1">--color-base-1</span>);</pre></div>
<h3>中性色搭配</h3>
<p>偷个懒，直接用Antd的中性色，以color-7为基准，大于7的为Dark模式下的色彩，小于7的为Light模式下的色彩。</p>
<p>生成这些中性色有什么用呢？</p>
<p>我把它搭配使用在Card组件当中，中性色是以Gray色彩为基准生成的，灰色是百搭色彩，没有什么色彩能够比中性色更适合做背景色彩了。除非你需要自定义一些Alert或者的Message类型的样式，才会去给这些组件定义一些不同状态下的背景色彩。</p>
<h3>辅助色搭配</h3>
<p>在选择辅助色时，需要考虑到的是辅助色和背景色以及字色的搭配，需要好好准备一下色彩，最好是从选择的主题色出发，去选择相近的色系。</p>
<h3>Transition 与 Animate交互</h3>
<p>我们最少需要准备两套动画，即入场动画和出场动画。举一个例子，我们给一个标签做hover特效时，定义在hover类中的就是入场动画，比如使用ease-in，那么定义在当前标签类中的即为出场动画，比如使用ease-out，这个样子给用户的体验是不是有了一点点趣味呢。</p>
<p>如果需要将我们的页面细节做起来，我们可以给每一个属性都指定一个特有的出场和入场动画，那看起来就更美妙了，但是我觉得用两个动画就可以了。</p>
<h3>间距设置</h3>
<p>现在我们的基础都是基于8px，几何倍数的增加或者是减少，这样看起来的页面的间距值就全部统一起来啦，从结构上确实好看，具体去看Antd的组件。我是觉得好看的~</p>
<p>除了8n这一种写法，还可以使用em单位和rem单位，em相对于父级标签的字号，rem相对于根标签的字号，也是一种结构性的写法。但是使用场景上有差异，用em单位我们通过Fontsize属性轻松实现自适应的组件，比如在项目中的AudioContronl组件和Button组件组，就是通过Fontsize来控制Audio按钮组的样式。</p>
<h3>富结构化缺省页</h3>
<p>查询结果结构化是我在做google查询优化时接触到概念，我认为这个概念也可以用到我们的构建个人网站的查询结果中。</p>
<p>举个例子<strong>404页面</strong>，我个人认为：在类博客项目这种内容型项目中，我并不是想看一个枯燥的404提示，我更想看的是我查询的关键字在没有反馈的内容时，开发者给用户的交互是一种什么样的实现，才能吸引用户停留在我的网站中。</p>
<p>比如，我查询一个<strong>色彩</strong>关键字，我的网站上没有相关的博客内容，但是我不能给你一个简简单单的404，而是给用户一些推荐内容的快捷入口，引导用户点击进入博客内容页面，留住流量。</p>
<p>而资源类的404，可以给一些动画，或者是引入腾讯404服务的交互，让服务器端的错误或者是资源缺失，给用户一种丰富、有趣的体验。</p>
<p>在最近有一个主题卖疯了，就是华为商城的宇航人主题，它不就是靠着有趣才吸引了消费者吗，在短视频中火了后，不知道多少人为了一个主题去买了一块手表。可见，现在C端产品考虑<strong>趣味性</strong>的比重应该是比较大的。</p>
<h3>Empty 空状态</h3>
<p>为了弥补我们网站内容的缺失和资源缺失，给用户带来的不良体验，我们需要优化的点就是做一个有意思的Empty 空状态展示页。</p>
<p>在Empty中，我设计的结构是，一个简单的动画吸引用户视线，一组类bilibili中的火星文，一组引流快捷入口，如下图：<br>
<a target="_blank" rel="noopener noreferrer nofollow" href="https://user-images.githubusercontent.com/34117238/115142870-96b59600-a076-11eb-8a89-4213573efe69.png"><img src="https://user-images.githubusercontent.com/34117238/115142870-96b59600-a076-11eb-8a89-4213573efe69.png" alt="2021-04-18-092633" style="max-width: 100%;"></a></p>
<h3>定制型设计</h3>
<p>我们在写作时，工具会提示让我们选择是否启用<strong>专注模式</strong>，专注模式就是一种定制型设计。再类比一下，我们使用网易云App时有精简模式和普通模式，精简模式下，只有控制按钮组其他的什么都没有。</p>
<p>所以，一个网站能不能够吸引用户长时间停留，最为关键的点大概就是有意义的丰富内容和有意思的交互。让用户觉得好玩，让他自己去浏览、去玩。这样的网站才能够保证<strong>回访率</strong>。</p>
<h3>清新的结构和出彩的细节</h3>
<p>单从设计结构来看，开发者可以给用户提供两套布局模式，比如：菜单固定在左的模型和菜单固定在顶部的模型。给用户提供两套视觉模型，Light型和Dark型，然后我们可以根据系统时间，默认给用户切换主题色。</p>
<p>或者，定义精简模式，在阅读模式下，减少系统相关的交互和按钮入口，新增一些阅读类型的按钮，比如：自定义行高、字号，自定义阅读模式下网站字色和背景色，有耐心的话，还可以给出一些国际化方案，例如，中英文切换或者是简繁体切换。</p>
<p>总之，需要我们动用一些时间和心血去做一些不一样的细节出来，把网站打磨的漂漂亮亮的，就可以比较大方的拿出去拉外快项目啦。</p>
<p>一个开发者的个人网站大概就是他的一个比较有特色的个人名片啦。我们前端开发者还是占据了大量的优势的，我们可以自己做一些自己想要的效果出来，我们自己就是自己项目的产品经理。</p>
<h3>简洁而不简单，清新才是关键</h3>
<p>在Antd的设计文档中，始终在着重描述三个设计思想：自然、高效和克制。始终在表达<strong>一种简简单单才是美</strong>。</p>
<p>在理想状态下，我们不太需要关注更多的细节，比如那个经典的撕逼话题：”你这个1像素能不能细一点点， 我看着就不像1像素“。相反我们需要去做减法，但是此减法非彼减法。视觉上的细节少了但是给开发的工作却多了。单是色彩就至少准备40多个，其看不见的工作就给了视觉上的简单赋予了另外的一种不简单。</p>
<p>做好一份设计并不是我以上描述的这么简单，有很多内容我们都不知道，毕竟不是专业的设计师，我了解的相关知识全部来自于书本。但是我们现在是从前端角度去分析一下下怎么去做设计，让我们的项目做得更好一点，更快一点，更<strong>简单</strong>一点。</p>
<p>简洁而不简单，把背后的工作留给自己慢慢品，把清新留给用户。</p>]]></content:encoded>
        </item>
    </channel>
</rss>