From a51ab7ed8508a3c38d137755a6cb17c0f83ebf5c Mon Sep 17 00:00:00 2001 From: linyibing Date: Thu, 29 Feb 2024 23:36:38 +0800 Subject: [PATCH] feat: img use cloudflare --- ...53\346\240\274\346\213\226\346\213\275.md" | 2 +- ...d brain \360\237\247\240 with Obsidian.md" | 28 ++--- ...22\346\237\245\346\226\271\346\263\225.md" | 12 +-- .../_posts/HTML5\346\210\252\345\233\276.md" | 2 +- ...27\343\201\256\347\254\224\350\256\260.md" | 20 ++-- ...00\344\275\263\345\256\236\350\267\265.md" | 14 +-- ...0\216 Cluster \346\250\241\345\235\227.md" | 4 +- ...71\345\274\217\351\233\206\351\224\246.md" | 40 +++---- ...16\344\270\213\350\275\275\346\235\241.md" | 2 +- ...t \345\244\207\345\277\230\345\275\225.md" | 16 +-- ...5\244\247\350\204\221 \360\237\247\240.md" | 30 +++--- ...44\273\216line-height\345\210\2600-5px.md" | 2 +- ...345\217\221Web\345\211\215\347\253\257.md" | 8 +- ...44\350\241\214\345\267\245\345\205\267.md" | 6 +- ...50\347\232\204\345\256\236\350\267\265.md" | 76 ++++++------- ...13\345\214\226\345\256\236\350\267\265.md" | 102 +++++++++--------- ...00\344\270\252\346\217\222\344\273\266.md" | 8 +- ...37\346\230\257\345\256\203\357\274\237.md" | 12 +-- ...44\270\244\345\210\206\347\232\204 bug.md" | 10 +- ...346\266\210\345\244\261\347\232\2041px.md" | 4 +- ...27\344\272\254\346\210\267\345\217\243.md" | 6 +- ...16\347\253\257\345\210\206\347\246\273.md" | 2 +- ...241\271\347\233\256\347\232\204 eslint.md" | 6 +- 23 files changed, 206 insertions(+), 206 deletions(-) diff --git "a/source/_posts/16\345\256\253\346\240\274\346\213\226\346\213\275.md" "b/source/_posts/16\345\256\253\346\240\274\346\213\226\346\213\275.md" index 07aa6f33..93aa47ed 100644 --- "a/source/_posts/16\345\256\253\346\240\274\346\213\226\346\213\275.md" +++ "b/source/_posts/16\345\256\253\346\240\274\346\213\226\346\213\275.md" @@ -13,7 +13,7 @@ tags: JavaScript # 16宫格拖拽 实现一个如图所示的16宫格页面,其中各个数字盒子之间是能相互拖拽,并交换位置的。而横纵各自的标题栏ABC与XYZ实现的功能则是,ABC(XYZ)之间两两互换位置,从而引起两列(行)一起调换位置。 -![示意图](./post-img/16宫格拖拽.png) +![示意图](https://quanru-github-io.pages.dev/post-img/16宫格拖拽.png) # 外观处理 diff --git "a/source/_posts/Building my second brain \360\237\247\240 with Obsidian.md" "b/source/_posts/Building my second brain \360\237\247\240 with Obsidian.md" index 046ebfe6..e2716d13 100644 --- "a/source/_posts/Building my second brain \360\237\247\240 with Obsidian.md" +++ "b/source/_posts/Building my second brain \360\237\247\240 with Obsidian.md" @@ -29,7 +29,7 @@ The content of this article is out of date. Please refer to the official website ### What is Obsidian? -![](./post-img/LifeOS-示例-标注-EN.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例-标注-EN.png) Here is how it introduces itself on the official website: @@ -55,7 +55,7 @@ I adopt two systems: one for knowledge management and another for periodic notes ### Core Systems -![](./post-img/第二大脑系统图-EN.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑系统图-EN.png) - Knowledge management: Using the [PARA](https://fortelabs.com/blog/para/) system - Projects -> Projects are a series of tasks related to a goal with a deadline. @@ -107,11 +107,11 @@ You might be curious that the above subsystem seems to only use "periodic notes. #### Tag Connection -![](./post-img/第二大脑系统图-连接-EN.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑系统图-连接-EN.png) Treat the first-level folders under PARA as special tags (they don't need to be exactly the same as the folder names), use them in "periodic notes," then you can index uniformly in each PARA folder in the same way. This ensures that the README.md index in each PARA folder has all the context for the current topic: -![](./post-img/LifeOS-示例-标注-EN.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例-标注-EN.png) #### Project Connection @@ -122,7 +122,7 @@ Generate a project in "knowledge management," to enhance focus on the project, t - The "priority first dimension" in the quarterly review is a snapshot of the current domain list, used for arranging main event dimension goals and subsequent reviews. - The "priority first dimension" in the annual review, automatically merged and deduplicated from the quarter's priority first dimension, used for setting domain dimension goals and subsequent reviews. -![](./post-img/LifeOS-示例-EN.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例-EN.png) ### Search @@ -148,16 +148,16 @@ Generate a project in "knowledge management," to enhance focus on the project, t ### Creating Notes - Quickly create daily, weekly, monthly, seasonal, and annual notes through the note creation module in the upper left corner. -![](./post-img/第二大脑周期笔记创建.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑周期笔记创建.png) - Quickly create PARA notes, i.e., projects, areas, resources, archives through the note creation module in the upper left corner. -![](./post-img/第二大脑PARA笔记创建.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑PARA笔记创建.png) ### "Daily Note" and "Project README" - Used for daily management, including project lists, daily records, habit tracking, energy allocation, today's accomplishments, etc. - The "project list" in the daily note is a snapshot of the current projects (i.e., under the Projects directory). -![](./post-img/LifeOS-示例-EN.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例-EN.png) ### "Weekly Review" and "Monthly Review" @@ -165,7 +165,7 @@ Generate a project in "knowledge management," to enhance focus on the project, t - In the weekly and monthly reviews, the "priority first dimension" are a collection of snapshots of the project lists from daily notes of the period (generated automatically). - The "reviews" in the weekly and monthly notes mainly focus on the projects of the period. -![](./post-img/LifeOS-示例2-EN.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例2-EN.png) ### "Quarterly Review" and "Annual Review" @@ -174,11 +174,11 @@ Generate a project in "knowledge management," to enhance focus on the project, t - In the annual review, the "priority first dimension" is a collection of snapshots from the priority first dimension of the period's quarterly reviews (generated automatically). - The "reviews" in the quarterly and annual notes mainly focus on the domains of the period. -![](./post-img/LifeOS-示例3-EN.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例3-EN.png) ### "PARA Index" and "Task Index" -![](./post-img/LifeOS-示例4-EN.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例4-EN.png) ### "Capture" and "Express" @@ -193,11 +193,11 @@ Those familiar with PARA will see that this model is actually proposed by Tiago My practice is to temporarily store some marked articles in the "Capture" directory while using the "-1. Capture/README.md" file to index the notes tagged with `#PARA/Capture` scattered in the daily notes. This makes it convenient for me during specific time nodes, such as weekends, month-ends, and quarter-ends, to review and sort, first organizing marked articles into each PARA topical directory, then transferring some inspirational notes from the daily notes into some explicit to-dos; -![](./post-img/第二大脑捕获-EN.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑捕获-EN.png) Next, let's talk about "Express." I place my blog in the express section and also record some fragmentary notes in the daily note tagged with `PARA/Express`. These are outputs after internalization. If this output needs to be further posted on a specific social platform, such as Zhihu or Xiaohongshu, I will conveniently record it as a task. When I review the tasks indexed in the "5. Express/README.md" file and find pending ones, just complete them one by one. -![](./post-img/第二大脑表达-EN.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑表达-EN.png) ## Small Tips in Practice @@ -225,7 +225,7 @@ I consider there to be three types of task reminders: ### Micro-Habits -![](./post-img/福格行为模型-EN.png) +![](https://quanru-github-io.pages.dev/post-img/福格行为模型-EN.png) - I list micro-habits in my journal, and remember, they are not tasks. It's okay whether they are completed or not; they mainly serve as a reminder, "Do you consider practicing these micro-habits today?" These act as prompts when I have the "capability" and "motivation." For example: - Micro-Habits diff --git "a/source/_posts/Chrome \350\260\234\344\270\200\346\240\267\345\215\241\346\255\273\347\232\204\346\216\222\346\237\245\346\226\271\346\263\225.md" "b/source/_posts/Chrome \350\260\234\344\270\200\346\240\267\345\215\241\346\255\273\347\232\204\346\216\222\346\237\245\346\226\271\346\263\225.md" index 2b44bc44..0a42a3cd 100644 --- "a/source/_posts/Chrome \350\260\234\344\270\200\346\240\267\345\215\241\346\255\273\347\232\204\346\216\222\346\237\245\346\226\271\346\263\225.md" +++ "b/source/_posts/Chrome \350\260\234\344\270\200\346\240\267\345\215\241\346\255\273\347\232\204\346\216\222\346\237\245\346\226\271\346\263\225.md" @@ -20,7 +20,7 @@ tags: 查看 pending 状态的请求: -![pending](./post-img/chrome1.jpg) +![pending](https://quanru-github-io.pages.dev/post-img/chrome1.jpg) 发现请求卡在了一个 "Stalled" 的状态,谷歌还贴心的给出了 ["Explanation"](https://developers.google.com/web/tools/chrome-devtools/network-performance/reference?utm_source=devtools#timing-explanation) 链接,解释如下: @@ -71,7 +71,7 @@ tags: 按上述方式打开浏览器,实时查看日志文件,一步一步复现步骤,日志打印如下: -![log](./post-img/chrome2.jpg) +![log](https://quanru-github-io.pages.dev/post-img/chrome2.jpg) 所以绕了一圈还是 "ResizeObserver" 的问题,原因在 [Chrome 83 下千帆工作台卡死的问题](http://way.xiaojukeji.com/article/22698) 中也有提到,这里列两个 issue 大家有兴趣查看下: @@ -107,11 +107,11 @@ EventTarget.prototype.addEventListenerBase = EventTarget.prototype.addEventListe 所以,还有除了 `EventTarget.prototype.addEventListener` 方法之外的监听没有被重写,我掐指一算,难道是 `window.onerror`,于是去当前卡死页面的调试控制台打印 `window.onerror`: -![log](./post-img/chrome3.jpg) +![log](https://quanru-github-io.pages.dev/post-img/chrome3.jpg) 真有这个监听,而且还有『字符串 replace 操作』,这要是无限循环调用这个回调,分分钟卡死!顺手点击这个打印结果,直接跳转到引用它的代码: -![log](./post-img/chrome4.jpg) +![log](https://quanru-github-io.pages.dev/post-img/chrome4.jpg) 竟然是 vConsole 监听的,前端同学都知道,这个 vConsole 是为了在移动端方便调试使用的,一般在测试环境使用,所以这端代码很可能是这个导致了测试环境卡死,而线上环境正常的罪魁祸首! @@ -121,11 +121,11 @@ EventTarget.prototype.addEventListenerBase = EventTarget.prototype.addEventListe 接下来我们就验证下猜想,首先把 window.onerror 覆盖为 console.log,重复复现步骤,控制台便打印出: -![verify](./post-img/chrome5.jpg) +![verify](https://quanru-github-io.pages.dev/post-img/chrome5.jpg) 果不其然,不过这样还是卡住了,因为 console.log 也是同步操作。接着直接置空 window.onerror,执行 `window.onerror = undefined`,再来一次复现: -![verify](./post-img/chrome6.jpg) +![verify](https://quanru-github-io.pages.dev/post-img/chrome6.jpg) diff --git "a/source/_posts/HTML5\346\210\252\345\233\276.md" "b/source/_posts/HTML5\346\210\252\345\233\276.md" index 8a312a54..a0e69165 100644 --- "a/source/_posts/HTML5\346\210\252\345\233\276.md" +++ "b/source/_posts/HTML5\346\210\252\345\233\276.md" @@ -14,7 +14,7 @@ tags: # 需求 实现一个类似于QQ截图的小东西,点击载入按钮,则载入图片,长按图片,弹出截图框,截图框右下角能够调整大小,并在右边的截图预览区域实时显示,其最终效果图如下: -![示意图](./post-img/screenShot.png) +![示意图](https://quanru-github-io.pages.dev/post-img/screenShot.png) # HTML 需要注意canvas的设置,主要结构如下: diff --git "a/source/_posts/JavaScript\346\235\203\345\250\201\346\214\207\345\215\227\343\201\256\347\254\224\350\256\260.md" "b/source/_posts/JavaScript\346\235\203\345\250\201\346\214\207\345\215\227\343\201\256\347\254\224\350\256\260.md" index 6c447eb8..6a941bba 100644 --- "a/source/_posts/JavaScript\346\235\203\345\250\201\346\214\207\345\215\227\343\201\256\347\254\224\350\256\260.md" +++ "b/source/_posts/JavaScript\346\235\203\345\250\201\346\214\207\345\215\227\343\201\256\347\254\224\350\256\260.md" @@ -183,7 +183,7 @@ tags: 47. 原型对象是类的唯一标识,当且仅当两个对象继承自同一个原型对象时,它们才属于同一个类的实例。 48. 对于任意函数F.prototype.constructor==F;一个对象的constructor属性指代这个类。 -49. 构造函数与原型对象之间的关系:![](./post-img/权威指南1.png) +49. 构造函数与原型对象之间的关系:![](https://quanru-github-io.pages.dev/post-img/权威指南1.png) 50. >1. 任何添加到构造函数对象(不是指添加到构造函数内部)的属性都是类字段和类方法,属于类而不属于类的某个实例; @@ -198,27 +198,27 @@ tags: >3. 以上两个方法不适用于多窗口和多框架子页面,因此可以使用构造函数的名称; 53. 工厂方法: -![](./post-img/权威指南2.png) -![](./post-img/权威指南3.png) +![](https://quanru-github-io.pages.dev/post-img/权威指南2.png) +![](https://quanru-github-io.pages.dev/post-img/权威指南3.png) 54. 构造函数方法: -![](./post-img/权威指南4.png) +![](https://quanru-github-io.pages.dev/post-img/权威指南4.png) 54. toJSON()用于序列号对象,如果一个对象有toJSON()方法,则JSON.stringify()并不会对传入的对象做序列号操作,而会调用toJSON()来执行序列号操作,JSON.parse()是其逆过程。 -55. forEach:![](./post-img/权威指南5.png) +55. forEach:![](https://quanru-github-io.pages.dev/post-img/权威指南5.png) 56. 私有方法: >通过将变量(或参数)闭包在一个构造函数内来模拟实现私有实例字段: -![](./post-img/权威指南6.png) +![](https://quanru-github-io.pages.dev/post-img/权威指南6.png) 57. 创建子类的关键: >1. B.prototype = inherit(A.prototype);//子类派生自父类 >2. B.prototype.constructor = B;//重载继承来的constructor属性 58. 用组合代替继承的集合的实现: -![](./post-img/权威指南7.png) -![](./post-img/权威指南8.png) +![](https://quanru-github-io.pages.dev/post-img/权威指南7.png) +![](https://quanru-github-io.pages.dev/post-img/权威指南8.png) 59. >1. Object.preventExtensions():将对象设置为不可扩展的,即不能给对象添加任何新属性; @@ -227,8 +227,8 @@ tags: 60. Object.create(null);//创建一个不包含原型的对象,使之能够直接对它使用in运算符 61. 作为私有命名空间的函数: -![](./post-img/权威指南9.png) -![](./post-img/权威指南10.png) +![](https://quanru-github-io.pages.dev/post-img/权威指南9.png) +![](https://quanru-github-io.pages.dev/post-img/权威指南10.png) 1. 创建屏外图像: >new Image(80,20).src = "images/***.gif"; diff --git "a/source/_posts/Monorepo \344\270\213 Git \345\267\245\344\275\234\346\265\201\347\232\204\346\234\200\344\275\263\345\256\236\350\267\265.md" "b/source/_posts/Monorepo \344\270\213 Git \345\267\245\344\275\234\346\265\201\347\232\204\346\234\200\344\275\263\345\256\236\350\267\265.md" index e6c5d7ff..40a4f2e6 100644 --- "a/source/_posts/Monorepo \344\270\213 Git \345\267\245\344\275\234\346\265\201\347\232\204\346\234\200\344\275\263\345\256\236\350\267\265.md" +++ "b/source/_posts/Monorepo \344\270\213 Git \345\267\245\344\275\234\346\265\201\347\232\204\346\234\200\344\275\263\345\256\236\350\267\265.md" @@ -25,7 +25,7 @@ tags: 2. Git flow, Gitlab flow, Github flow 都属于 feature branch development,它们有一个共同点:都采用『功能驱动式开发』,即:需求是开发的起点,先有需求再有功能分支(feature branch)或者补丁分支(hotfix branch); -![图片](./post-img/mono-git1.png) +![图片](https://quanru-github-io.pages.dev/post-img/mono-git1.png) ## 适用场景 @@ -35,7 +35,7 @@ tags: **总的来说,选择一个工作流不仅仅是一系列操作工具的流程,我们往往还需要对它背后的思想买单;下面的表格是两种工作流模式在各个维度的适用情况:** -![图片](./post-img/mono-git2.png) +![图片](https://quanru-github-io.pages.dev/post-img/mono-git2.png) > 目前大部分业务场景使用的都是 feature branch 的开发模式,如果你的业务是多人开发一个巨型应用(如抖音主站、飞书文档等),应该尝试使用 Trunk based 开发模式,这会提高仓库整体工程质量和管理水平。 @@ -50,7 +50,7 @@ tags: 『功能分支开发模式』的核心思想是所有特性开发都应该在专用的分支,而不是主分支中进行。这种封装使多个开发人员,可以轻松地在不干扰主代码库的情况下处理特定功能。这也意味着主分支永远不会包含损坏的代码,这对于持续集成环境来说是一个巨大的优势。-- Git Feature Branch Workflow | Atlassian Git Tutorial -![图片](./post-img/mono-git3.png) +![图片](https://quanru-github-io.pages.dev/post-img/mono-git3.png) - #### **上线模式** @@ -103,7 +103,7 @@ tags: 与其它存在『长期分支』的功能分支策略相比,开发者可以通过一些小的提交创建『短期分支』。随着代码库复杂性和团队规模的增长,『基于主干的开发模式』有助于保持生产版本的流畅。-- Trunk-based Development | Atlassian -![图片](./post-img/mono-git4.png) +![图片](https://quanru-github-io.pages.dev/post-img/mono-git4.png) - #### **上线模式** @@ -113,7 +113,7 @@ tags: 半自动化流程,适用于低频率部署,以及自动化测试不全面的项目 -![图片](./post-img/mono-git5.png) +![图片](https://quanru-github-io.pages.dev/post-img/mono-git5.png) (A dot represents an MR merged into master. Green dots means good commits that passed e2e tests, and red dot means a buggy commit which should be avoided when deploying/rollback) @@ -138,7 +138,7 @@ tags: 全自动化流程,适用于需要高频率部署,以及自动化测试较为全面的项目 -![图片](./post-img/mono-git6.png) +![图片](https://quanru-github-io.pages.dev/post-img/mono-git6.png) (A dot represents an MR merged into master. Green dots means good commits that passed e2e tests, and red dot means a buggy commit which should be avoided when deploying/rollback) @@ -184,7 +184,7 @@ Trunk-based development 更容易做到线性的 commit 历史,它有如下几 4. 撤销变更,比如:当你发现一个有问题的 commit,简单的 revert 对应的 commit 即可,而非线性的历史会有很多跨分支的合并,使 revert 变得困难 -![图片](./post-img/mono-git7.jpg) +![图片](https://quanru-github-io.pages.dev/post-img/mono-git7.jpg) ### **有效的两个前提** diff --git "a/source/_posts/Node.js \347\232\204 Morgan \346\250\241\345\235\227\344\270\216 Cluster \346\250\241\345\235\227.md" "b/source/_posts/Node.js \347\232\204 Morgan \346\250\241\345\235\227\344\270\216 Cluster \346\250\241\345\235\227.md" index f58b425f..74c2a422 100644 --- "a/source/_posts/Node.js \347\232\204 Morgan \346\250\241\345\235\227\344\270\216 Cluster \346\250\241\345\235\227.md" +++ "b/source/_posts/Node.js \347\232\204 Morgan \346\250\241\345\235\227\344\270\216 Cluster \346\250\241\345\235\227.md" @@ -20,7 +20,7 @@ tags: 整个 microblog 的代码已放至 github 上:[摸我](https://github.com/quanru/microblog) -![ ](./post-img/microblog.png "microblog") +![ ](https://quanru-github-io.pages.dev/post-img/microblog.png "microblog") # 二、Morgan 模块の日志生成 @@ -74,7 +74,7 @@ tags: } ``` 此时查看进程管理器,发现有多个 node 进程,而原来的代码则只有两个: -![ ](./post-img/node.png "node 进程个数") +![ ](https://quanru-github-io.pages.dev/post-img/node.png "node 进程个数") # 四、Node.js 除虫工具 diff --git "a/source/_posts/Node.js \350\260\203\350\257\225\346\226\271\345\274\217\351\233\206\351\224\246.md" "b/source/_posts/Node.js \350\260\203\350\257\225\346\226\271\345\274\217\351\233\206\351\224\246.md" index 0e0b62d5..4d51531b 100644 --- "a/source/_posts/Node.js \350\260\203\350\257\225\346\226\271\345\274\217\351\233\206\351\224\246.md" +++ "b/source/_posts/Node.js \350\260\203\350\257\225\346\226\271\345\274\217\351\233\206\351\224\246.md" @@ -84,7 +84,7 @@ tags: #### 步骤三:启动前端调试服务(如果有的话) - ![image](./post-img/启动调试服务.png) + ![image](https://quanru-github-io.pages.dev/post-img/启动调试服务.png) @@ -104,9 +104,9 @@ tags: #### 步骤五:打开 Chrome,并打开 dev tool,当你的 Chrome 足够新(60 以上),可点击如下 node.js 图标,进入调试 - ![image](./post-img/打开调试.png) + ![image](https://quanru-github-io.pages.dev/post-img/打开调试.png) - ![image](./post-img/打断点.png) + ![image](https://quanru-github-io.pages.dev/post-img/打断点.png) @@ -122,13 +122,13 @@ tags: #### 步骤二:切换到 debug tab: - ![image](./post-img/vscode-debug.png) + ![image](https://quanru-github-io.pages.dev/post-img/vscode-debug.png) #### 步骤三:打开 launch.json 文件 - ![image](./post-img/编辑-vscode-配置.png) + ![image](https://quanru-github-io.pages.dev/post-img/编辑-vscode-配置.png) @@ -160,19 +160,19 @@ tags: #### 步骤五:启动前端调试服务(如果有的话) - ![image](./post-img/启动调试服务.png) + ![image](https://quanru-github-io.pages.dev/post-img/启动调试服务.png) #### 步骤六:使用 VSCode 打开源码文件,点击行号左侧以添加断点 - ![image](./post-img/源码断点.png) + ![image](https://quanru-github-io.pages.dev/post-img/源码断点.png) #### 步骤七:切回 debug tab,启动 node.js 服务,即可调试 - ![image](./post-img/调试.png) + ![image](https://quanru-github-io.pages.dev/post-img/调试.png) --- @@ -180,19 +180,19 @@ tags: #### 步骤一:打开项目目录,展开 `Run` 菜单,选择 `Edit Configurations` - ![image](./post-img/WebStorm.png) + ![image](https://quanru-github-io.pages.dev/post-img/WebStorm.png) #### 步骤二:新建 Node.js 调试配置 - ![image](./post-img/WebStorm-配置.png) + ![image](https://quanru-github-io.pages.dev/post-img/WebStorm-配置.png) #### 步骤三:配置参考 - ![image](./post-img/WebStorm-配置参考.png) + ![image](https://quanru-github-io.pages.dev/post-img/WebStorm-配置参考.png) @@ -207,19 +207,19 @@ tags: #### 步骤四:启动前端调试服务(如果有的话) - ![image](./post-img/启动调试服务.png) + ![image](https://quanru-github-io.pages.dev/post-img/启动调试服务.png) #### 步骤五:使用 WebStorm 打开源码文件,点击行号左侧以添加断点 - ![image](./post-img/WebStorm-源码调试.png) + ![image](https://quanru-github-io.pages.dev/post-img/WebStorm-源码调试.png) #### 步骤六:点击启动调试服务 - ![image](./post-img/WebStorm-启动调试.png) + ![image](https://quanru-github-io.pages.dev/post-img/WebStorm-启动调试.png) --- @@ -227,34 +227,34 @@ tags: #### 步骤一:安装插件:a. [xatom-debug](https://github.com/willyelm/xatom-debug) b. [xatom-debug-nodejs](https://github.com/willyelm/xatom-debug) - ![image](./post-img/atom-安装插件.png) + ![image](https://quanru-github-io.pages.dev/post-img/atom-安装插件.png) #### 步骤二:打开 node 项目入口文件,并在箭头处切换需要调试的项目目录,之后点击箭头右侧的 Node.js 按钮进入配置 - ![image](./post-img/atom-切换调试目录.png) + ![image](https://quanru-github-io.pages.dev/post-img/atom-切换调试目录.png) #### 步骤三:配置调试 - ![image](./post-img/atom-配置调试.png) + ![image](https://quanru-github-io.pages.dev/post-img/atom-配置调试.png) #### 步骤四:启动前端调试服务(如果有的话) - ![image](./post-img/启动调试服务.png) + ![image](https://quanru-github-io.pages.dev/post-img/启动调试服务.png) #### 步骤五:使用 ATOM 打开源码文件,点击行号左侧以添加断点 - ![image](./post-img/atom-源码调试.png) + ![image](https://quanru-github-io.pages.dev/post-img/atom-源码调试.png) #### 步骤六:点击启动调试服务 - ![image](./post-img/atom-启动调试服务.png) + ![image](https://quanru-github-io.pages.dev/post-img/atom-启动调试服务.png) diff --git "a/source/_posts/Schema\344\270\216\344\270\213\350\275\275\346\235\241.md" "b/source/_posts/Schema\344\270\216\344\270\213\350\275\275\346\235\241.md" index 405f6bbd..5c060c5d 100644 --- "a/source/_posts/Schema\344\270\216\344\270\213\350\275\275\346\235\241.md" +++ "b/source/_posts/Schema\344\270\216\344\270\213\350\275\275\346\235\241.md" @@ -20,7 +20,7 @@ tags: 1. 点击下载时,如果本机已经安装该软件,则尝试打开对应软件; 2. 点击下载时,如果本机未安装该软件,则跳转到对应系统的下载地址。 -![示意图](./post-img/downloadBar.png) +![示意图](https://quanru-github-io.pages.dev/post-img/downloadBar.png) # schema协议 diff --git "a/source/_posts/git checkout \344\270\216 git reset \345\244\207\345\277\230\345\275\225.md" "b/source/_posts/git checkout \344\270\216 git reset \345\244\207\345\277\230\345\275\225.md" index 361f2836..22788f67 100644 --- "a/source/_posts/git checkout \344\270\216 git reset \345\244\207\345\277\230\345\275\225.md" +++ "b/source/_posts/git checkout \344\270\216 git reset \345\244\207\345\277\230\345\275\225.md" @@ -44,7 +44,7 @@ tags: 如图, 当我们修改一个文件后, 查看 git status, 给出了如下提示, 要么使用 git add 将修改过的文件提交到暂存区, 要么使用 git checkout -- 丢弃当前工作区对该文件的修改. 可以发现执行 git checkout -- 之后, README.md 恢复到修改之前. -![git](./post-img/git-0.png) +![git](https://quanru-github-io.pages.dev/post-img/git-0.png) # git reset @@ -56,11 +56,11 @@ tags: > 其中 HEAD 可省略, 即默认指向当前 commit. -![git](./post-img/git-1.png) +![git](https://quanru-github-io.pages.dev/post-img/git-1.png) 如下图, 继续修改 README.md, 在文件中追加 ' third modify', 此时查看 git status, 发现除了原有的暂存区修改 (绿色文字) 之外, 还有第二次修改的当前工作区修改 (红色文字), 接着执行 git reset HEAD, 终端提示 '重置后取消暂存的变更', 此时查看 git status, 发现暂存区不见了, 只剩当前工作区, 查看工作区中的 README.md 文件, 发现原有的暂存区修改被撤销了, 且不影响工作区文件. -![git](./post-img/git-2.png) +![git](https://quanru-github-io.pages.dev/post-img/git-2.png) ## '重置后取消暂存的变更' @@ -70,7 +70,7 @@ tags: 如图, 带参数的 git reset 不能 (或不建议) 作用于某个路径, 因此只要用于操作 commitID. -![git](./post-img/git-3.png) +![git](https://quanru-github-io.pages.dev/post-img/git-3.png) ## --soft @@ -78,7 +78,7 @@ tags: 如图, 将 HEAD 指针重置到两个 commit 之前, 发现当前分支落后远程分支两个 commit, 此时查看暂存区 (git diff --cached) 和 工作区 (cat README.md), 可以发现这两个区域的文件相比 git reset --soft 之前都没有变化. -![git](./post-img/git-4.png) +![git](https://quanru-github-io.pages.dev/post-img/git-4.png) ## --mixed @@ -86,7 +86,7 @@ tags: 如图, 此处与 [git reset soft,hard,mixed之区别深解](http://www.cnblogs.com/kidsitcn/p/4513297.html) 中所述的 --mixed 有出入, 并没有 '并且重置index以便和HEAD相匹配'; -![git](./post-img/git-5.png) +![git](https://quanru-github-io.pages.dev/post-img/git-5.png) ## --hard @@ -94,7 +94,7 @@ tags: 如图, 执行 --hard 重置之后, 暂存区与工作区全都消失了, 此时可以选择 git pull, 重新与远程分支一致, 也可使用 git push --force, 强行将远程分支的 commit 记录与本地仓库保持一致. 不过一般建议使用 git revert, 回退的同时保存 commit 的历史记录. -![git](./post-img/git-6.png) +![git](https://quanru-github-io.pages.dev/post-img/git-6.png) ## 到底哪个才是默认参数 @@ -102,4 +102,4 @@ tags: 如图, 可知默认参数是 --mixed -![git](./post-img/git-7.png) +![git](https://quanru-github-io.pages.dev/post-img/git-7.png) diff --git "a/source/_posts/\344\270\200\347\247\215\345\256\236\347\224\250\346\226\260\345\236\213 Obsidian \345\256\236\350\267\265\344\271\213\346\236\204\345\273\272\346\210\221\347\232\204\347\254\254\344\272\214\345\244\247\350\204\221 \360\237\247\240.md" "b/source/_posts/\344\270\200\347\247\215\345\256\236\347\224\250\346\226\260\345\236\213 Obsidian \345\256\236\350\267\265\344\271\213\346\236\204\345\273\272\346\210\221\347\232\204\347\254\254\344\272\214\345\244\247\350\204\221 \360\237\247\240.md" index 2685e6b5..96d4bb62 100644 --- "a/source/_posts/\344\270\200\347\247\215\345\256\236\347\224\250\346\226\260\345\236\213 Obsidian \345\256\236\350\267\265\344\271\213\346\236\204\345\273\272\346\210\221\347\232\204\347\254\254\344\272\214\345\244\247\350\204\221 \360\237\247\240.md" +++ "b/source/_posts/\344\270\200\347\247\215\345\256\236\347\224\250\346\226\260\345\236\213 Obsidian \345\256\236\350\267\265\344\271\213\346\236\204\345\273\272\346\210\221\347\232\204\347\254\254\344\272\214\345\244\247\350\204\221 \360\237\247\240.md" @@ -29,7 +29,7 @@ tags: ### 什么是 Obsidian? -![](./post-img/LifeOS-示例-标注.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例-标注.png) 官网上它是这么自我介绍的: @@ -52,13 +52,13 @@ tags: ## 我的实践 -![](./post-img/第二大脑最新系统图.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑最新系统图.png) 我采用两套系统,一个是知识管理系统,另一个是周期笔记,前者以项目/领域/资源为维度,进行知识管理,后者以时间为维度,进行任务/目标/时间管理。 ### 两套系统 -![](./post-img/第二大脑系统图.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑系统图.png) - 知识管理:采用 [PARA](https://fortelabs.com/blog/para/) 系统 - Projects -> 项目是与目标相关的一系列任务,有截止日期 @@ -109,11 +109,11 @@ tags: #### 标签连接 -![](./post-img/第二大脑系统图-连接.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑系统图-连接.png) 将 PARA 下的一级文件夹作为一种特殊的标签(不一定要与文件名完全一致),在『周期笔记』中使用,那么便可在各个一级文件夹中,以相同的方式进行统一的索引。这样能保证每个 PARA 文件夹下的 README.md 索引有当前主题的所有上下文: -![](./post-img/LifeOS-示例-标注.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例-标注.png) #### 项目连接 @@ -124,7 +124,7 @@ tags: - 季记的『要事维度』,它是一份当前领域列表的快照,用于安排要事维度的目标和后续的复盘 - 年记中的『要事维度』,是从本年的季记中自动合并去重得到的一个列表,用于设置领域维度的目标和后续的复盘 -![](./post-img/LifeOS-示例.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例.png) ### 检索 @@ -150,9 +150,9 @@ tags: ### 创建笔记 - 通过左上角笔记创建模块,快速创建日记、周记、月记、季记、年记 -![](./post-img/第二大脑周期笔记创建.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑周期笔记创建.png) - 通过左上角笔记创建模块,快速 PARA 笔记,即项目、领域、资源、存档 -![](./post-img/第二大脑PARA笔记创建.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑PARA笔记创建.png) ### 『日记』与『项目 README』 @@ -160,7 +160,7 @@ tags: - 用于日常管理,包括项目列表、日常记录、习惯打卡、精力分配、今日完成等模块 - 日记中的『项目列表』是一份当前项目(即 Projects 目录下)的快照 -![](./post-img/LifeOS-示例.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例.png) ### 『周记』与『月记』 @@ -168,7 +168,7 @@ tags: - 在周记和月记中,『要事维度』是一份本周期日记『项目列表』的快照合集(自动生成) - 在周记和月记中,『复盘』主要针对本周期内的项目进行 -![](./post-img/LifeOS-示例2.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例2.png) ### 『季记』与『年记』 @@ -177,11 +177,11 @@ tags: - 在年记中,『要事维度』是一份本周期季记『要事维度』的快照合集(自动生成) - 在季记和年记中,『复盘』主要针对本周期内的领域进行 -![](./post-img/LifeOS-示例3.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例3.png) ### 『PARA 索引』与『任务索引』 -![](./post-img/LifeOS-示例4.png) +![](https://quanru-github-io.pages.dev/post-img/LifeOS-示例4.png) ### 『捕获』与『表达』 @@ -196,11 +196,11 @@ tags: 而我的实践就是在『捕获』目录中,暂时存放一些标记过的文章,同时借助『-1. 捕获/README.md』文件去索引分散在日记中,被打上 `#PARA/Capture` 标签的记录,这样方便我在特定的时间节点,比如每周末、每月末、每季度末进行回顾梳理,首先将标记文章梳理到各个 PARA 主题目录,接着将日记中的某些灵感记录转化成一些明确的待办事项; -![](./post-img/第二大脑捕获.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑捕获.png) 接下来说说『表达』,我会把自己的博客放到表达中,也会在日记中记录一些零碎的记录并打上 `PARA/Express`  标签,这些都是自己内化后的输出,如果这个输出需要进一步发布到指定的社交平台,比如知乎、小红书,我会顺手把它记录为任务,当我在回顾『5. 表达/README.md』文件索引的时候,发现有待办任务,直接一项一项的完成即可。 -![](./post-img/第二大脑表达.png) +![](https://quanru-github-io.pages.dev/post-img/第二大脑表达.png) ## 实践中的小 Tips @@ -228,7 +228,7 @@ tags: ### 微习惯 -![](./post-img/福格行为模型.png) +![](https://quanru-github-io.pages.dev/post-img/福格行为模型.png) - 我会在日记中列一些微习惯,切记不是任务,完不完成都行,主要用来提醒『这些微习惯,你今天考虑做一下吗?』,即在我有『能力』和『动机』的时候,起到『提示』的作用,比如: - 微习惯 diff --git "a/source/_posts/\344\273\216line-height\345\210\2600-5px.md" "b/source/_posts/\344\273\216line-height\345\210\2600-5px.md" index d6bbf213..e22a0320 100644 --- "a/source/_posts/\344\273\216line-height\345\210\2600-5px.md" +++ "b/source/_posts/\344\273\216line-height\345\210\2600-5px.md" @@ -37,7 +37,7 @@ tags: ## 那么问题来了 -1px边框到底如何在非ios8的retina屏幕下实现呢?今天看到一篇博客里的一句话,我激动了:![被嫌弃的target-densitydpi属性](./post-img/0.5px.png) +1px边框到底如何在非ios8的retina屏幕下实现呢?今天看到一篇博客里的一句话,我激动了:![被嫌弃的target-densitydpi属性](https://quanru-github-io.pages.dev/post-img/0.5px.png) 要是没有最后一句话该多好啊~ diff --git "a/source/_posts/\344\275\277\347\224\250 Linux \347\263\273\347\273\237\345\274\200\345\217\221Web\345\211\215\347\253\257.md" "b/source/_posts/\344\275\277\347\224\250 Linux \347\263\273\347\273\237\345\274\200\345\217\221Web\345\211\215\347\253\257.md" index 2f5c5bed..3af207ea 100644 --- "a/source/_posts/\344\275\277\347\224\250 Linux \347\263\273\347\273\237\345\274\200\345\217\221Web\345\211\215\347\253\257.md" +++ "b/source/_posts/\344\275\277\347\224\250 Linux \347\263\273\347\273\237\345\274\200\345\217\221Web\345\211\215\347\253\257.md" @@ -22,7 +22,7 @@ Mac纵有千千万万的好,作为学生党来说,毕竟其较高的价格 ## 系统安装 建议使用U盘刻录安装,推荐刻录软件[UNetbootin](https://unetbootin.github.io/),将下载好的ISO文件通过UNetbootin烧进U盘,安装前记得空出一块磁盘,系统本身占用很小,10G虽然够,还是建议20G吧。安装过程不细说了,网上教程一大堆,建议第一次安装还是对着教程来吧,记得备份重要文件。这是我安装后的桌面: -![Gnome-shell](./post-img/gnome-shell.png) +![Gnome-shell](https://quanru-github-io.pages.dev/post-img/gnome-shell.png) ## 开发软件 前端开发所需的软件大都有对应的Linux版本,比如Sublime、Atom、Charles、WebStorm、Chrome,大家可自行Google下载。 @@ -117,11 +117,11 @@ Mac纵有千千万万的好,作为学生党来说,毕竟其较高的价格 装好后,打开PlayOnLinux,如何安装请看下图: -![PlayOnLinux](./post-img/playonlinux.png) +![PlayOnLinux](https://quanru-github-io.pages.dev/post-img/playonlinux.png) 再往后就是下一步、下一步、下一步、完成。 -![PhotoShop](./post-img/photoshop.png) +![PhotoShop](https://quanru-github-io.pages.dev/post-img/photoshop.png) wine软件可能出现中文乱码,可参看[彻底消除wine中文乱码](http://www.wuwenhui.cn/2692.html) @@ -139,7 +139,7 @@ wine软件可能出现中文乱码,可参看[彻底消除wine中文乱码](htt 4. 安装主题,本人推荐主题[bullet-train-oh-my-zsh-theme](https://github.com/caiogondim/bullet-train-oh-my-zsh-theme) -![bullet-train-oh-my-zsh-theme](./post-img/zsh.png) +![bullet-train-oh-my-zsh-theme](https://quanru-github-io.pages.dev/post-img/zsh.png) ## 其它软件 diff --git "a/source/_posts/\344\275\277\347\224\250 Node.js \345\260\206\347\217\215\350\227\217\347\232\204 bash \350\204\232\346\234\254\345\260\201\350\243\205\346\210\220\345\221\275\344\273\244\350\241\214\345\267\245\345\205\267.md" "b/source/_posts/\344\275\277\347\224\250 Node.js \345\260\206\347\217\215\350\227\217\347\232\204 bash \350\204\232\346\234\254\345\260\201\350\243\205\346\210\220\345\221\275\344\273\244\350\241\214\345\267\245\345\205\267.md" index ece1ea93..36f2f223 100644 --- "a/source/_posts/\344\275\277\347\224\250 Node.js \345\260\206\347\217\215\350\227\217\347\232\204 bash \350\204\232\346\234\254\345\260\201\350\243\205\346\210\220\345\221\275\344\273\244\350\241\214\345\267\245\345\205\267.md" +++ "b/source/_posts/\344\275\277\347\224\250 Node.js \345\260\206\347\217\215\350\227\217\347\232\204 bash \350\204\232\346\234\254\345\260\201\350\243\205\346\210\220\345\221\275\344\273\244\350\241\214\345\267\245\345\205\267.md" @@ -151,7 +151,7 @@ run() #### 示例 -![](./post-img/gerrit1.jpg) +![](https://quanru-github-io.pages.dev/post-img/gerrit1.jpg) ### JavaScript 工程局部使用(前端工程推荐使用) @@ -174,7 +174,7 @@ run() #### 示例 -![](./post-img/gerrit2.jpg) +![](https://quanru-github-io.pages.dev/post-img/gerrit2.jpg) ### 和 [husky](https://www.npmjs.com/package/husky) 配合使用 @@ -199,7 +199,7 @@ run() `git push` #### 示例 -![](./post-img/gerrit3.jpg) +![](https://quanru-github-io.pages.dev/post-img/gerrit3.jpg) ## TODO - 新增子命令支持生成 gerrit 的配置文件 diff --git "a/source/_posts/\345\211\215\347\253\257 Monorepo \345\234\250\345\255\227\350\212\202\350\267\263\345\212\250\347\232\204\345\256\236\350\267\265.md" "b/source/_posts/\345\211\215\347\253\257 Monorepo \345\234\250\345\255\227\350\212\202\350\267\263\345\212\250\347\232\204\345\256\236\350\267\265.md" index 5d197d92..1fbc5248 100644 --- "a/source/_posts/\345\211\215\347\253\257 Monorepo \345\234\250\345\255\227\350\212\202\350\267\263\345\212\250\347\232\204\345\256\236\350\267\265.md" +++ "b/source/_posts/\345\211\215\347\253\257 Monorepo \345\234\250\345\255\227\350\212\202\350\267\263\345\212\250\347\232\204\345\256\236\350\267\265.md" @@ -18,18 +18,18 @@ tags: ## 本主题简介 -![](./post-img/海报.jpg) +![](https://quanru-github-io.pages.dev/post-img/海报.jpg) ## PPT 正文 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践.jpeg) - 大家早上好,我是林宜丙,今天我带来的分享主题是《前端 Monorepo 在字节跳动的实践》 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%202.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%202.jpeg) - 简单介绍下我自己,我来自字节跳动的 Web Infra 部门,拥有多年前端工程化的经验,主要帮助前端工程师更好地管理和治理工程 - 目前负责前端 Monorepo 解决方案的设计及其落地工作,有丰富的 Monorepo 实践和治理经验 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%203.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%203.jpeg) - 今天的分享分为以下五个部分 - 首先分析『现代前端工程开发』的趋势,并引出字节跳动,前端工程开发面临的痛点 - 其次简单介绍下 Monorepo,以及它为什么能解决我们面临的痛点 @@ -38,47 +38,47 @@ tags: - 最后总结当前方案的问题和针对这些问题的展望 - OK,我们先来看看现代前端工程开发的几个趋势 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%204.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%204.jpeg) - 首先是前端工种的趋势 - 第一个趋势是涉及的平台越来越多,Web,Node,客户端和跨平台等 - 第二个趋势是所能支撑的业务越来越多,复杂度越来越大,特别是近年来前端侧涌现出不少重前端交互的应用,比如搭建类的 figma,文档类的飞书等 - 第三个趋势是随着上述两个趋势而来地、不可避免地使得前端团队的规模不断增大 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%205.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%205.jpeg) - 上述三个趋势又客观上造成了前端工程的四个趋势,即: - 代码规模增大,内部已经出现代码量超过 10G 的大型工程 - 维护人数增多,一个工程少则十来人,多则四五十人 - 研发工具增加,不断出现的新工具在一个工程上堆叠,构建方面比如 webpack,rollup,vite 等,测试方面比如 jest,vitest 等 - 依赖关系复杂,各类项目安装依赖后的 lock 文件大小,足以说明一个工程的复杂依赖关系 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%206.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%206.jpeg) - 那么,在上述这些个趋势下,我们的前端工程开发面临了哪些痛点呢?主要有三个: - 其一,项目基建重复,每次新增项目都需要重复配置 Git,构建平台,CI/CD 配置等 - 其二,代码复用困难,跨项目的代码复用和调试极其繁琐,往往通过发布 npm 包来复用,而这种复用方式又不可避免地遇到更新触达率的问题 - 其三,工作流程割裂,一个功能往往涉及到多个模块,这时需要分别在各个模块的工程上开发、合码、上线和验证,这是繁琐和割裂的 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%207.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%207.jpeg) - 面对上述痛点,我们该如何更好地组织工程,以提高工程的『可维护性』和『开发效率』?我们给出的答案是 Monorepo! -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%208.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%208.jpeg) - 接下来我们来简单介绍下 Monorepo -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%209.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%209.jpeg) - 首先我们来看看,什么是 Monorepo 呢?Monorepo 是一种源代码管理的模式,其形式就是将多个项目集中到一个仓库中管理; - 与 Monorepo 相对的是 Polyrepo 模式,这种模式各个项目都有独立的仓库。 - 这里需要注意,Monorepo 的子项目不仅仅是简单地放到一个仓库中维护而已,处理子项目间的关系也十分重要。 - 子项目也可以是任意的类型,可以是 web 项目,也可以是 node 项目和 Lib 库。 - 总而言之,Monorepo 就是将多个不同的项目以良好的组织关系放到单个仓库中维护。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2010.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2010.jpeg) - 在前端领域,大家可能对 Lib 型的 Monorepo 更加熟悉,知名的开源项目比如 React、Vue、Babel 等都采用 Monorepo 方式管理源码,大家看左边这个图,通过将整个系统拆分成多个 package,便于抽象和复用,并且这些 package 往往不需要走线上部署流程,只需要发布到 npm registry 即可。我相信大家在公司中接触和使用到的大部分 Monorepo 项目就是这个类型,但是这类项目往往在商业公司中并不是主流。 - 除了 Lib 型以外,我们看右边这张图,多个 App 也是可以放到一个仓库中维护的,这就是 App 型的 Monorepo,它包含多个 App 项目,以及项目共享的组件、工具函数等等,App 类型的项目需要走完整的部署流程,App 及其依赖的 Lib 一般不需要发布到 npm registry,这类项目呢,才是商业公司的主流。 - 在字节跳动,大部分的项目都是 App 应用,我们侧重在这个类型上建设自研的 Monorepo 方案,它的覆盖的人群和覆盖的应用是最多的,因此它的收益也是巨大的;当然支持了 App 型的 Monorepo,自然也就支持了 Lib 型的 Monorepo。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2011.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2011.jpeg) - 那为什么要采用 Monorepo 呢?接下来我们看看使用 Monorepo 的收益都有哪些?它是如何解决我们遇到的痛点的? -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2012.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2012.jpeg) - 首先,Monorepo 可以降低多项目的维护成本,从而解决项目基建重复的痛点。 - Polyrepo 下,每个项目都需要有同学创建和维护,当创建更多项目的时候,需要更多同学,或者更多精力去创建和维护。 - 而在 Monorepo 中,只需要少数几个同学负责设立起 Monorepo,所有的项目以及将来的项目都能够在一个仓库中统一维护,从而降低多项目维护成本; @@ -86,7 +86,7 @@ tags: - 同时将一个项目的调整,同步到其他项目的成本也很高,比如想在 CI 流程中为所有项目加入类型检查,来提高下 ts 项目的质量,那么需要修改每个项目,提交代码,跑 CI,这样成本其实是很高的。而在 Monorepo,只需要创建一套基建,所有子项目,以及未来的子项目都能够接入现有的基建。这些基建的调整和维护,也能够很容易地应用到多个项目。 - 在 Polyrepo 中,如果要开发多个项目的话,还可能需要来回切换开发环境、切换仓库,link 代码,而在 Monorepo 中可以一键启动多个子项目的调试、构建,从而提高研发效率。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2013.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2013.jpeg) - 其次,在 Monorepo 下可以很方便的共享代码,从而解决代码复用困难的痛点。 - Polyrepo 中,复用公用代码比较困难,需要为公用代码单独维护一个仓库,此外升级、调试流程也十分繁琐低效。 - 首先是调试很繁琐,公用模块的调试需要手动执行 link,与当前调试的项目关联起来,如果公用模块较多的话,link 步骤将非常繁琐低效。 @@ -94,35 +94,35 @@ tags: - 而在 Monorepo 中,可以直接一键创建公用模块,顶层的模块一键引入公用模块进行开发、调试,底层模块的更改能够直接被上层感知,甚至不需要经过 link 和 npm 发布,即可在本地调试或者部署平台发布,降低了很多重复的工作。 - 因此,在 Polyrepo 中复用代码比较困难,导致代码复用率比较低,而 Monorepo 中能够很方便的复用代码,抽离新的工具库的成本非常低,这使得大家更愿意做这类抽离工作,提高了代码复用率。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2014.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2014.jpeg) - 再次,在 Monorepo 里能够实现自动化的多项目工作流,从而解决工作流程割裂的痛点。 - 如果我的业务需求要涉及到多个项目,在 Polyrepo 模式中,我需要修改多个项目,对多个项目各自提交 commit,每个项目单独跑 CI 流程,如果项目间有依赖关系的话,还需要手动升级项目的依赖版本。例如需要修改图中的三个项目,需要先修改提交底层模块,跑每个模块的 CI 流程,在处理顶层模块时,还得更新底层依赖,接着再跑一次 CI 流程,这一套流程非常繁琐且不连续。 - 而在 Monorpeo 中,我们可以直接修改多个子项目,只需一次 commit 提交,多个子项目的 CI 和发布流程也是一次性的。从而将整个多项目的工作流自动化,连续化。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2015.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2015.jpeg) - 我们简单的做一个总结:在 Polyrepo 的模式下,每一个项目都有自己的地址、仓库,这会导致共享代码很困难,基建比较重复,同时工作流也比较割裂。 - 但是在 Monorepo 里面,我们可以将多个项目的工作流进行规范统一。比如说统一的 CI 流程、 code review 等等。同时,它的代码共享也比较方便,我们可以直接将多个项目共享的组件或者工具函数,抽离出来,便可以在多个项目间复用,当然,多个项目的基建也可以复用。 - 很多时候一个团队、或者一块业务,他们的项目之间并不是完全割裂的,而是相互联系的,Monorepo 可以很方便的将这些项目组织到一起,进行维护。当团队有多项目需求的时候,推荐使用 Monorepo。 - 当然,Monorepo 并不是银弹,Polyrepo 也能符合大部分的场景,我们选择 Monorepo 是基于我们遇到的痛点考虑和权衡的,使用 Monorepo 虽然很好地解决我们遇到的痛点,但是也给我们来了不少额外成本,接下来的部分我会着重分享,在采用 Monorepo 方案后,所带来的问题及其实践。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2016.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2016.jpeg) - 接下来,我们来看看引入 Monorepo 方案后遇到的问题,以及我们在解决这些问题方面的实践 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2017.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2017.jpeg) - 我们的自研方案在实践过程中,主要遇到如下四个方面的问题: - 第一个,因为大部分用户习惯于 Polyrepo 的开发,有些甚至是第一次使用 Monorepo,因此存在用户教育,上手门槛的问题 - 第二个是随着业务的迭代,Monorepo 工程的规模,即子应用数量迅速增多,而一次调试和构建往往涉及多个子应用,从而导致调试、构建性能下降 - 第三个是 Monorepo 工程的生命周期往往比 Ployrepo 长很多,且参与开发的同学也非常多,如何确保合入工程的代码保持良好的可维护性,也是一个挑战 - 第四个,字节的业务线众多,号称 App 工厂,那么面对这些不同业务的需求,如何提供丰富的扩展能力呢? -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2018.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2018.jpeg) - 此处简述我们自研方案的整体架构,主要分为三层,底层是开源工具层,即我们依赖的开源方案,有 npm 包管理器,changesets 版本管理工具,子应用集成工具,比如 jest 和 webpack 等; - 顶层是各类业务场景的支撑,有微前端、跨端、BFF、H5、Library 等场景; - 中间层是自研的解决方案,方案也包含三个层次,底层是各类基础能力的封装,比如缓存管理、任务管理、依赖管理、子项目管理等;中间是面向用户的功能模块封装,比如 Checker、Builder、Pipeline 等;顶层则是命令行工具,提供各种命令用于调用 Monorepo 的能力; - 右侧则是自研方案提供的,基于插件的扩展能力。 - 架构设计不是本次分享的重点,接下来才是我们的重点,重点分享这套自研方案,在面对上述四个问题时,它是如何实践的。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2019.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2019.jpeg) - 因为相当多的用户对 Monorepo 项目的开发方式了解不足,习惯于 Polyrepo 的开发,甚至此前都没有接触过 Monorepo 项目的开发。 - 因此在加强用户教育,降低上手门槛方面,我们紧密贴合字节基础设施,实践有 - 提供『内置脚手架』工具,使得用户能够一键创建字节常见的应用类型 @@ -130,83 +130,83 @@ tags: - 提供『可视化扩展』,通过界面教育和引导用户使用 - 接下来我们展开说说 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2020.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2020.jpeg) - 内置脚手架方面,我们提供了一种生成器的模版机制来应对子项目的初始化,我们集成字节常见应用类型的模版(Lynx、Gulu、Garfish、组件库/工具库等),此外也能实现自己的模版,模版既可以是项目级别的,也可以是组件级别的。 - 如图就是生成器的架构图,它由 5 个 stage 组成,比如 prompt 阶段用于收集用户的命令行输入,render 阶段是渲染模版文件阶段,emit 阶段是生成模版文件,然后通过插件机制,使得每个插件能够在上述 5 个 stage 中处理插件的逻辑,我们提供的插件有 Git 仓库创建插件、SCM 创建插件、Npm 依赖安装插件等。 - 基于生成器的设计,用户能够定制合适自己业务线的、开箱即用的模版,一键创建项目及其 Git 仓库、配置 SCM 构建等,降低了创建项目的成本。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2021.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2021.jpeg) - 接下来看看 CI/CD 能力方面的实践,如下是架构图,它也是通过插件的机制来实现的,比如 EntryPackagePlugin 用于入口应用探测,BuildDependenciesPlugin 用于构建依赖,RunSubPackageBuildShPlugin 用于执行子应用的 shell 脚本; - 为了实现多环境支持,它会对每一种场景都有对应的 Pipeline,比如 GitlabPipeline 和 SCMPipeline,可以简单的理解为,为每个场景维护了一份插件集合,然后在对应的场景执行时会调用对应场景的 Pipeline,每个 Pipeline 都会执行相同的 stage; - stage 列表如右边所示,其中 analyzeEntries stage 会分析依赖,得出哪些代码变更了,collect tasks stage 会去调度子项目的任务流水线。 - 基于插件集的定制,使得当前的 Pipeline 机制在 SCM 和 Gitlab CI 场景下开箱即用,也方便为未来新的场景进行扩展。 - 这套机制支持了 Gitlab CI 自动发版流水线与 SCM 构建流水线等场景,大大降低了应用接入发版平台和构建平台的成本。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2022.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2022.jpeg) - 可视化扩展方面,我们通过界面教育和引导用户,提供快捷操作创建子应用、调用各类命令、搜索子应用、配置 Monorepo 等; - 从而降低新手用户的上手成本、降低记住命令的心智负担。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2023.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2023.jpeg) - 因为 Monorepo 势必会让子应用迅速增多,而一次调试和构建往往涉及多个子应用,从而导致调试、构建性能下降,所以我们需要有特别的方案来保障调试和构建的性能。 - 因此在规模增大导致性能下降方面,我们通过多种方式提高构建效率,实践有 - 支持『任务并行能力』,采用最大限度的批量任务并行加速 - 支持『多级缓存能力』,对依赖安装、构建产物、测试结果等实现了多级缓存 - 支持『按需构建能力』,根据依赖图和代码更改的影响面来构建和测试 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2024.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2024.jpeg) - 接下来看看我们是如何实践任务并行能力的! - 如图所示,根据子项目的依赖关系转成一个任务依赖图,就可以根据这个依赖图进行任务的调度。 - 看左下角的任务依赖图,它的构建顺序必须符合这样一种要求,即上层的项目构建依赖于底层项目构建的完成。 - OK,我们看方式一,一种最朴素的方式,即通过串行排成 DEBCA,这是能够符合构建要求的;但是它的性能是比较低的,比如 D 和 E 是可以并行的,所以第二种方式就是通过把 DE 以及 BC 进行并行处理,这样便能把前面的 5 个步骤加速到 3 个步骤。但这种优化还不够极致,我们发现任务 C 呢,不依赖任务 E 的完成,但是方式二下,任务 C 却得等待 D 和 E 都完成才开始执行。 - 因此便引出第三种方式,在任务 E 完成后,D 和 C 便可以并行执行;考虑到并行执行涉及的子项目任务过多时,会导致 cpu 过载,因此还配套了并发控制能力,比如右侧的时序图,4 个需要执行的任务,最大的并发数为 2。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2025.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2025.jpeg) - 接下来看看多级缓存能力的实践,当 monorepo 体积增大以后,每一次开发或者上线都可能涉及到数个子项目,每次都需要对这些子应用进行重新构建,这将会极大地拖慢构建和部署时间。 - 我们提供了对构建产物进行缓存的能力,能够同时将产物缓存到本地和远程,当相关的子项目没有修改过代码时,将会复用之前的构建产物以减少构建时间。此外,除了构建任务,其它的操作,比如单测任务的执行,也会缓存结果,从而跳过单测任务。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2026.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2026.jpeg) - 按需构建能力方面的实践,我们支持按影响面执行 CI 流程,通过 diff 更改的代码并进行依赖分析,得到整个 Monorepo 中受影响的项目,并执行其 CI 流水线;否则每次 CI 都会完整的构建整个所有的子项目 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2027.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2027.jpeg) - 此处以一个简单的 Monorepo 为例,依赖关系如图所示,三个 App,一个 Component,一个 Sdk 和工具库 1. 如果一次性全量构建所有应用,耗时约为 17.72秒 2. 如果仅仅改动了 component 模块,那么按需构建的话,我只需要构建 component, app1, app2,此时耗时 8.94秒,节约 50% 的时间 3. 再来看看无缓存的情况下,我仅仅构建 App1, App2, App3 它们的耗时在 10.77秒到16.94秒之间 4. 如果有缓存的话,比如 component, sdk, util 已经构建过,那么再单独构建 App1, App2, App3 时,构建耗时在 7.55秒到9.74秒 之间,大约节约 45% 的时间 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2028.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2028.jpeg) - 因为 Monorepo 会让众多水平不一工程师在同一个仓库里开发,而且仓库每周新增的代码量都是巨大的,所以我们更需要去保障代码质量。 - 因此在代码防劣化方面,我们通过 Checker 机制进行各类规范的检查,实践有 - 支持『内置多种 Checker』,确保项目基本的可维护性 - 支持『自定义 Checker』,使用户能够编写基于各自团队的规范检查 - 支持『支持自动修复』,自动修复不符合规范的代码或配置 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2029.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2029.jpeg) - 我们内置了多种 Checker,用于保证基本的可维护性,比如统一依赖版本,它会检查各项目的 package.json 下相同依赖版本是否一致等等 - 此处以统一依赖版本一致为例,我们都知道,如果多个项目使用相同依赖的不同版本容易导致问题,比如 components 和 app 的 react 版本不一致,就会有 react hooks 报错等问题,此时应用这里的第一个 Checker,就能检测这个问题,并自动修复为统一的版本。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2030.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2030.jpeg) - 如果内置的 Checker 不满足用户对于规范的需求呢?我们也提供了自定义 Checker 的能力,在每个 Checker 内部,我们注入当前 Monorepo 工程的配置和依赖图等上下文信息,Checker 开发者便能通过这些信息去 check 当前的工程是否符合规范。 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2031.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2031.jpeg) - 当我们探测出问题后,其实有些不规范是能通过自动修复解决的,就像 eslint 一样,我们也支持自动修复代码 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2032.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2032.jpeg) - 因为字节的业务线众多,号称 App 工厂,所以各个业务对 Monorepo 有这样或那样的扩展需求。 - 因此在面对这些不同业务的扩展需求方面,我们通过插件机制支持用户自定义命令,Checker,生成器,并暴露整个生命周期的钩子,以提供更灵活的扩展能力 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2033.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2033.jpeg) - 以上就是自研方案在实践过程中遇到的一些问题,接下来简要说说整体的落地情况 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2034.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2034.jpeg) - 在字节前端领域,我们的自研方案在所有的 Monorepo 工程中 - 仓库占比达 25%,采用自研方案的仓库绝对数量已经有上千个 - 月活占比 47%,拍平后的子应用总数接近多仓项目数量 - 每周的 npm 包下载量达 14万+ - 在字节跳动的前端领域,无论从仓库占比,月活占比,以及下载量来看,我们的自研方案都是排名第一的 Monorepo 工具 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2035.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2035.jpeg) -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2036.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2036.jpeg) - 过去通过自研方案的建设,拥有高效率、可扩展、开箱即用的 Monorepo 解决方案,良好地服务了字节跳动上千个 Monorepo 工程,颇受业务好评。当前的 Monorepo 方案,面临几个核心问题: - 第一个,本地任务调度性能瓶颈,巨型 Monorepo 仓库开始出现依赖安装和构建速度问题,需要更极致的性能和体验优化 - 第二个,缺乏缓存管理能力,如何跟踪和解决缓存命中率偏低问题 @@ -216,7 +216,7 @@ tags: - 针对缓存,我们将会开发缓存 sdk,提供缓存的管理和复用能力,跟踪和提高缓存命中率 - 针对依赖管理,我们将提供开发辅助工具,用可视化的方式管理复杂的依赖关系 -![](./post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2037.jpeg) +![](https://quanru-github-io.pages.dev/post-img/前端%20Monorepo%20解决方案在字节跳动的实践%2037.jpeg) ## PPT 附件 diff --git "a/source/_posts/\345\255\227\350\212\202\350\267\263\345\212\250\347\232\204\345\211\215\347\253\257\345\267\245\347\250\213\345\214\226\345\256\236\350\267\265.md" "b/source/_posts/\345\255\227\350\212\202\350\267\263\345\212\250\347\232\204\345\211\215\347\253\257\345\267\245\347\250\213\345\214\226\345\256\236\350\267\265.md" index cf7b3b99..bddca1b6 100644 --- "a/source/_posts/\345\255\227\350\212\202\350\267\263\345\212\250\347\232\204\345\211\215\347\253\257\345\267\245\347\250\213\345\214\226\345\256\236\350\267\265.md" +++ "b/source/_posts/\345\255\227\350\212\202\350\267\263\345\212\250\347\232\204\345\211\215\347\253\257\345\267\245\347\250\213\345\214\226\345\256\236\350\267\265.md" @@ -22,20 +22,20 @@ tags: ## 本主题简介 -![](./post-img/wot-海报1.jpg) -![](./post-img/wot-海报2.jpg) +![](https://quanru-github-io.pages.dev/post-img/wot-海报1.jpg) +![](https://quanru-github-io.pages.dev/post-img/wot-海报2.jpg) ## PPT 正文 -![](./post-img/字节跳动的前端工程化实践.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践.jpeg) -![](./post-img/字节跳动的前端工程化实践%202.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%202.jpeg) - 大家早上好,我叫林宜丙,今天带来的分享主题是『字节跳动的前端工程化实践』。 -![](./post-img/字节跳动的前端工程化实践%203.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%203.jpeg) - 简单介绍下我自己,我是一名来 Web Infra 部门的前端架构工程师,拥有多年前端工程化的经验,致力于帮助前端工程师更好地管理和治理工程;目前负责工程治理方向的方案设计和落地工作。 -![](./post-img/字节跳动的前端工程化实践%204.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%204.jpeg) - 今天的分享包括以下四个部分 - 首先分析『当下前端开发领域的趋势』,并引出字节跳动前端开发所面临的新挑战; - 其次分享我们目前通过哪些实践来应对这些新挑战; @@ -43,31 +43,31 @@ tags: - 最后总结和展望前端工程化的发展规律; - OK,在分享之前,我们先来看看『什么是前端工程化?』 -![](./post-img/字节跳动的前端工程化实践%205.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%205.jpeg) - 所谓前端工程化,就是在前端开发过程中,采用一系列的技术手段和工具,来提高开发效率、保证代码质量、提高代码复用性、实现自动化流程和促进团队协作等方面的目标,是现代前端开发不可或缺的一部分; - 在这里呢我要特别说明一下,本次主题所要分享的并不是大而全的前端工程化,而是分享为了应对当前『新趋势』下的『新挑战』,我们所做的『新实践』,接下来一起看看,前端开发出现了哪些新趋势呢? -![](./post-img/字节跳动的前端工程化实践%206.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%206.jpeg) - 首先是前端工种的趋势 - 第一个趋势是涉及的平台越来越多,Web,Node,客户端和跨平台等 - 第二个趋势是所能支撑的业务也越来越多,复杂度越来越大,特别是近年来前端侧涌现出不少重前端交互的应用 - 第三个的话,是随着上述两个趋势而来地、不可避免地使得前端团队的规模增大 -![](./post-img/字节跳动的前端工程化实践%207.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%207.jpeg) - 上述三个趋势又客观上造成了前端工程的四个趋势,也就是: - 第一个是,代码规模增大,内部已经出现代码量超过 10G 的大型工程,同时一个应用的上下游依赖所涉及的项目数量也是非常的多 - 第二个,维护人数增多,一个工程少则十来人,多则四五十人 - 第三个,应用体积增加,随着应用功能的迭代,体积越来越庞大 - 第四个,治理难度增高,复杂的依赖关系难以治理,复杂的构建产物难以诊断 -![](./post-img/字节跳动的前端工程化实践%208.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%208.jpeg) - 那么,在上述这些趋势下,我们的前端开发面临了哪些新的挑战呢?主要有四个: - 第一个是多项目维护成本比较高,项目基建重复、代码复用困难、工作流程割裂等 - 第二个是多人开发协作成本比较高,相互依赖的流程、级联的依赖升级,都增加了协作成本 - 第三个是巨型应用构建速度比较慢,构建耗时随着应用增大而变慢 - 第四个是大型应用劣化速度比较快,我们缺乏有效的防劣化手段 -![](./post-img/字节跳动的前端工程化实践%209.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%209.jpeg) - 为了解决上述新挑战,我们投资如下工具来应对 - 第一个是自研 Monorepo 工具,用于降低多项目的维护成本 - 第二个是对原有的微前端框架进行升级,进一步降低多人开发的协作成本 @@ -75,124 +75,124 @@ tags: - 第四个是提供诊断工具,来有效地防止应用劣化 - 接下来将从这四个主题展开聊聊,为了达到上述目标,我们是如何实践的 -![](./post-img/字节跳动的前端工程化实践%2010.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2010.jpeg) - 什么是 Monorepo? - 它是一种源代码管理的模式,其形式就是将多个项目集中到一个仓库中管理; - 与之相对的是 Polyrepo 模式,这种模式下各个项目都有独立的仓库; - 简而言之,Monorepo 就是将多个不同的项目以良好地组织关系,放到单个仓库中维护; -![](./post-img/字节跳动的前端工程化实践%2011.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2011.jpeg) - 那么 Monorepo 是如何降低多项目的维护成本的?他通过 - 复用基建,让开发人员重新专注于应用本身 - 代码共享,能够低成本的做到代码复用 - 原子提交,使用自动化的多项目工作流 -![](./post-img/字节跳动的前端工程化实践%2012.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2012.jpeg) - 首先是通过复用基建,让开发人员重新专注于应用本身: - 在传统的 Polyrepo 模式下,每个项目都需要有开发人员创建和维护;而在 Monorepo 中,只需要一两个开发人员负责建立 Monorepo 工程,所有的项目都能够在一个仓库中统一维护,通过复用一套基建(比如 CI 配置、Lint 规则、构建脚本等),从而降低多项目维护成本 - 此外,复用基建也使得统一改造和升级基建变得方便,比如想在 CI 流程中为所有项目加入类型检查,来提高下项目的质量,在 Polyrepo 下需要修改每一个项目,这样成本其实是很高的。而在 Monorepo 下,基建的调整和维护,能够很容易地应用到多个项目中。 -![](./post-img/字节跳动的前端工程化实践%2013.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2013.jpeg) - 其次是通过代码共享,让开发人员能够低成本地复用代码: - Polyrepo 中,维护公用模块的成本比较高,首先是调试很繁琐,公用模块的调试需要手动执行 npm link,与当前调试的项目关联起来,如果公用模块较多的话,npm link 步骤将非常繁琐低效。 - 其次公用模块的升级很繁琐,需要手动管理这种依赖关系,先升级底层的模块,然后发布,最后再升级顶层模块,如果中途出错了,我还得再来一遍这个过程。 - 而在 Monorepo 中,可以直接一键创建公用模块,顶层的模块一键引入公用模块进行开发、调试,底层模块的更改能够直接被上层感知,甚至不需要经过 link 和 npm 发布,减少了很多重复的工作,大大降低了抽离新的『复用代码』的成本,这使得大家更愿意做这类抽离工作,这反过来又提高了代码复用率。 -![](./post-img/字节跳动的前端工程化实践%2014.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2014.jpeg) - 再次,是通过原子提交,让开发人员享受自动化的多项目工作流 - 如果我的业务需求要涉及到多个项目,在 Polyrepo 模式中,例如需要修改图中的三个项目,需要先修改提交底层模块,跑每个模块的 CI 流程,在处理顶层模块时,还得更新底层依赖,接着再跑一次 CI 流程,这一套流程非常繁琐且不连续。 - 而在 Monorpeo 中,我们可以直接一次性调整并提交多个项目,CI 和发布流程也都是一次搞定的,从而将多项目的工作流自动化。 -![](./post-img/字节跳动的前端工程化实践%2015.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2015.jpeg) - 我们简单的做一个总结: - 在 Polyrepo 的模式下,每一个项目都有各自的一套基建,且代码复用困难、工作流程割裂,但是在 Monorepo 里面,我们可以让多个项目复用一套基建;方便地共享代码,使用一致的工作流。 - 很多时候一个团队的项目往往不是割裂的,而是相互联系的,Monorepo 可以很方便地将这些项目组织到一个大仓库中维护,从而极大地降低了多项目的维护成本。 -![](./post-img/字节跳动的前端工程化实践%2016.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2016.jpeg) - 接下来分享下 bunlder 和 build stystem 的工程化实践,因为无论是单仓项目还是多仓项目,随着代码规模和子应用数量的增加,都会导致构建性能下降,为了应对这种情况,我们分别建设了 Bundler 和 Build System 工具。 - 其中 Bundler 是为了解决单个巨石应用构建速度慢的问题; - 而 Build System 则是为了解决 Monorepo 下构建速度慢的问题。 -![](./post-img/字节跳动的前端工程化实践%2017.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2017.jpeg) - 在前端领域,Bundler 是一种工具,用于将多个前端资源(例如 JS、CSS、图像等文件)打包到一个或多个文件中,从而让浏览器能够直接运行; - 常见的 Bundler 工具有 Webpack、Rollup、Vite、Parcel、Esbuild。 -![](./post-img/字节跳动的前端工程化实践%2018.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2018.jpeg) - 接下来介绍一下我们自研的 Rspack Bundler,它是一个基于 Rust 语言的高性能构建引擎, 具备与 Webpack 生态系统的互操作性。 - 从这句介绍中,我们可以知道,Rspack 有两个特性,一个是高性能,另一个是与 Webpack 生态的兼容性。 -![](./post-img/字节跳动的前端工程化实践%2019.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2019.jpeg) - 首先第一个特性,即采用 Rust 语言实现,由于 JS 是单线程的,虽然在 JS 中也有些方式做到并发,但这些方法都有种带着镣铐跳舞的感觉,而在 Rust 中我们能很好地支持并发特性,所以我们将构建过程中的任务,利用并发的特性去执行,这极大提升了构建性能; - 这两张图片是 Webpack 与 RSpack 在构建过程中的线程情况对比,我们可以明显地看出,Webpack 其实只是一个单线程,而 Rspack 则能充分发挥多核 CPU 的优势,极致地压榨出性能。 -![](./post-img/字节跳动的前端工程化实践%2020.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2020.jpeg) - 这是我们官网上的一个对比图,在相同的项目下,RSpack 只需要 4.2秒,而 Webpack 需要 34.8秒 -![](./post-img/字节跳动的前端工程化实践%2021.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2021.jpeg) - Rspack 第二个特性就是与 webpack 部分兼容,目前的实现可以理解为是一个 webpack 的子集,这套子集里包含了大部分的常用配置,满足了日常的业务开发,那么,为什么要与 webpack 生态兼容? - 首先,webpack 的插件机制满足了项目对定制化的要求 - 其次,复用 webpack 丰富的生态,相当于用最小的成本优化巨型项目的开发体验 - 最后,存量的 webpack 项目非常多,兼容 webpack 能大大降低迁移成本 -![](./post-img/字节跳动的前端工程化实践%2022.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2022.jpeg) - 这是两个典型业务上的收益,两个项目原本 dev 启动耗时5分钟左右,使用 Rspack 后只需 20 秒左右,原本 hmr 需要 20 秒左右,使用 Rspack 后只需要 1 秒左右,性能收益基本都在 10 倍左右 -![](./post-img/字节跳动的前端工程化实践%2023.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2023.jpeg) - 简单介绍下 Build System,它通过处理 Monorepo 下的项目依赖关系图,并根据这个关系图调度构建任务; - 为什么 Monorepo 下需要一个 Build System 呢? 因为 Monorepo 不是简单的将多个项目直接维护到单个仓库而已,它还需要借助 Build System 来管理整个代码仓库中的多个项目,并根据项目之间的依赖关系进行构建; - 常见的 Build System 工具有 Bazel、NX、Turborepo、Lage 等 -![](./post-img/字节跳动的前端工程化实践%2024.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2024.jpeg) - 接下来介绍下在我们自研的 Monorepo 工具中,如何实践 Build System,我们通过 - 支持『任务并行能力』,采用最大限度的并行任务加速 - 支持『多级缓存能力』,对构建任务实现了多级缓存 - 支持『按需构建能力』,根据代码更改的影响面来构建 -![](./post-img/字节跳动的前端工程化实践%2025.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2025.jpeg) - 如图所示,根据子项目的依赖关系转成一个任务依赖图,它的构建顺序必须符合这样一种要求,即上层的项目构建,需要等待底层项目构建的完成。 - OK,我们看方式一,即通过串行排成 DEBCA,这是能够符合构建要求的;但是它的性能是比较低的,比如 D 和 E 是可以并行的,所以第二种方式就是通过把 DE 以及 BC 进行并行处理,这样便能把前面的 5 个步骤加速到 3 个步骤。 - 此时我们发现任务 C 呢,它不依赖任务 D 的完成,但是方式二下,任务 C 却得等待 D 和 E 都完成才开始执行。 - 因此便引出第三种方式,在任务 E 完成后,D 和 C 以并行方式执行; -![](./post-img/字节跳动的前端工程化实践%2026.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2026.jpeg) - 当 monorepo 子项目规模增大后,每一次开发或者上线都会涉及多个子项目,如果每次都需要对这些子项目进行重新构建,这将会极大地拖慢构建和部署速度。 - 我们提供了对构建产物进行缓存的能力,能够同时将产物缓存到本地和远程,当相关的子项目没有修改过代码时,将会复用之前的构建产物以减少构建时间。 -![](./post-img/字节跳动的前端工程化实践%2027.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2027.jpeg) - 按需构建能力方面的实践,我们支持按影响面执行 CI 流程,通过 git diff 当前有改动的代码进行依赖分析,从而只构建受影响的项目,否则每次 CI 都会完整的构建所有的子项目。 -![](./post-img/字节跳动的前端工程化实践%2028.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2028.jpeg) - 此处以一个简单的 Monorepo 为例,依赖关系如图所示 1. 如果一次性全量构建所有项目,耗时约为 17.72秒 2. 如果仅仅改动了 component 模块,那么按需构建的话,我只需要构建 component, app1, app2,此时耗时 8.94秒,节约 50% 的时间 3. 再来看看无缓存的情况下,我仅仅构建 App1, App2, App3 它们的耗时在 10.77秒到16.94秒之间 4. 如果有缓存的话,比如 component, sdk, util 已经构建过,那么再单独构建 App1, App2, App3 时,构建耗时在 7.55秒到9.74秒之间,大约节约 45% 的时间 -![](./post-img/字节跳动的前端工程化实践%2029.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2029.jpeg) - 通过 Builder 和 Build System 的建设,我们分别借助 Rust 语言的高性能和远程构建缓存能力,极大地加速了巨型应用的构建速度,但这又不仅仅是开发速度上的提升,它同时给我们的业务带来两个巨大的收益 1. 拉高了一个巨石应用上限:使得我们能够开发一个更巨型更强大的应用 2. 加快迭代速度:使得我们可以更快更多地做 AB 测试和功能发布 -![](./post-img/字节跳动的前端工程化实践%2030.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2030.jpeg) - 所谓微前端,就是前端领域的应用分治解决方案,字节跳动的微前端实践也经过 iframe, spa, 框架阶段;在实践中遇到不少问题,为了进一步降低多人协作的成本,目前进入一个新方案的探索阶段。 -![](./post-img/字节跳动的前端工程化实践%2031.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2031.jpeg) - 接下来看下微前端的工程化实践,新方案是如何降低多人开发的协作成本的 - 首先,通过减轻基座负担,将基座应用与业务逻辑解耦 - 其次,采用细粒度的组合,在更细粒度的模块上独立开发、部署 - 最后,通过约定模块协议标准,我们搭建了模块中心,甚至可以结合低代码平台,从而带来更高的模块复用率;并支持模块级别的灰度和AB能力; -![](./post-img/字节跳动的前端工程化实践%2032.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2032.jpeg) - 它是如何减轻基座负担的呢?传统的微前端,通过建立一个公共的基座来承载公共逻辑,这种复用方式,除了工具库以外,往往导致不少业务逻辑耦合到基座中,这导致基座更频繁的改动和发布,更容易出现更大的故障影响面和更容易失效的缓存; - 这又让子应用从独立开发、部署重新回到某种程度上地相互依赖,因此,新方案通过两种方式消除了这类基座的存在,一个是消费机制,另一个是共享机制,前者一般用于复用业务逻辑,后者用于复用业务无关的工具库 -![](./post-img/字节跳动的前端工程化实践%2033.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2033.jpeg) - 那么,新方案的机制是如何起作用的? - 传统的微前端架构中,多个子应用之间是相对隔离的,往往都会通过一套沙箱机制来保证这种隔离性; - 但随着前端子应用的规模增大,人员增多,如此粗粒度的隔离又会反过来制约每个子应用内外的人员协作; - 因此,我们新的应对方式就是通过提供更细粒度的模块消费和共享方案,从而让开发人员能够以更小的单位进行独立开发、测试、部署 -![](./post-img/字节跳动的前端工程化实践%2034.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2034.jpeg) - 模块协议标准约定了模块的元信息,通过该协议在各个平台之间流转,来达到特定的功能 1. 比如构建平台会根据配置文件构建出这份协议文件 2. 比如部署平台会将元信息转成包含具体 cdn 地址的数据,直接回填到 html 里下发,这步操作也是能做到模块灰度和AB的前提 @@ -200,40 +200,40 @@ tags: - 有了细粒度组合和模块协议标准的特性,我们又能够十分方便地建立一个在线模块中心,无论是业务相关的组件,还是业务无关的工具库,都能以极低的成本在团队内部,甚至跨团队复用; - 此外,基于这套机制,我们还尝试了与低代码平台的配合,通过低代码平台搭建出符合模块协议标准的组件,并注册为在线模块后,能够极大的提高业务开发的效率。 -![](./post-img/字节跳动的前端工程化实践%2035.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2035.jpeg) - 这是一个典型的接入业务,接入之后我们发现从构建耗时、部署耗时,甚至迭代速度和需求吞吐量都有了显著的提升效果。 -![](./post-img/字节跳动的前端工程化实践%2036.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2036.jpeg) - 接下来分享下诊断工具,市面上的工具大多面向构建产物进行诊断和分析,无法对构建过程进行更深入诊断和分析,作用十分有限 -![](./post-img/字节跳动的前端工程化实践%2037.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2037.jpeg) - 那么我们自研的诊断工具,是如何有效地防止应用劣化的呢? - 首先,提供面向构建过程的分析能力,由于记录了构建过程的数据,我们会有更细粒度和更丰富的分析; - 其次,提供一套可扩展的规则机制,让不同的垂直场景和业务场景能够扩展自身的规则; - 最后,通过与核心研发流程结合,让规则真正发生作用; -![](./post-img/字节跳动的前端工程化实践%2038.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2038.jpeg) - 这是一份 statoscope 的分析结果页面,它是典型面向构建产物分析的,即通过消费 stats.json 来分析产物; - 它没有 loader、resolver、plugin 等和构建过程相关的分析和诊断; - 而我们的工具能做到如下一些能力: -![](./post-img/字节跳动的前端工程化实践%2039.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2039.jpeg) - webpack loader 时序分析 -![](./post-img/字节跳动的前端工程化实践%2040.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2040.jpeg) - webpack loader 分析 -![](./post-img/字节跳动的前端工程化实践%2041.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2041.jpeg) - webpack resolver 分析 -![](./post-img/字节跳动的前端工程化实践%2042.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2042.jpeg) - webpack plugin 分析 -![](./post-img/字节跳动的前端工程化实践%2043.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2043.jpeg) - bundle 深度分析 - 那我们是如何做到的呢?我们通过监听插件钩子和劫持 Loader 等方式介入构建过程,并收集和生成专门用于诊断和分析场景的数据结构,比如依赖图、模块图、三方包图、源码、loader、plugin、resolver 等数据,从而获得更完善的构建上下文信息,以便我们后续进行深度的诊断和分析 -![](./post-img/字节跳动的前端工程化实践%2044.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2044.jpeg) - 我们提供了一些默认的诊断规则,比如 1. 重复包检查 - Duplicate Packages 2. 包规范语句匹配 - Default Import Check @@ -243,22 +243,22 @@ tags: 2. 特定依赖的版本检查 3. 禁止使用特定的语句 -![](./post-img/字节跳动的前端工程化实践%2045.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2045.jpeg) - 光有上述两个能力还不够,我们还谋求与核心研发流程结合,在 CI 中支持基于分支的 diff 拦截,从而让规则真正发生作用 -![](./post-img/字节跳动的前端工程化实践%2046.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2046.jpeg) - 总结一下,这是一些典型的业务收益情况 -![](./post-img/字节跳动的前端工程化实践%2047.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2047.jpeg) - 这是目前上述几个工具的整体落地情况,其中 Monorepo 工具接入工程x个,微前端活跃用户y个,Bundler 开源工具 Rspack z stars,诊断工具的周下载量达到n次 -![](./post-img/字节跳动的前端工程化实践%2048.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2048.jpeg) - 接下来做一个简单的总结和展望: - 当上述一系列工具链支撑了更大的工程规模,更多的人员规模,更加快速的构建速度,更可维护的前端工程之后; - 我相信未来一定会催生出更『强大』的前端应用,当这个更强大的前端应用继续增大工程规模,增加团队人员,渐渐地拉低了构建速度和可维护性 - 那未来必将会对这些工具提出更高的要求,从而带动整个前端工具链再一次革新 -![](./post-img/字节跳动的前端工程化实践%2049.jpeg) +![](https://quanru-github-io.pages.dev/post-img/字节跳动的前端工程化实践%2049.jpeg) - 谢谢,这就是我今天带来的分享 ## PPT 附件 diff --git "a/source/_posts/\346\210\221\347\273\231\346\210\221\347\232\204 Obsidian \345\256\236\350\267\265\345\206\231\344\272\206\344\270\200\344\270\252\346\217\222\344\273\266.md" "b/source/_posts/\346\210\221\347\273\231\346\210\221\347\232\204 Obsidian \345\256\236\350\267\265\345\206\231\344\272\206\344\270\200\344\270\252\346\217\222\344\273\266.md" index 1e03ae58..a193fcba 100644 --- "a/source/_posts/\346\210\221\347\273\231\346\210\221\347\232\204 Obsidian \345\256\236\350\267\265\345\206\231\344\272\206\344\270\200\344\270\252\346\217\222\344\273\266.md" +++ "b/source/_posts/\346\210\221\347\273\231\346\210\221\347\232\204 Obsidian \345\256\236\350\267\265\345\206\231\344\272\206\344\270\200\344\270\252\346\217\222\344\273\266.md" @@ -182,7 +182,7 @@ ArchiveListByFolder 2. 关系图谱优化,即在 PARA 目录下支持 XXX.README.md 作为索引,否则所有的节点都将是 README 3. 单一事实来源,用户只需要设置元信息,插件负责读取 -![](./post-img/w28-1.png) -![](./post-img/w28-2.png) -![](./post-img/w28-3.png) -![](./post-img/w28-4.png) \ No newline at end of file +![](https://quanru-github-io.pages.dev/post-img/w28-1.png) +![](https://quanru-github-io.pages.dev/post-img/w28-2.png) +![](https://quanru-github-io.pages.dev/post-img/w28-3.png) +![](https://quanru-github-io.pages.dev/post-img/w28-4.png) \ No newline at end of file diff --git "a/source/_posts/\346\216\222\346\237\245 Node.js \346\234\215\345\212\241\345\206\205\345\255\230\346\263\204\346\274\217\357\274\214\346\262\241\346\203\263\345\210\260\347\253\237\346\230\257\345\256\203\357\274\237.md" "b/source/_posts/\346\216\222\346\237\245 Node.js \346\234\215\345\212\241\345\206\205\345\255\230\346\263\204\346\274\217\357\274\214\346\262\241\346\203\263\345\210\260\347\253\237\346\230\257\345\256\203\357\274\237.md" index 3df6d7f6..42ae136a 100644 --- "a/source/_posts/\346\216\222\346\237\245 Node.js \346\234\215\345\212\241\345\206\205\345\255\230\346\263\204\346\274\217\357\274\214\346\262\241\346\203\263\345\210\260\347\253\237\346\230\257\345\256\203\357\274\237.md" +++ "b/source/_posts/\346\216\222\346\237\245 Node.js \346\234\215\345\212\241\345\206\205\345\255\230\346\263\204\346\274\217\357\274\214\346\262\241\346\203\263\345\210\260\347\253\237\346\230\257\345\256\203\357\274\237.md" @@ -18,7 +18,7 @@ tags: 团队最近将两个项目迁移至 `degg 2.0` 中,两个项目均出现比较严重的内存泄漏问题,此处以本人维护的埋点服务为例进行排查。服务上线后内存增长如下图,其中红框为 `degg 2.0` 线上运行的时间窗口,在短短 36 小时内,内存已经增长到 50%,而平时内存稳定在 20%-30%,可知十之八九出现了内存泄漏。 -![泄漏](./post-img/node-leak1.jpg) +![泄漏](https://quanru-github-io.pages.dev/post-img/node-leak1.jpg) @@ -34,7 +34,7 @@ tags: 使用 [alinode](http://alinode.aliyun.com/) 获取堆快照,服务启动后,使用小流量预热一两分钟便记录第1份堆快照(2020-4-16-16:52),接着设置 qps 为 125 对服务进行施压,经过大约一个小时(2020-4-16-15:46)获取第2份堆快照。使用 Chrome dev 工具载入两份堆快照,如下图所示,发现服务仅短短运行一小时,其堆快照文件就增大了 45MB,而初始大小也不过 39.7MB;我们按 `Retained Size` 列进行排序,很快就发现了一个『嫌疑犯』,即 generator;该项占用了 55% 的大小,同时 `Shallow Size` 却为 0%,一项一项地展开,锁定到了图中高亮的这行,但是继续展开却提示 0%,线索突然断了。 -![快照](./post-img/node-leak2.jpg) +![快照](https://quanru-github-io.pages.dev/post-img/node-leak2.jpg) @@ -87,7 +87,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge 重复之前获取堆快照的步骤,惊奇地发现即使过了一天,内存也没有增长,而且 generator 也没有持有未释放的内存: -![验证](./post-img/node-leak3.jpg) +![验证](https://quanru-github-io.pages.dev/post-img/node-leak3.jpg) 至此,内存泄漏问题已经解决!那么如何避免遇到这个问题呢? @@ -169,7 +169,7 @@ run(); 经过二分排查,发现该泄漏问题从 `v11.0.0` 引入,在 [v12.16.0]( https://github.com/nodejs/node/pull/31691) 解决;内存泄漏版本执行脚本时,内存占用逐步递增直到 crash,而未泄漏版本则会及时回收内存。 -![对比](./post-img/node-leak4.jpg) +![对比](https://quanru-github-io.pages.dev/post-img/node-leak4.jpg) @@ -265,7 +265,7 @@ void PrototypeUsers::ScanForEmptySlots(WeakArrayList array) { 1. 内存回收效率低,导致执行完后,仍有相当大的内存占用; 2. 执行效率非常慢,`async/await` 版本仅需要 0.953 秒,而 generator 却需要 17.754 秒; -![对比](./post-img/node-leak5.jpg) +![对比](https://quanru-github-io.pages.dev/post-img/node-leak5.jpg) 这说明,相比 generator 语法,`async/await` 语法无论从执行效率还是内存占用方面都有压倒性优势。那么执行效率对比如何呢?上 `benchmark` 工具比划比划: @@ -347,7 +347,7 @@ generator 每秒执行了 516,178 次操作,而 `async/await` 每秒执行了 既然是 v8 的问题,那么 chrome 浏览器也是有这个问题的,打开空白标签页,执行前文给出的 `leak.js` 代码: -![chrome](./post-img/node-leak6.jpg) +![chrome](https://quanru-github-io.pages.dev/post-img/node-leak6.jpg) 可发现,chrome 下也会有内存泄漏问题,只不过 chrome 页面上的代码运行一般不会有密集地、重复地执行某段『导致内存泄漏』的代码,因此该问题在 chrome 端不容易被察觉。 diff --git "a/source/_posts/\346\216\222\346\237\245\345\256\210\345\200\231\345\234\250\351\233\266\347\202\271\344\270\244\345\210\206\347\232\204 bug.md" "b/source/_posts/\346\216\222\346\237\245\345\256\210\345\200\231\345\234\250\351\233\266\347\202\271\344\270\244\345\210\206\347\232\204 bug.md" index 737ffe93..42668a18 100644 --- "a/source/_posts/\346\216\222\346\237\245\345\256\210\345\200\231\345\234\250\351\233\266\347\202\271\344\270\244\345\210\206\347\232\204 bug.md" +++ "b/source/_posts/\346\216\222\346\237\245\345\256\210\345\200\231\345\234\250\351\233\266\347\202\271\344\270\244\345\210\206\347\232\204 bug.md" @@ -16,13 +16,13 @@ tags: 故事的开始是由老板的一个艾特开始的: -![](./post-img/2am1.jpg) +![](https://quanru-github-io.pages.dev/post-img/2am1.jpg) 当时排查了下没啥思路,就放弃了(以为偶现,过几天它能自己好起来!)。直到某一天我又收到了同样的告警,我回想了下最近好几天都有这个告警。 抬头一看: -![](./post-img/2am2.jpg) +![](https://quanru-github-io.pages.dev/post-img/2am2.jpg) 心里一惊:别搞出事故啊!就开始了我的排查之路。 @@ -48,7 +48,7 @@ tags: 所以一层一层往上找,是找不到抛错的源头的!猜测了一通无果,于是我去翻告警群的记录,我发现了一个惊人的规律,该告警只要出现,必定是每天凌晨 00:02: -![](./post-img/2am3.jpg) +![](https://quanru-github-io.pages.dev/post-img/2am3.jpg) 因此排查思路锁定在以下几个: @@ -63,7 +63,7 @@ tags: 经过多番搜索代码,发现了几处设置 redis 值的代码,同时结合 Google,有人指出 Redis 设置的超时时间不能为小于 0。经过本地验证,的确发现超时时间不能为 0: -![](./post-img/2am4.jpg) +![](https://quanru-github-io.pages.dev/post-img/2am4.jpg) 于是排查方向转为代码中哪里出现了设置超时时间小于 0 的逻辑。可疑代码如下: @@ -106,7 +106,7 @@ setRedisKey('abc', 'value', 0) 执行结果如下,与告警的错误栈一致: -![](./post-img/2am5.jpg) +![](https://quanru-github-io.pages.dev/post-img/2am5.jpg) 既然复现了错误,回头继续找 bug,深入到 `setRedisKey` 代码: diff --git "a/source/_posts/\346\266\210\345\244\261\347\232\2041px.md" "b/source/_posts/\346\266\210\345\244\261\347\232\2041px.md" index af3fac65..13b72a0a 100644 --- "a/source/_posts/\346\266\210\345\244\261\347\232\2041px.md" +++ "b/source/_posts/\346\266\210\345\244\261\347\232\2041px.md" @@ -18,7 +18,7 @@ tags: CSS 在[LayoutUnit](http://trac.webkit.org/wiki/LayoutUnit)中提到了两种将亚像素(即小数点像素)转换为真实物理像素的两种方法,示意图如下: -![WebKitlayouttypes](./post-img/WebKitlayouttypes.png) +![WebKitlayouttypes](https://quanru-github-io.pages.dev/post-img/WebKitlayouttypes.png) #### enclosingIntRect @@ -72,7 +72,7 @@ tags: CSS 显示效果如下: -![iPhone4](./post-img/ip4.png) +![iPhone4](https://quanru-github-io.pages.dev/post-img/ip4.png) 其计算规则如下: diff --git "a/source/_posts/\347\273\231\344\275\240\345\277\203\347\210\261\347\232\204 npm \345\214\205\344\270\212\344\270\252\345\214\227\344\272\254\346\210\267\345\217\243.md" "b/source/_posts/\347\273\231\344\275\240\345\277\203\347\210\261\347\232\204 npm \345\214\205\344\270\212\344\270\252\345\214\227\344\272\254\346\210\267\345\217\243.md" index af64ff81..bd69ebce 100644 --- "a/source/_posts/\347\273\231\344\275\240\345\277\203\347\210\261\347\232\204 npm \345\214\205\344\270\212\344\270\252\345\214\227\344\272\254\346\210\267\345\217\243.md" +++ "b/source/_posts/\347\273\231\344\275\240\345\277\203\347\210\261\347\232\204 npm \345\214\205\344\270\212\344\270\252\345\214\227\344\272\254\346\210\267\345\217\243.md" @@ -46,15 +46,15 @@ tags: 有的,有的!回忆下你使用 `npm view` 查询一个 npm 包时的情形: -![](./post-img/npm-sync1.jpg) +![](https://quanru-github-io.pages.dev/post-img/npm-sync1.jpg) 聪明的你肯定发现 `.tarball: https://registry.npmjs.org/koa/-/koa-2.13.0.tgz`,这个包正是当前最新版本 2.13.0 的 koa 包。下载解压后发现这正是我们要的,只要进入该目录执行 npm publish 即可: -![](./post-img/npm-sync2.jpg) +![](https://quanru-github-io.pages.dev/post-img/npm-sync2.jpg) 那我们能获取指定版本的 tgz 包吗?当然,使用 `npm view koa@1.0.0` 即可查看指定版本。等等,我没法知道当前 koa 包有哪些版本诶?别慌,试试这个命令 `npm view koa --json`,答案就藏在 `versions` 字段里: -![](./post-img/npm-sync3.jpg) +![](https://quanru-github-io.pages.dev/post-img/npm-sync3.jpg) 此外 `dist-tags` 字段也需要同步( *默认不同步 tag,这有可能导致杭州源的 tag 覆盖了北京源的 tag,而杭州源的 tag 可能不是最新的*)。 diff --git "a/source/_posts/\350\256\260\344\270\200\346\254\241\351\232\276\345\277\230\347\232\204\345\211\215\345\220\216\347\253\257\345\210\206\347\246\273.md" "b/source/_posts/\350\256\260\344\270\200\346\254\241\351\232\276\345\277\230\347\232\204\345\211\215\345\220\216\347\253\257\345\210\206\347\246\273.md" index 5dfbb141..d81eb651 100644 --- "a/source/_posts/\350\256\260\344\270\200\346\254\241\351\232\276\345\277\230\347\232\204\345\211\215\345\220\216\347\253\257\345\210\206\347\246\273.md" +++ "b/source/_posts/\350\256\260\344\270\200\346\254\241\351\232\276\345\277\230\347\232\204\345\211\215\345\220\216\347\253\257\345\210\206\347\246\273.md" @@ -14,7 +14,7 @@ tags: -![image](./post-img/用户中心重构.png) +![image](https://quanru-github-io.pages.dev/post-img/用户中心重构.png) ## 可维护 diff --git "a/source/_posts/\350\256\272\345\246\202\344\275\225\347\247\221\345\255\246\345\234\260\346\262\273\347\220\206\350\200\201\351\241\271\347\233\256\347\232\204 eslint.md" "b/source/_posts/\350\256\272\345\246\202\344\275\225\347\247\221\345\255\246\345\234\260\346\262\273\347\220\206\350\200\201\351\241\271\347\233\256\347\232\204 eslint.md" index 830fb7a7..71fcaed2 100644 --- "a/source/_posts/\350\256\272\345\246\202\344\275\225\347\247\221\345\255\246\345\234\260\346\262\273\347\220\206\350\200\201\351\241\271\347\233\256\347\232\204 eslint.md" +++ "b/source/_posts/\350\256\272\345\246\202\344\275\225\347\247\221\345\255\246\345\234\260\346\262\273\347\220\206\350\200\201\351\241\271\347\233\256\347\232\204 eslint.md" @@ -26,7 +26,7 @@ tags: 当前存在的 [eslint](https://www.npmjs.com/package/eslint) 错误数和告警数: -![](./post-img/eslint1.png) +![](https://quanru-github-io.pages.dev/post-img/eslint1.png) 其中 5697 个错误和 65 个警告可被自动修复,修复后仍剩余 6468 个。经发现,原有项目使用 Airbnb 等规则集,由于该规则十分严格,且本身没有可靠手段阻止不符合规范的代码入库,使得开发更不愿意遵守,错误数有越来越多的趋势,即使自动修复,也仍有 6000+ 的错误。因此选择和制定一个大家都能良好遵守的规则集就很重要,切忌进入『越严格越牛逼』的误区,时刻记住我们的目的是保证代码符合一定的规范,从而避免低级错误。 @@ -59,13 +59,13 @@ eslint 默认的结果仅支持文件维度的统计,这在错误数非常多 ### 接入后未修复的 eslint 结果 -![](./post-img/eslint2.png) +![](https://quanru-github-io.pages.dev/post-img/eslint2.png) 10959 个错误和警告 ### 接入并修复的 eslint 结果 -![](./post-img/eslint3.jpg) +![](https://quanru-github-io.pages.dev/post-img/eslint3.jpg) 837 个错误和警告