资讯动态

Git 主机当静态站点生成器:一次推演与动手实验

发布时间:2026/9/16 23:51:45 来源:尧图企业网站定制
What if my Git host were a static site generator?——这句话是我在部署博客 CI 又失败了一次之后随手写进备忘录里的。当时只是觉得别扭我改了两行 Markdown推送一个 commit剩下的时间全都耗在等待构建环境、解析依赖、拷贝产物这些和内容毫无关系的事情上。仓库里明明已经有全部的材料为什么发布一个静态站点还需要专门搭一条流水线这个念头后来缠了我挺久。我越来越觉得Git 和静态站点生成器之间隔着的那层 CI/CD可能只是历史惯性而不是技术必然。Git 主机存储内容、管理版本、处理协作静态站点生成器读取内容、套模板、产出 HTML。二者每天都在跟同一批文件打交道却非要通过第三方调度才能接上头。那如果 Git 主机自己就懂渲染呢这篇文章就是我围绕这个问题做的一次完整推演和动手实验。我会拆解 Git 主机和静态站点生成器的职责边界推演二者融合后工作流会变成什么样再用裸仓库 hook、GitHub Actions、Pages 服务三套方案把如果变成可以。整个过程适合正在用 GitHub Pages、GitLab Pages 或其他 Git 托管平台发网站的开发者也适合那些对 Git 底层机制感兴趣、想换个角度理解版本管理的人。先说结论这个想法不仅能落地而且它真正改变的东西可能跟你想的不太一样。1. 这个脑洞问题的起点我为什么开始琢磨Git 主机渲染这件事1.1 那次让我哭笑不得的构建失败一切始于一次非常普通的发布。我的博客用的是 Eleventy 加一个很简单的主题内容全是 Markdown 文件。那天我只是修正了某篇文章里的一个英文逗号顺手在结尾加了一句注释然后执行了那句已经刻进肌肉记忆的命令git push origin main。接下来是漫长的等待。CI 平台先要拉取一个干净的 Ubuntu 镜像装 Node 环境跑npm ci再执行构建脚本最后把生成的_site目录同步到服务器。整个过程快的时候三分半慢的时候能磨到十几分钟。那天恰好赶上平台排队我盯着转圈的构建状态图标突然意识到一个荒诞的事实我改动的内容大约 20 个字节而为了发布这 20 个字节系统重新创建了一个操作系统级的隔离环境下载了几百 MB 的依赖跑了一遍完整构建。如果这个站点是动态的比如后端有数据库、有接口服务那这么重的流程是合理的。但一个静态站点产出就是一堆 HTML、CSS、JS 文件输入就是仓库里的内容为什么要绕这么大一圈1.2 一个看起来外行、其实很本质的直觉我当时的第一个反应是把仓库克隆到本地执行一条npx eleventy把输出目录扔给 Nginx站点就起来了。整个渲染动作只需要三步读取、转换、写入。这三步完全可以在任何一台装了 Node 的机器上完成也就是说Git 主机本身完全有能力做这件事。顺着这个思路往下想更重要的观察是Git 仓库里存的不是代码而是一棵随时可以被还原的文件树。这篇博客的 Markdown 源文件、主题模板、站点配置、图片资源全都在仓库里。静态站点生成器做的全部事情就是把这棵文件树按规则映射成另一棵文件树。而 Git 恰恰是管理文件树版本的最成熟的系统。二者处理的几乎是同一批文件只是工作在不同抽象层面。换句话说我的网站在某种意义上就是我的仓库在经过某个确定性变换之后的样子。那Git 主机如果内置渲染这个假设本质上是在问为什么要把变换这个步骤外包给一个独立的 CI 系统1.3 从能不能到为什么现在不能当然稍微动脑子想一想就知道现在主流平台没这么做是有原因的。最大的障碍是安全Git 主机如果直接执行仓库里的构建脚本等于允许任意推送者在服务器上运行任意代码。这要求平台具备极强的沙箱隔离能力而这是云平台级别的安全工程不是个人项目能轻易承担的。第二个原因是生态多样性。不同项目的依赖语言不同Node 的构建方式和 Python、Rust、Go 完全不同。静态站点生成器只是构建生态中的一小块要让 Git 主机原生支持所有构建需求等于重复造一个 CI 的轮子。但这两个原因都解释不了我心底的疑问安全可以用沙箱解决生态可以用插件机制解决这些是工程问题不是原理问题。真正需要回答的是——如果 Git 主机真的理解渲染这个概念我们的日常发布体验会发生什么改变这才是这个脑洞问题最有趣的部分。2. 拆开看Git 主机与静态站点生成器到底在各自做什么2.1 Git 主机的本职不是网页是存储与协作抛开 GitHub、GitLab 这些具体产品不谈一个Git 主机的核心职责其实分成三层。最底层是对象存储和引用管理维护 commit、tree、blob 三者的关系维护分支和标签这些 refs让任意时刻的仓库状态都能被精确还原。第二层是协作层Push/Pull 的权限控制、分支保护规则、Pull Request 或者 Merge Request 的评审流程、Issue 和讨论区。第三层是扩展层Webhook 通知、CI/CD 流水线、Pages 静态托管、包管理 Registry 等等。注意这三层里没有任何一层包含理解你的文件内容。Git 主机对仓库里的 Markdown 和二进制图片一视同仁它只关心这些文件的哈希值不关心文件内部的语法。这种不关心内容的设计是 Git 成功的原因之一但也正是它和静态站点生成器之间那道天然鸿沟的来源。2.2 静态站点生成器从内容树到发布物的转换器静态站点生成器的职责则完全在另一个维度。它做的事可以概括为读取源文件、应用模板、生成输出。以 Hugo 为例它会扫描content/目录下的 Markdown 文件解析 front matter 里的标题、日期、标签读取layouts/下的模板再把二者合并成 HTML 页面。Eleventy、Astro、Zola 也是类似的逻辑只是语法和生态不同。它们都遵循一个共同的哲学内容是数据模板是视图构建过程是纯函数。这个纯函数的属性特别关键。只要输入的内容树不变输出就不会变。这也意味着一次构建的产物和另一次构建的产物之间没有隐藏状态一切都可以重复复现。而这恰恰是 Git 最擅长管理的对象可复现、可追溯、可回到任意历史状态。2.3 两者交汇点同一棵文件树把两边的职责放在一起看就会发现一个有趣的重叠。Git 的操作单位是文件树静态站点生成器的处理单位也是文件树。区别只在于Git 存储的是所有历史版本的文件树静态站点生成器消费的是某个特定版本的文件树通常就是工作区当前检出的那棵树。我画过一张简单的对照表把两边的能力按维度列出来越看越觉得像是同一个系统的两部分维度Git 主机静态站点生成器对象单元commit/tree/blob源文件/模板/输出文件核心操作版本记录、分支合并、差异比较内容读取、模板渲染、静态输出时间维度保留全部历史只关心当前状态协作能力多人并行、评审流程无内置协作输出结果可还原的文件树可直接发布的文件树决定性哈希保证一致性无副作用保证一致性把这张表读三遍你会发现一个惊人的事实Git 主机其实什么都不缺缺的只是一个在git push之后自动检出一棵树、运行一次渲染、把结果发布出去的动作。而这个动作理论上可以被理解为主机内置能力的一部分而不是需要 CI 系统介入的外部任务。2.4 中间为什么隔着一层 CI既然两边这么契合为什么现实里非要插一个 CI除了前面说的安全问题还有一个被很多人忽略的因素构建环境不是免费的。静态站点生成器虽然只是读取加变换但它运行在 Node 或者 Go 或者 Python 的运行时之上这些运行时意味着依赖安装、版本管理、缓存机制。平台如果要在每次推送后自动运行任意项目里的构建命令就必须为任意语言准备可复用的运行时池。GitHub Actions 给出的方案是把运行时完全交给用户定义的容器或者虚拟机平台只负责调度。这当然灵活但同时也复杂。复杂到你改一个标点符号也要经历一次装系统、装依赖、跑构建的完整旅程。所以问题的本质不是Git 主机做不到而是通用化地做到成本太高。如果我们把范围缩小到静态站点这个子集——不涉及数据库、不涉及服务端逻辑、构建方式相对统一——那这个成本就降到了可接受的范围。这也是 GitHub Pages、GitLab Pages 能存在的底层逻辑它们都是缩小了范围的假设。3. 顺着这个思路推演假如 Git 主机内置渲染日常开发会怎么变3.1 push 即发布中间不再有等待构建的煎熬先想最直观的变化。假如我的 Git 主机内置了静态渲染能力那么git push origin main这个动作的含义会从请求 CI 去发布变成提交一个待渲染的版本。主机在收到推送到 main 分支的请求之后会校验仓库配置里声明的渲染方式比如用 Node 18 Eleventy入口是 package.json 的 build 脚本把刚刚推送的这个 commit 的内容检出一个临时工作区执行构建把产物原子地替换到站点所在的发布目录。整个过程可能只需要几秒因为连依赖都可以在主机侧做缓存。这带来的变化是巨大的发布不再是一个需要中途打开浏览器查看构建日志的事件而是git push这个动作的自然延伸。你提交内容主机确认收到顺手就把它变成了线上网站。发布动作的失败率也会大幅下降因为渲染行为从每次重新安排一个隔离环境变成了主机内部一个高度优化的标准流程。3.2 分支预览每一次 push 都是一次环境部署更有意思的变化发生在分支上。现在的常规流程里如果你想预览一个未合并的功能分支渲染出来的效果你得手动配置独立的预览环境或者依赖 Vercel、Netlify 这种平台的 Deploy Preview 功能。但在Git 主机即 SSG的设想下分支预览可以成为默认行为。主机在收到任何非 main 分支的推送时都可以自动为这个分支分配一个预览地址比如preview-分支名.example.com。渲染方式完全一致只是发布目标不同。Pull Request 的评审者打开的不是代码 diff 页面而是一个可以直接点击浏览的真实渲染版本。文案改了没改对、布局有没有被破坏、图片路径有没有写错看一眼就知道。这意味着 Web 开发的评审环节会从阅读抽象代码变成体验具体成果。对内容创作者来说尤其友好——他们不需要懂 Markdown 语法差异不需要知道模板变量和短代码的区别只需要在一个真实的页面上指出这行字的位置不对。3.3 回滚变成git revert而不是重新部署传统部署里最让人紧张的一个操作是回滚。你要找到上一个正常版本的构建产物确认它的保留位置然后执行切换。在 Git 原生渲染的设想下回滚的语义可以变得无比干净出问题的是某个 commit那就git revert或者重新推送一个修正 commit主机自动处理剩下的事情。这本质上是因为线上站点状态和仓库历史状态完全一一对应。线上是什么样子取决于当前 main 分支的 HEAD 是什么HEAD 回退线上就回退。静态站点的状态不再是一个需要单独保存的部署产物而是随时可以从 Git 历史里重建的对象。我实际体验过一次这种回滚之后最大的感受是心理负担消失了。以前我改博客布局总是小心翼翼怕改坏了还要走一遍漫长的构建部署流程去恢复。现在我的心态是改坏了就回滚成本跟撤销一个 commit 差不多。这种心态上的转变其实比工具效率的提升更重要。3.4 协作模型的重心会从代码评审滑向内容评审还有一个容易被忽略的变化当 Git 主机内置渲染之后版本控制的每个概念都会找到新的对应关系。分支成了内容版本合并请求成了发布审批单提交历史成了站点变更日志。想象一个团队维护产品文档的场景。编辑者只需要在一个友好的内容编辑界面里修改 Markdown保存后系统自动创建一个 commit 和一个新的分支提出合并请求。技术负责人不需要逐行读 diff只需要打开预览链接看渲染结果点击批准合并。合并动作完成后文档站点的更新自动完成而且每一步操作在 Git 历史里都留有完整痕迹。这个模型下Git 不再是开发者的专利而是整个内容团队共享的工作底稿。它甚至可能催生更抽象的协作方式审校人员对特定内容进行批注批注对应到具体的 commit历史版本可以随时对比看看这一版比上一版改了什么而不需要第三方内容管理系统的额外支撑。3.5 什么不该变别把 Git 的底层逻辑改掉推演到这里我反而要给这个脑洞踩一脚刹车。Git 主机内置渲染想改变的应该是发布流程和体验不应该改变 Git 本身的版本模型。分支、提交、合并、冲突解决这些底层操作依然照旧因为正是它们提供了内容版本可追溯的核心价值。假设里有危险倾向的是为了让主机原生渲染而给 Git 协议增加新的传输层语义或者把渲染结果塞进 Git 对象库里。这些是本末倒置的做法。Git 的优势就在于它的简单和统一渲染应当是检出一棵树之后发生的事情而不是 Git 存储格式的一部分。想明白这一点后面的设计才不会走偏。4. 不等假设用现有工具链把这个如果跑起来4.1 方案一裸仓库 post-receive hook最接近真相的模拟推演归推演我更想亲手验证push 即发布到底是什么感觉。于是我在自己的服务器上搭了一套最接近Git 主机内置渲染的实验环境一个裸仓库加一条 post-receive hook。这套方案不需要第三方 CIpush 完成、渲染完成、发布完成全部在 Git 事件触发的原生流程里完成。服务器端需要的东西很朴素一个空目录作为发布目录一个裸仓库一个工作区目录还有 Git 自带的 hook 机制。我在裸仓库的hooks/目录下创建了post-receive文件内容大致是这样#!/bin/bash # post-receive hook: 模拟Git 主机内置渲染 TARGET/var/www/mysite WORK_TREE/srv/git-render-work/mysite REPO_DIR/srv/git/repos/mysite.git while read oldrev newrev ref do if [[ $ref ~ refs/heads/main$ ]]; then echo 检测到 main 分支更新开始渲染站点 # 第 1 步把最新提交检出到工作区 git --work-tree$WORK_TREE --git-dir$REPO_DIR checkout -f main # 第 2 步进入工作区按项目声明的方式构建 cd $WORK_TREE if [ -f build.sh ]; then bash build.sh fi # 第 3 步把构建产物同步到发布目录 rsync -a --delete $WORK_TREE/_site/ $TARGET/ echo 渲染完成站点已发布到 $TARGET else echo 非 main 分支更新跳过发布 fi done这套脚本的关键点有三个。第一git --work-tree... --git-dir... checkout -f main这个命令是整条链路的核心它让裸仓库在不改变自身对象库结构的前提下把指定分支的文件树还原到外部的工作区。这是 Git 原生支持的操作不需要额外插件。第二构建动作没有写死。我留了个build.sh的开关如果项目里有自定义构建脚本就优先执行它没有的话就用工作区里项目配置的默认命令。这模拟了内置渲染的灵活性——主机不知道你的具体站点生成器是什么但它尊重项目自己的声明。第三rsync -a --delete负责把构建产物同步到 Nginx 或者 Caddy 托管的目录。--delete参数保证旧文件被清理发布目录始终是当次构建的完整镜像。这一步模拟了原子替换的效果。设置完成后读者可以自己在本地把仓库 clone 到任意目录写一篇文章执行git add content/new-post.md git commit -m 发布新文章 git push origin main然后在服务器上观察 hook 的输出。当终端显示渲染完成站点已发布的时候打开浏览器访问服务器上的域名新文章已经在线了。整个过程中没有任何第三方 CI 参与从 push 到线上可见正常的构建时间之内就完成了。4.2 方案二GitHub Pages 和 GitLab Pages平台的现成答案如果说第一条路是自己动手还原理想那 GitHub Pages 和 GitLab Pages 就是平台方已经给出的简化版答案。GitHub Pages 的逻辑非常简单你把仓库推到远端在仓库设置里开启 Pages选择分支和目录平台自动完成 Jekyll 渲染或者按你的.github/workflows配置执行自定义构建。GitLab Pages 类似通过.gitlab-ci.yml里定义的pagesjob 把public/目录发布出去。这两个服务的本质就是Git 主机 有限范围内的内置渲染。它们早就证明了[设定一个窄一点的范围Git 主机真的可以直接发布站点这件事是成立的。GitHub Pages 对 Jekyll 的原生支持几乎就是我设想的内置 SSG的雏形——只是它把渲染引擎限定为 Jekyll目录结构也做了强约定。缺点是显而易见的自由度过低。如果你想用 Hugo、Eleventy、Astro 或者任意一门新语言写的生成器就必须滑落到自定义 CI 的轨道上去那又回到了本文开头那个漫长的构建流程。所以这类服务证明了方向正确但没有证明适用范围足够广。4.3 方案三用 CI伪装内嵌渲染重点在于打磨体感现实中多数人不可能为了一个博客去维护一台自己的服务器所以第三条路是在现有 CI 平台里伪装出内嵌渲染的体验。思路很简单让构建配置尽可能透明把整个发布流程压缩到用户在本地只感知到git push这一件事。以 GitHub Actions 为例一个比较顺滑的配置长这样name: deploy on: push: branches: [main] jobs: build: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkoutv4 - uses: actions/setup-nodev4 with: node-version: 20 cache: npm - run: npm ci - run: npm run build - uses: peaceiris/actions-gh-pagesv4 with: publish_dir: ./_site publish_branch: gh-pages这套配置的实际效果是用户只要推送main分支几分钟后站点就自动更新。虽然底层的构建、部署依然依赖外部平台但从用户的感知角度来说,push 即发布的体验已经成立。我把这条方案称为伪装因为它的架构和三段式 CI 没有任何区别但它指向一个明确的目标所有复杂性都应该被压缩到配置里用户不需要关心也最好永远不用打开 CI 页面。4.4 三种方案怎么选三个方案做下来我的选型建议其实很明确方案贴近理想的程度维护成本自由度适合场景裸仓库 hook最高中高很高有自己的服务器想完全掌控流程Pages 服务中最低低个人博客、项目文档接受平台约定CI 伪装内嵌中高低高依赖 GitHub/GitLab但想保留完整自由度如果你有云服务器并且不介意折腾我强烈建议试一次方案一。它带给你的不只是一个发布通道而是一种重新理解 Git 和网站之间关系的视角。如果你不想管服务器方案三是性价比最高的解法本质上是把内置渲染的外部依赖封装到了配置文件里。5. 真实世界的类似物哪些项目已经在践行这个想法5.1 GitHub Pages 与 GitLab Pages最诚实的先行者GitHub Pages 从 2008 年就上线了它最初就是把你的仓库变成网站的朴素实现。用户创建一个名为username.github.io的仓库往里推 HTML 文件平台直接托管。后来加了 Jekyll 支持让用户可以只维护 Markdown 和模板推送后自动渲染。GitLab Pages 则走得更贴近流水线化它把所有构建逻辑都显式地放进.gitlab-ci.ymlPages 变成 CI 流水线的一个普通产物类型。它的设计哲学是渲染不应该是黑盒而应该是流水线里清晰的一步。这两种取向各有拥趸但它们都承认一个前提静态站点的最终托管可以由 Git 平台直接完成不需要独立的部署服务器。5.2 基于 Git 的 CMS把编辑体验搬进仓库比 Pages 更进一步的是那些把 Git 当作内容后台的内容管理系统。Decap CMS原来的 Netlify CMS就是一个典型用户在浏览器里编辑内容保存时 CMS 自动创建一个 commit 并推送回 GitHub发布按钮对应的是创建合并请求或者直接合并。整个 CMS 没有自己的数据库Git 仓库就是唯一的真相来源。CloudCannon 和 TinaCMS 也走类似路线只是更强调可视化编辑和实时预览。这些工具的产品逻辑说明一个问题当 Git 仓库本身承载了内容的全部版本历史之后内容管理系统的职能从存储内容退缩为操作内容。这和我前面推演的Git 主机内置渲染其实是同一个方向的两条路径一个从托管侧往内容侧延伸一个从内容编辑侧往 Git 侧延伸。5.3 数字花园与笔记工具Git 作为内容源还有一个很有意思的现象是近几年数字花园Digital Garden和笔记发布工具的流行让 Git 作为内容源这件事变得格外自然。比如 Quartz它会把一个 Obsidian 仓库渲染成静态站点部署到 Netlify 或者 GitHub Pages用户在 Obsidian 里写笔记处理好双链push 到仓库站点自动更新。这套流程里用户的写作环境和发布管道完全解耦。写作者只关心本地文件和 Git 提交平台负责剩下的渲染与托管。我见过一些知识管理重度用户他们的整个博客体系就是一个私有仓库 一个公开静态站点中间没有任何 CMS。这种工作方式本质上就是如果我的 Git 主机是静态站点生成器这个设想的日常化版本。5.4 这些项目共同验证的结论把前面这些案例放在一起能提炼出两个共识。第一内容即代码已经脱离概念阶段成为大量工具默认支持的实践方式。无论是 Pages、基于 Git 的 CMS还是数字花园工具它们共同组成了一个生态内容的编辑、版本管理、评审、发布全部落在 Git 仓库这一个载体上。第二真正的瓶颈不在技术而在用户体验。Git 的命令行操作对内容编辑者来说仍然有门槛所以上述工具几乎都在做同一件事在 Git 之上提供一层友好的交互界面。Decap CMS 提供可视化表单GitHub 提供网页端编辑Obsidian 提供本地写作界面。它们的努力方向是一致的——把 Git 的强大能力藏到后台让内容创作的人感觉不到 Git 的存在。这个观察反过来印证了那个脑洞问题的价值如果在 Git 主机的层面就把渲染做好Git 作为内容基础设施就不需要藏起来因为它本身就是最自然的工作方式。6. 试过之后才明白的事这个想法的边界、误区和真正的价值6.1 边界一Git 是日志不是数据库这套机制真正适合的范围是有边界的。Git 本质上是一个只追加的变更日志它对文件树的版本管理近乎完美但对查询这件事几乎没有任何优化。你的内容如果强依赖分类、标签、全文检索、实时数据聚合那么仓库即网站的模式很快会碰壁。我的经验是文档站、个人博客、产品落地页、小型团队知识库、项目官网这些内容量不大、更新频率适中、结构相对固定的站点适合 Git 原生渲染。而带有大量用户生成内容、评论系统、动态表单、个性化推荐的站点还是老老实实用动态框架。判断标准很简单如果每次改动都触发全量重新渲染这件事你能接受那这个模式就适合你如果内容更新频繁且只想改一个局部页面静态化的成本就有点划不来了。6.2 边界二让主机执行构建代码是有安全成本的方案一里的 post-receive hook 直接以服务器用户身份执行仓库里的脚本这意味着任何能推送到仓库的人都等于拿到了服务器上的命令执行权限。自己用没关系但多人协作时这个问题就会放大。即便在 GitHub 这类平台上让平台执行你的构建脚本也存在同样的安全隐患只不过平台的沙箱机制替你扛住了大部分风险。如果你想在生产环境里推广这套思路一定要在权限模型上想清楚谁能推送、谁能改构建脚本、构建环境怎么隔离每一环都得有明确答案。这是内置渲染这个想法最务实的一道坎。6.3 误区这个设想的重点不是消灭 CI而是重新分配职责我反复推演之后想明白了一件事纠结要不要 CI其实是个伪命题。CI 系统里那些安装依赖、运行测试、产物缓存的能力是实打实有用的问题只在于它的触发时机和关注点被过度放大了。更合理的分工是CI 负责验证——跑测试、做静态检查、检查链接有效性而 Git 主机负责渲染发布——把通过验证的内容变成线上页面。这样 CI 不再是你每次推送都要盯着看的发布通道而是内容质量的防线。这个视角下的Git 主机即 SSG不是让 CI 消失而是给 CI 松绑让它回到它擅长的领域。6.4 真正打动我的价值版本化内容带来的心智自由实验做到最后技术细节反而没那么重要了。让我真正上瘾的是这套工作流重塑了我对写网站这件事的认知。以前我更新博客要先想部署流程再想内容本身。现在我只想一件事我要写什么。写完 commitpush完事。站点当前是什么状态、上一版长什么样、某句话是什么时候改的这些问题随时可以在 Git 历史里找到答案。我把发布行为从一个需要谨慎对待的项目任务降级成了一次普通的版本提交。更重要的一点是这个模式让内容的所有权变得非常清晰。内容不再散落在某个 CMS 的数据库里不再绑定在某个平台的服务器上而是以普通文件的形式存在于一个你可以随时 clone 到本地的仓库里。哪怕有一天所有自动化构建服务都停运了你手上的仓库依然包含站点的全部材料——只要有一个人愿意执行一次本地构建站点的灵魂就不会丢失。我现在的站点仍然跑在 GitHub Pages 上底层用的还是那套不算复杂的 YAML 构建配置。但我心里清楚真正的网站并不在服务器的某个目录里也不在 CI 的构建产物里。它就是我本地那个 Git 仓库——一个随时可以渲染成网站、也随时可以迁移到任何平台的文件集合。这种确定性和自由度才是Git 主机是静态站点生成器这个假设最大的魅力。如果你也在维护一个静态站点不妨用文中的 post-receive hook 或者一套精简的 CI 配置体验一下push 即发布的感觉我猜你也会和我一样再也回不到盯着构建日志等发布的日子了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价