Copy disabled (too large)
Download .txt
Showing preview only (13,740K chars total). Download the full file to get everything.
Repository: ChanceYu/front-end-rss
Branch: master
Commit: 0c0f6de45d5b
Files: 112
Total size: 11.5 MB
Directory structure:
gitextract_01fb6mri/
├── .github/
│ └── workflows/
│ └── server.yml
├── .gitignore
├── README.md
├── TAGS.md
├── article-to-md/
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ └── src/
│ ├── check-cloud.js
│ ├── images.js
│ ├── index.js
│ ├── once.js
│ ├── processor.js
│ ├── rules/
│ │ ├── fenghen.js
│ │ ├── index.js
│ │ ├── javascriptweekly.js
│ │ ├── nodeweekly.js
│ │ ├── ruanyifeng.js
│ │ ├── weixin.js
│ │ └── zhangxinxu.js
│ ├── server.js
│ ├── stealth.js
│ ├── upload.js
│ └── utils.js
├── data/
│ ├── atom.xml
│ ├── deleted.json
│ ├── hotwords.json
│ ├── links.json
│ ├── processed.json
│ ├── rss.json
│ └── tags.json
├── details/
│ ├── JavaScript-Weekly.md
│ ├── Node-Weekly.md
│ ├── Nodejs技术栈.md
│ ├── iCSS前端趣闻.md
│ ├── tags/
│ │ ├── ai.md
│ │ ├── audio-video.md
│ │ ├── browser.md
│ │ ├── canvas-image.md
│ │ ├── css.md
│ │ ├── dev-desktop.md
│ │ ├── dev-game.md
│ │ ├── dev-mobile.md
│ │ ├── front-end-advanced.md
│ │ ├── git-svn.md
│ │ ├── html.md
│ │ ├── javascript.md
│ │ ├── job-interview.md
│ │ ├── miniprogram.md
│ │ ├── nodejs.md
│ │ ├── optimization.md
│ │ ├── other.md
│ │ ├── pack-build.md
│ │ ├── react.md
│ │ ├── server.md
│ │ ├── typescript.md
│ │ └── vue.md
│ ├── 凹凸实验室.md
│ ├── 前端之巅.md
│ ├── 前端从进阶到入院.md
│ ├── 前端侦探.md
│ ├── 前端大全.md
│ ├── 前端技术优选.md
│ ├── 前端早读课.md
│ ├── 前端精读评论.md
│ ├── 字节前端-ByteFE.md
│ ├── 张鑫旭-鑫空间-鑫生活.md
│ ├── 淘系前端团队.md
│ ├── 程序员成长指北.md
│ ├── 阮一峰的网络日志.md
│ └── 风痕·術&思.md
├── server/
│ ├── app.js
│ ├── dedupe-links.js
│ ├── feed.js
│ ├── fetch.js
│ ├── once.js
│ ├── package.json
│ ├── update.js
│ ├── utils.js
│ └── writemd.js
├── site/
│ ├── .babelrc
│ ├── .editorconfig
│ ├── .eslintignore
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .npmrc
│ ├── .postcssrc.js
│ ├── README.md
│ ├── build/
│ │ ├── build.js
│ │ ├── check-versions.js
│ │ ├── createFiles.js
│ │ ├── data.js
│ │ ├── template-parameters.js
│ │ ├── upload.js
│ │ ├── utils.js
│ │ ├── vue-loader.conf.js
│ │ ├── webpack.base.conf.js
│ │ ├── webpack.dev.conf.js
│ │ └── webpack.prod.conf.js
│ ├── config/
│ │ ├── dev.env.js
│ │ ├── index.js
│ │ └── prod.env.js
│ ├── index.html
│ ├── package.json
│ ├── src/
│ │ ├── App.vue
│ │ ├── components/
│ │ │ ├── Index.vue
│ │ │ └── MarkdownViewer.vue
│ │ ├── main.js
│ │ └── router/
│ │ └── index.js
│ └── static/
│ └── .gitkeep
└── templates/
├── DETAILS.md
├── README.md
└── TAGS.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/server.yml
================================================
name: RSS Runner
run-name: 'Runner #${{ github.run_number }} ${{ inputs.suffix }}'
on:
workflow_dispatch:
inputs:
suffix:
description: 'Input suffix'
required: false
type: string
schedule:
- cron: '0 */2 * * *'
jobs:
build:
if: github.repository == 'ChanceYu/front-end-rss'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
version: 9
run_install: false
- name: Use node.js
uses: actions/setup-node@v4
with:
node-version: 24.x
cache: pnpm
cache-dependency-path: server/pnpm-lock.yaml
- name: Get pnpm cache directory
id: pnpm-store-path
shell: bash
run: echo "dir=$(pnpm store path)" >> ${GITHUB_OUTPUT}
- name: Cache modules
id: pnpm-cache
uses: actions/cache@v3
with:
path: |
${{ steps.pnpm-store-path.outputs.dir }}
~/.cache/ms-playwright
key: ${{ runner.os }}-pnpm-20260303-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-20260303-
- name: Install and run server
working-directory: ./server
env:
WORKFLOW: true
RSS_CONFIG: ${{ secrets.RSS_CONFIG }}
run: |
pnpm install
pnpm run once
- name: Process new articles to Markdown
continue-on-error: true
working-directory: ./article-to-md
env:
QINIU_ACCESS_KEY: ${{ secrets.QINIU_ACCESS_KEY }}
QINIU_SECRET_KEY: ${{ secrets.QINIU_SECRET_KEY }}
run: |
if [ -f "../server/node_modules/new-articles.json" ]; then
pnpm install
pnpm exec playwright install chromium
pnpm run once
else
echo "No new articles to process, skipping"
fi
- name: Commit all changed files
id: auto-commit-action
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: update by action runner https://github.com/ChanceYu/front-end-rss/actions/runs/${{ github.run_id }}
- name: Publish site
if: steps.auto-commit-action.outputs.changes_detected == 'true'
working-directory: ./site
run: |
pnpm install
pnpm run build
pnpm add -g surge
surge ./dist front-end-rss.surge.sh --token ${{ secrets.SURGE_TOKEN }}
================================================
FILE: .gitignore
================================================
.DS_Store
.AppleDouble
.LSOverride
.svn
._*
.Spotlight-V100
.Trashes
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
.idea
*.sublime-*
.vscode
node_modules
*.js.map
.vercel
dist
server/.env
data/articles
================================================
FILE: README.md
================================================
<div align="center"><img width="100" src="/assets/rss.gif" /><h1>Front-End RSS</h1>
每天定时更新前端技术文章,并推送到 GitHub 方便查看
</div>
##
在线浏览:[https://fed.chanceyu.com](https://fed.chanceyu.com)
订阅地址:[https://fed.chanceyu.com/atom.xml](https://fed.chanceyu.com/atom.xml)
备用地址:[https://front-end-rss.surge.sh](https://front-end-rss.surge.sh)
##
[](https://github.com/ChanceYu/front-end-rss/actions/workflows/server.yml)
:alarm_clock: 更新时间: 2026-04-04 00:27:55,:rocket: 更新条数: +4,  表示有更新,[文章分类](/TAGS.md)
## 文章来源
- [Node-Weekly](#node-weekly)
- [JavaScript-Weekly](#javascript-weekly)
- [前端早读课](#前端早读课)
- [前端大全](#前端大全)
- [前端之巅](#前端之巅)
- [前端技术优选](#前端技术优选)
- [程序员成长指北](#程序员成长指北)
- [iCSS前端趣闻](#icss前端趣闻)
- [字节前端-ByteFE](#字节前端-bytefe)
- [前端精读评论](#前端精读评论)
- [前端从进阶到入院](#前端从进阶到入院)
- [前端侦探](#前端侦探)
- [淘系前端团队](#淘系前端团队)
- [张鑫旭-鑫空间-鑫生活](#张鑫旭-鑫空间-鑫生活)
- [阮一峰的网络日志](#阮一峰的网络日志)
- [凹凸实验室](#凹凸实验室)
- [风痕·術&思](#风痕·術&思)
- [Nodejs技术栈](#nodejs技术栈)
## 文章链接
<details open>
<summary id="node-weekly">
Node-Weekly
</summary>
- [2026-04-02-Node.js-25.9-brings---max-heap-size-and-better,-iterable-streams](https://nodeweekly.com/issues/618) [📖](https://fed.chanceyu.com?id=a0092eef8f955b266308b261ede471c7)
- [2026-03-26-How-TypeScript-6.0-affects-Node-developers](https://nodeweekly.com/issues/617) [📖](https://fed.chanceyu.com?id=e55606bacdcabcc0e704fb0b287afa99)
- [2026-03-19-Petition-calls-for-ban-on-AI-generated-code-in-Node.js-core](https://nodeweekly.com/issues/616) [📖](https://fed.chanceyu.com?id=8304d5c228ab03ce9e37e82e6708c689)
- [2026-03-12-Can-we-all-code-Node-like-Matteo-Collina?](https://nodeweekly.com/issues/615) [📖](https://fed.chanceyu.com?id=4fd3d273122c5dd50833d091b8072ee0)
- [2026-03-05-Node-25.8-adds-permission-model-audit-mode](https://nodeweekly.com/issues/614) [📖](https://fed.chanceyu.com?id=5f5c57319a5bfc69c25d56c851546cd3)
- [2026-02-26-AdonisJS-v7-brings-batteries-included-framework-upgrades](https://nodeweekly.com/issues/613) [📖](https://fed.chanceyu.com?id=ccf8b8c331b88f29e0df7da443c3d11d)
- [2026-02-19-Halving-Node.js-memory-usage-with-pointer-compression](https://nodeweekly.com/issues/612) [📖](https://fed.chanceyu.com?id=10534a1adb1cc6c825d744c1149f1bec)
- [2026-02-12-An-experimental-Node-environment-in-the-browser](https://nodeweekly.com/issues/611) [📖](https://fed.chanceyu.com?id=1034a7cc15f756440e7efaa7dc25f9e5)
- [2026-02-05-Hono-tops-developer-satisfaction,-but-Express-still-leads](https://nodeweekly.com/issues/610) [📖](https://fed.chanceyu.com?id=afbf582d974800a80368a662a4ce1ffe)
- [2026-01-29-A-smoother-way-to-ship-Node-apps](https://nodeweekly.com/issues/609) [📖](https://fed.chanceyu.com?id=ce1264f78ebed42fb0cb07f68938612d)
- [查看更多 >](/details/Node-Weekly.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="javascript-weekly">
JavaScript-Weekly
</summary>
- [2026-03-31-A-new,-major-npm-supply-chain-attack-via-Axios](https://javascriptweekly.com/issues/779) [📖](https://fed.chanceyu.com?id=a38f983bf5b0be6c9bef393b01c22eca)
- [2026-03-24-TypeScript-6.0,-Next.js-16.2,-and-a-new-Node.js-runtime](https://javascriptweekly.com/issues/778) [📖](https://fed.chanceyu.com?id=d0951a7d0cbc423bd09580c6dc93539d)
- [2026-03-17-It’s-about-time:-Temporal-advances,-Vite-accelerates](https://javascriptweekly.com/issues/777) [📖](https://fed.chanceyu.com?id=faf433affe522ee3f47ca53649f0423a)
- [2026-03-10-TypeScript-6.0-RC-and-Solid-2.0-beta-arrive](https://javascriptweekly.com/issues/776) [📖](https://fed.chanceyu.com?id=7b61aa9e6536635f9f97e85864c97d2c)
- [2026-03-03-External-import-maps,-a-big-Bun-release,-and-Node.js-schedule-changes](https://javascriptweekly.com/issues/775) [📖](https://fed.chanceyu.com?id=dbbe04ca63486bcfca913832c3969f09)
- [2026-02-24-Oxfmt-beta:-30x-faster-than-Prettier,-100%-compatible](https://javascriptweekly.com/issues/774) [📖](https://fed.chanceyu.com?id=050fad08bd23e55077593ec5761eac91)
- [2026-02-17-Electrobun-v1:-Bun-powered-desktop-apps-in-12MB-bundles](https://javascriptweekly.com/issues/773) [📖](https://fed.chanceyu.com?id=6b724b897f76457956320ded9db76b8a)
- [2026-02-10-A-fast,-modern-way-to-browse-and-compare-npm-packages](https://javascriptweekly.com/issues/772) [📖](https://fed.chanceyu.com?id=0540ace4956454d4be0f78a28d73b708)
- [2026-02-03-Babel-8-RC-Arrives,-Gatsby-Lives,-Lodash-Resets](https://javascriptweekly.com/issues/771) [📖](https://fed.chanceyu.com?id=6b807c8dbdb15b9f73f44d022f2b72df)
- [2026-01-27-What's-next-for-JavaScript-frameworks-in-2026](https://javascriptweekly.com/issues/770) [📖](https://fed.chanceyu.com?id=1918512e52091d5c3c5fcee21b28ceba)
- [查看更多 >](/details/JavaScript-Weekly.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="前端早读课">
前端早读课
</summary>
- [2026-04-03-【早说】Ghostty创始人的AI之旅](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278865&idx=1&sn=72b0e18b749cbe4817d6f7b96c6ea7ed&chksm=bc0ba13f35cf0cd8f410ef892d3490d0c93ca1c3c35d2469057a5c6b627953e4a637451bd6f5&scene=0#rd) [📖](https://fed.chanceyu.com?id=a293f09b5b10f96424be936e261e049a)
- [2026-04-02-【早说】AI指数级进化时代的产品管理](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278860&idx=1&sn=54fc7b48572bb519431e0f10a96445c9&chksm=bcb6d6dbe3d7fe42cd6a8ffb84b072ab7ce5b5787129441ac8e8f430178090eed732c782546d&scene=0#rd) [📖](https://fed.chanceyu.com?id=f893f77eeda4a1b71e0dc2c0c0f133a6)
- [2026-04-01-【第3679期】Worker-线程拯救-Node.js-心跳:Inngest-Connect-架构演进](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278851&idx=1&sn=345f03042da9e8e147067ba61f53d100&chksm=bcc60dfe4387d045b658feb6d099daafe18623fc66183e42aa721ad37cfa667b479c1b68a077&scene=0#rd) [📖](https://fed.chanceyu.com?id=eac84651b97ef33fa50224a1f98a61e3)
- [2026-03-31-【第3678期】JavaScript-解析-VIN-码:三种方案全面对比](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278846&idx=1&sn=0d115156c8935e60b834012164ef7800&chksm=bceeca05c71c678764c030775f4d20f2c35ef6630abbaaf57b63d2cf160ee4dd7aca2ec8b20f&scene=0#rd) [📖](https://fed.chanceyu.com?id=bcdcba287995a70e4c3da99c0c0e96ed)
- [2026-03-30-【早说】用Karpathy的自动研究法,让Claude技能全自动进化](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278841&idx=1&sn=e762bc54b4bbb5b0241fb21e4c55724c&chksm=bc9f23e7052140459dd76fc264be79a0da4e99560e7558995a0a3f74e6bae80db75c12d16599&scene=0#rd) [📖](https://fed.chanceyu.com?id=24219f568ce8f9f8b8d8d730e9ebd2e3)
- [2026-03-27-【第3677期】深入解剖-.claude/-文件夹:Claude-Code-的隐藏控制中枢](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278835&idx=1&sn=4e93359b8c4aeb263947a58a0f553a5f&chksm=bc966b89a5b88230ddb95df5a6b7b47bf4c11c0920c45a733e4ea67d57f1734a4e464010cbf4&scene=0#rd) [📖](https://fed.chanceyu.com?id=99add4dd12e2bbba1b91f43a196d574c)
- [2026-03-26-【早说】智能体工程的八重境界](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278825&idx=1&sn=f0b2fbb1b6875140057155fbd56989a8&chksm=bc5746aafc9944fa78e72a3b19fff74b858059a38d70cca896229b414be87694d1261e60143a&scene=0#rd) [📖](https://fed.chanceyu.com?id=525bf5b9998c670208c123171603328d)
- [2026-03-25-【第3676期】Node.js-终于有了虚拟文件系统:node:vfs-来了](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278813&idx=1&sn=80c81e8f0c210a87af8d57d50eb75cea&chksm=bc3e9b6577e3c0ccdb6665b94de47c558fb761c9df30bd83777f9048d053c6592c2788804ad7&scene=0#rd) [📖](https://fed.chanceyu.com?id=812473d5e9668d2f7f7b8b84f1c8462e)
- [2026-03-24-【第3675期】别再纠结格式:5-种设计模式,构建真正可靠的-AI-Agent](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278808&idx=1&sn=cb062b1747e889741fc1ad1e42b7c225&chksm=bc82f8ebecd3a413e6a8963b5fc3378d07daac7e332cfb66b561307cfcea47b174050ae3c627&scene=0#rd) [📖](https://fed.chanceyu.com?id=48eb58d9afd8932362d9060125fd08d3)
- [2026-03-23-【第3674期】Claude-Code-技能实战指南:来自-Anthropic-内部的经验总结](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278796&idx=1&sn=ae74d4204ea67ce9a7f9a44df91ec00c&chksm=bc234bff6ad75f48aea9ff5c2aebd0b9b9c0bb755c7bdfc327341ede689ebdb29aa1ffe7d9d7&scene=0#rd) [📖](https://fed.chanceyu.com?id=c03c0567c066de52e119df074d7d891d)
- [查看更多 >](/details/前端早读课.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="前端大全">
前端大全
</summary>
- [2026-04-01-紧急预警:Axios-供应链被投毒(附排查与紧急补救指南)](http://mp.weixin.qq.com/s/5clC4GUlYIkzDhd_ROVcpw) [📖](https://fed.chanceyu.com?id=b7cd5137bbc85a4fc44d24079287f289)
- [2026-03-31-Claude-Code-意外泄露-51.2万行-TS-核心代码](http://mp.weixin.qq.com/s/fX7ux-Ob1vM4KroYl4a7DA) [📖](https://fed.chanceyu.com?id=1fa5240fac911e4f9fa05daff20c5b30)
- [2026-03-31-前端圈沸腾!Claude造出15KB引擎,渲染狂飙1200倍:文字里能跑马里奥](http://mp.weixin.qq.com/s/vlhTJXe8JiHD7si24Kwszw) [📖](https://fed.chanceyu.com?id=d2b16fc47e976a71a3705cdb4cbf7885)
- [2026-03-31-Node.js核心成员请愿:项目里应禁止AI辅助开发](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624290&idx=1&sn=666bae099de90637678d7f9bf9774341&chksm=802244a3b755cdb5ee6d01f08e7d8ff082b8a7fdf5cc4e5eb902c29b0d448474d415767e3b83#rd) [📖](https://fed.chanceyu.com?id=22a373d7671fb10e7a9990190a93f260) 
- [2026-03-30-OpenClaw-3.28上线!4天爆更上百项重要更新](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624284&idx=1&sn=b9fefd90b5c387d6b3318fe428938918&chksm=8022449db755cd8bb0e0b411950d7c952651614c98126a22035adf2c82c151005cf03fdc55e5#rd) [📖](https://fed.chanceyu.com?id=4ec6b64bb895ef0e50b336572af01f2b) 
- [2026-03-29-第一批“首席龙虾官”,月薪6万](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624278&idx=1&sn=1b9254fa6fd15be1ff73a54f356919a1&chksm=80224497b755cd819fac891e4523d4bd3d804190b413ffb226f99ee9f7166d8d20588a2327ea#rd) [📖](https://fed.chanceyu.com?id=1ff53364e1456931b9d0eaaffb6fdbaf) 
- [2026-03-26-别搞混了!MCP-和-Agent-Skill-到底有什么区别?](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624270&idx=1&sn=5ea044378804aac90c99a512f0841ced&chksm=8022448fb755cd99287d93c79da36f31423d24435ff43c54b7227af4a95586aaa661fadd3d36#rd) [📖](https://fed.chanceyu.com?id=2a59a0c17b5ab748744700b1dc34884e)
- [2026-03-24-AI-已能写-80%-代码,但-Agent-也有致命短板!](http://mp.weixin.qq.com/s/xuD2qJqTuqygrgzm8SE57A) [📖](https://fed.chanceyu.com?id=2fac7d91d3aaa43b49ad19eb1fc8facb)
- [2026-03-21-王自如招-01-号“全栈前端工程师”,没说薪资要求还多,堪比“人形项目组”。有人看完窒息,也有吐槽“没几百万年薪谁去啊”](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624234&idx=1&sn=f0b4f74be6c4fc533de871efe49b2239&chksm=802244ebb755cdfdee002530148d03da78d812f2b3218593957b499d5ed04e9c382a370e120a#rd) [📖](https://fed.chanceyu.com?id=dedc5fd22f10abeaffdc9ef2120b7970)
- [2026-03-18-黄仁勋:龙虾就是新操作系统!](http://mp.weixin.qq.com/s/jmxV8-3Kf5j4Ptl5roYMug) [📖](https://fed.chanceyu.com?id=844fddfdcf4b15ea20a81423e137d57d)
- [查看更多 >](/details/前端大全.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="前端之巅">
前端之巅
</summary>
- [2026-03-15-18-岁创业者用-OpenClaw-管-16-个-AI-Agent,一个人的-Agent-公司怎么运转](http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247526537&idx=1&sn=28314d27c60bebda882b03976db9bef0&chksm=f952ddcace2554dc0d6243571dd6c6b9e84dc31c22665eee4179695181a8e55db4f0f7219bad#rd) [📖](https://fed.chanceyu.com?id=8c2a551fccd6c05e0fe817392312ef5d)
- [2026-03-01-别再学做App了:Karpathy预言Agent将淘汰App-Store,软件进入“用完即丢”时代](http://mp.weixin.qq.com/s/nECex63WezZXZMQEplmDug) [📖](https://fed.chanceyu.com?id=f8cf5da47acc4ed0d54f75762b8b0e0a)
- [2026-01-21-烧掉数万亿-Token、数百-Agent-连跑一周:Cursor“从零写浏览器”,结果是拼装人类代码?](http://mp.weixin.qq.com/s/aXhDQk7Wxa6rZg4ms0I3Tg) [📖](https://fed.chanceyu.com?id=e8badfdbff6ecb2012bc76ee02cc7afd)
- [2026-01-06-信息图的不可能三角,被这个开源项目打破了](http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247526503&idx=1&sn=907c88cb06ac0b09dc308018d0fdc2d4&chksm=f952dd24ce25543252ddfdd3f62994fd95d30039f17040ec08c6837421e93021c397f6a990c2#rd) [📖](https://fed.chanceyu.com?id=5288a82a54e3585b045f986002ff2629)
- [2025-12-14-Cloudflare被React坑了!两周内二次“翻车”:沉睡15年的老代码一招KO全球互联网,安全升级反酿史诗级宕机](http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247526407&idx=1&sn=927074d89c3d51f41041e230de48c673&chksm=f952dd44ce255452cce09960e570099054727b6965afa06217aaadcd59eba0becc9d58a1ea7b#rd) [📖](https://fed.chanceyu.com?id=7b7f953b1057cd12f1a4bafc40674999)
- [2025-12-14-沙龙报名-|-AI领航,跨端技术新范式!](http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247526407&idx=2&sn=06a841bf0bdb146ac14499ea30a7ea60&chksm=f952dd44ce2554521db848180e92b2a624d93022b86ee8c20495a44f1f10a69102e927d9825d#rd) [📖](https://fed.chanceyu.com?id=81a2471d730fd72b4d88e2ed97963c95)
- [2025-12-02-全球前端岗位招聘需求断崖下降-9.89%,前端的未来究竟在哪里?](http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247526386&idx=1&sn=e67f786252a6b088d62c6994a014a8a2&chksm=f952d2b1ce255ba76066a1cf107e97324c8a910e331cf75ea4071717e82f9078bb121fa5fa65#rd) [📖](https://fed.chanceyu.com?id=af735bb797a1dfa60413075d2c94f194)
- [2025-11-23-Meta-用一个烂-Web-应用替换原生-WhatsApp:Windows-用户活该将就?](http://mp.weixin.qq.com/s/Z4hMbWBazYbm8Ua45pb5Rw) [📖](https://fed.chanceyu.com?id=32eb586eafeb6eddaae1b0392c1c1ae0)
- [2025-11-16-印度迎来AI工具“0元购”时代!OpenAI、谷歌等巨头内心os:别急,先让他们上瘾,我们再来收费](http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247526362&idx=1&sn=534112b862b7b641c0cbd3f86fa5d48f&chksm=f952d299ce255b8feffa5fc55414d7d748826d70a7cc4cfac0b8476d94c2e3bf2b87349cdb3f#rd) [📖](https://fed.chanceyu.com?id=5076164405d2ee1b6544eef3ab129e03)
- [2025-11-11-完整前端代码突然公开?苹果把App-Store“老底”都揭了,开发者社区炸锅!](http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247526357&idx=1&sn=6ef10df8180a60a78224c93566659990&chksm=f952d296ce255b80ecd68e067e2fb215fe727f80c7c370415b28662f6a0c1a40133fdac2f7d7#rd)
- [查看更多 >](/details/前端之巅.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="前端技术优选">
前端技术优选
</summary>
- [2022-11-15-Git-是如何工作的](https://mp.weixin.qq.com/s?__biz=MzA4ODUzNTE2Nw==&mid=2451064549&idx=1&sn=3922357763a2ef0c346cb4c792f1f523&chksm=87c45ff5b0b3d6e3491a22c1e0c6acab926ad5c7f2457967a81973e67e96c6bdf150c1dd9de5#rd)
- [2022-11-13-Chrome-DevTools中的这些骚操作,你都知道吗?](https://mp.weixin.qq.com/s?__biz=MzA4ODUzNTE2Nw==&mid=2451064544&idx=1&sn=f9965a33b2b51b20a8d187ed65cb06b2&chksm=87c45ff0b0b3d6e6908e9e5e82771ba696dbafd06be1c5aadd64674f156e1b764040fe1deba4#rd)
- [2022-11-10-面试官:Vue3响应式系统都不会写,还敢说精通?](https://mp.weixin.qq.com/s?__biz=MzA4ODUzNTE2Nw==&mid=2451064482&idx=1&sn=020af2c7e28b8d13624526edf2c133e4&chksm=87c45fb2b0b3d6a4bbd177f797271399f467ea555c607485da65d9630ef7ea1fa0e116152209#rd)
- [2022-11-08-小而美的-css-的原子化](https://mp.weixin.qq.com/s?__biz=MzA4ODUzNTE2Nw==&mid=2451064434&idx=1&sn=8d5b8d48129456b4bcf079dcdbb35eff&chksm=87c45f62b0b3d6743571195f5d26b55ef4fb3b5384ba3b77d5b74c18071bd376d18e83cd5737#rd)
- [2022-11-07-【面试系列一】如何回答如何理解重排和重绘](https://mp.weixin.qq.com/s?__biz=MzA4ODUzNTE2Nw==&mid=2451064409&idx=1&sn=6aceb17d8bd43f15a4b55f3d4d111340&chksm=87c45f49b0b3d65f56b3884f3626566a37ab12a513b697db2ae369a22a220d9e6003a8552638#rd)
- [2022-11-06-两道超有意思的-CSS-面试题,试试你的基础](https://mp.weixin.qq.com/s?__biz=MzA4ODUzNTE2Nw==&mid=2451064392&idx=1&sn=19dee18adbe4dac9d3cb94075d9c71ca&chksm=87c45f58b0b3d64e1d256f8e0e5fced8aaa024f8091f9aa6f5972ff4e18cbf72ffb2eb3b61bb#rd)
- [2022-11-03-尤雨溪:Turbopack真的比Vite快10倍吗?](https://mp.weixin.qq.com/s?__biz=MzA4ODUzNTE2Nw==&mid=2451064371&idx=1&sn=3324989534d2ef636c4e00a8c983fb87&chksm=87c45f23b0b3d63533d77cc8ae83e290391e2daa4fd7c783b3e3f85b197a4fc54af9e4f2610d#rd)
- [2022-11-01-antd-mobile-作者教你写-React-受控组件和非受控组件](https://mp.weixin.qq.com/s?__biz=MzA4ODUzNTE2Nw==&mid=2451064361&idx=1&sn=bc3aed698abd0e3e8e4d3b54ebc5d6d1&chksm=87c45f39b0b3d62f5dc99b0e172455ae0239cb118d1068e521cc1e205e551602b0915e34f149#rd)
- [2022-09-20-写给前端仔的自动化测试入门小作文](http://mp.weixin.qq.com/s?__biz=MzA4ODUzNTE2Nw==&mid=2451064044&idx=1&sn=b110b24130e8d5abf41cf7792e8c4765&chksm=87c45dfcb0b3d4eac6ae48ff6a137aa18c41c499f1f5d24d2d9607f3b8836b4f81c658689b81#rd)
- [2022-09-15-Three.js系列:-在元宇宙看电影,享受-VR-视觉盛宴](http://mp.weixin.qq.com/s?__biz=MzA4ODUzNTE2Nw==&mid=2451063991&idx=1&sn=399b9110462e3a684fb0d3a714e56167&chksm=87c45da7b0b3d4b173d862eb35ad8e36b237195653af514611ff0c80ec42fa5c095c130e7d7c#rd)
- [查看更多 >](/details/前端技术优选.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="程序员成长指北">
程序员成长指北
</summary>
- [2026-04-02-Skills-乱麻了!这款开源神器彻底终结噩梦,Cursor/Claude-一键全同步](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529642&idx=1&sn=16005d3929226468a8ee1b8c0ed4c8b6&chksm=f992727bcee5fb6d914cc77966c95fe1545ed7ad4d303da32eade65e5d892b826e4acc3e8b26#rd) [📖](https://fed.chanceyu.com?id=19f3ce33241b6645338b72c2f8cd13f1) 
- [2026-04-01-LangGraph:基于图的-AI-Agent-编排框架深度解析](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529639&idx=1&sn=325db2a072d5d2f17c7d84734ca0683a&chksm=f9927276cee5fb60652b3ddc219a4b464b07ab5c86df4eb4486fce3b6a47e8fd6b57dd8a4469#rd) [📖](https://fed.chanceyu.com?id=27b84778555eecfdf7f5fbfca2c4730e)
- [2026-03-31-【D2-演讲实录】从上下文工程到-Harness-Engineering](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529620&idx=1&sn=d0bba042bb1f4b84b07f5a3075b8f360&chksm=f9927245cee5fb530ec8bb3e239ab3b50b17deab10d2e0e1b9ce4b92516f38708a91e6c6a22a#rd) [📖](https://fed.chanceyu.com?id=45812a14c92e0214a946516285072199)
- [2026-03-30-嘿,OpenClaw-实战指南来了!](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529618&idx=1&sn=e91261b0e90c4a0a5615d1b62c4db871&chksm=f9927243cee5fb55b81bae193418261cac2de108ff4922a1f956cfc2973f7e04edfa4bd7ff40#rd) [📖](https://fed.chanceyu.com?id=b321c588c8f86fb8ebc86367cf136328)
- [2026-03-27-别再纠结格式:5-种设计模式,构建真正可靠的-AI-Agent](http://mp.weixin.qq.com/s/hEBK6mHO5nM5Sm8MWEmQWQ) [📖](https://fed.chanceyu.com?id=3c42bde4e9133a33914dcc292053b348)
- [2026-03-26-别再裸用-Claude-Code-了!32-个亲测技能-+-8-个-MCP-服务器,开发效率直接拉满!](http://mp.weixin.qq.com/s/jPZsQsXt3ugysBn4bIHOag) [📖](https://fed.chanceyu.com?id=f657ee79c57bd9c719991e3b736f4de5)
- [2026-03-25-web前端230道场景题,背完你的offer就炸了](http://mp.weixin.qq.com/s/9HRDafcOghUyk1QFFW9R6Q) [📖](https://fed.chanceyu.com?id=1ae2a6c36cb02dc10dcb430de4fb8571)
- [2026-03-24-前端发版后页面白屏?一套解决用户停留旧页面问题的完整方案](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529538&idx=1&sn=fa53f719ba91ed033fae434acdf2f443&chksm=f9927293cee5fb856f554e25b5367b1f8bc638c899ff4eb73442b4781657d7f22797f6ee0ef0#rd) [📖](https://fed.chanceyu.com?id=f4f0cd4ace5924b2093000c24d1a7522)
- [2026-03-23-从-nanobot-理解-openclaw-核心设计](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529495&idx=1&sn=49439277e289e9948fb70033f69862e5&chksm=f99272c6cee5fbd0a2e43a039ee7e5eb9fc8958d8eb3e19bee8c4fa9f112dcdcf132db72efa7#rd) [📖](https://fed.chanceyu.com?id=6829985cc37c3fb667d478974ba28203)
- [2026-03-20-为什么越来越多前端开始重看-WebAssembly](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529493&idx=1&sn=8cf92b7ef4c302f8520300bf46b62f6e&chksm=f99272c4cee5fbd27e92a676b16307ac3c014bfc80031bd0b62b0662a8d944f998ecf5895bc1#rd) [📖](https://fed.chanceyu.com?id=e48488bb76dc67e0f2a406113bedc8b3)
- [查看更多 >](/details/程序员成长指北.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="icss前端趣闻">
iCSS前端趣闻
</summary>
- [2025-11-05-当-AI-开始「偷懒」:一次对抗-AI-架构限制的工程实践](http://mp.weixin.qq.com/s/EqZkL7xERKgmV2RDMKhijQ)
- [2025-06-04-虾皮(Shopee)大量前端社招-&-实习生岗位推荐](http://mp.weixin.qq.com/s?__biz=Mzg2MDU4MzU3Nw==&mid=2247499451&idx=1&sn=1eadbc4ec8c1f58a39a33509cdbcd9b1&chksm=ce269d4df951145b31f249309992daf2e8c4d16b398f320c164c837e581b9fd9aedec69f8e99#rd)
- [2025-02-26-浏览器原生「磁吸」效果!Anchor-Positioning-锚点定位神器解析](http://mp.weixin.qq.com/s?__biz=Mzg2MDU4MzU3Nw==&mid=2247499441&idx=1&sn=cb6713ff9b3dfeef2fe8908e75ebd8b1&chksm=ce269d47f9511451ba413febbae93eb3d770c48e41fa4986c29649f855b8e448304dd2631123#rd)
- [2025-02-19-2025,重新认识-HTML!](http://mp.weixin.qq.com/s?__biz=Mzg2MDU4MzU3Nw==&mid=2247499414&idx=1&sn=3685ff0fe846d50843519f482f8f4029&chksm=ce269d60f951147662e21de956a80781f52c29d85c14d765f13d93726d4d97c648d408add397#rd)
- [2025-02-18-巧用-CSS-实现-deepseek-都无法实现的复杂怪状按钮---内凹平滑圆角](http://mp.weixin.qq.com/s?__biz=Mzg2MDU4MzU3Nw==&mid=2247499409&idx=1&sn=abcce97ec283ec9bb7d6e5a799f798ad&chksm=ce269d67f951147134cd54f3984df43bcaf3bcc1e818b51f20562f2ea301c6f2e25ff84bf1cf#rd)
- [2025-01-03-2024-年前端大事记!](http://mp.weixin.qq.com/s?__biz=Mzg2MDU4MzU3Nw==&mid=2247499379&idx=1&sn=541be36ba4f03ed6947e12935e4c3100&chksm=ce269d85f9511493875baa7c01cc3f7562e10f3de5daaed0b5050b00288b6620b98a1e5cf297#rd)
- [2024-12-10-2024-年-CSS-已经变得如此强大了?](http://mp.weixin.qq.com/s?__biz=Mzg2MDU4MzU3Nw==&mid=2247499376&idx=1&sn=74406b84d86433ed48f49b8e43741978&chksm=ce269d86f95114909a1586ccd3378fcd0de2bfe18e24a333176b5de1fe51514d28e4c527922f#rd)
- [2024-12-03-CSS-如何模拟“真实”的进度条?](http://mp.weixin.qq.com/s?__biz=Mzg2MDU4MzU3Nw==&mid=2247499370&idx=1&sn=0bfc88a88808907c415b505a64685838&chksm=ce269d9cf951148a1089f808f2427e07922b0c60cdd80ee9bcad8b99a2129c54de283bd57a2e#rd)
- [2024-11-28-开源了!跳槽一定用得上的前端项目源码!](http://mp.weixin.qq.com/s?__biz=Mzg2MDU4MzU3Nw==&mid=2247499364&idx=1&sn=7ebd86a2106d48470a3e657e34825644&chksm=ce269d92f9511484ad3e682eb47e3dadc736e436d7a5f432c1b99f6e22d1e77cc1a689136b82#rd)
- [2024-11-26-Chrome-全面拥抱-AI-!](http://mp.weixin.qq.com/s?__biz=Mzg2MDU4MzU3Nw==&mid=2247499348&idx=1&sn=aa32fbad227ad0a9289e32b930a0c044&chksm=ce269da2f95114b45a26be3be1b38ccb49cdb937982ce3c772dffc00971e3ea62c0c948e1bf8#rd)
- [查看更多 >](/details/iCSS前端趣闻.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="字节前端-bytefe">
字节前端-ByteFE
</summary>
- [2025-12-16-豆包-应用生成「一点都不技术」创作派对-·-一起来玩儿!](http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506133&idx=1&sn=1d4b9ff25a4a5c879eac3d88e2cd0ae2&chksm=cea96fd4f9dee6c205f1b2f9d1e0a3829c256edf7e519aac00decd02497f2ab6a28b3a72821a#rd) [📖](https://fed.chanceyu.com?id=28e4a70aabca6a3ddb9bd49c2281e164)
- [2025-12-03-AI赋能成长,豆包-应用生成“一点都不技术”挑战赛创作分享第二期来啦!](http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506128&idx=1&sn=0b9341c31064697eef8dcccc06272b24&chksm=cea96fd1f9dee6c7052f888c5d1bce132967747c58f7daba3ef4a67e42b4a208c24811a83650#rd) [📖](https://fed.chanceyu.com?id=4926632218fc237d79494d2ac86cf672)
- [2025-11-19-和“动物塑”创作者面对面!豆包-应用生成“一点都不技术”挑战赛的创作分享来啦](http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506123&idx=1&sn=469d203c58b7734f580c0a0fc622e1c3&chksm=cea96fcaf9dee6dc48075a1cb7e56be8752cfe5c0fc82d1912e1abb4b6b0bfa80a7c5ac6f7b7#rd) [📖](https://fed.chanceyu.com?id=9069392b62b79aeedf3e45cdec06547c)
- [2025-11-11-豆包-应用生成「一点都不技术」创作挑战赛开启,20-万奖池等你解锁!](http://mp.weixin.qq.com/s/xK4w6NX-eupUrdI33mDE8A)
- [2025-10-24-豆包编程升级,新增创作模式,让创意轻松实现](http://mp.weixin.qq.com/s/6pT1xECveLM7iTqiYvjH-g)
- [2025-09-26-免费报名中,到场即有字节周边|豆包编程Workshop深圳场---「一点都不技术」的创作派对](http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506104&idx=1&sn=31656cdc75c005aa5124784b9298de66&chksm=cea96fb9f9dee6af326fbb5d46997b3294a9a27a57763ce96fadfe4580d8bfc3c624fc962f59#rd)
- [2025-06-13-字节跳动技术副总裁洪定坤:TRAE-想做-AI-Development](http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506097&idx=1&sn=13ce2f9aa6f5a4fd95046953a058bdc9&chksm=cea96fb0f9dee6a6116ef5ae31e7cd7d9a4d127db424f4c39ef6fb26ce60f6681f9536440f36#rd)
- [2025-06-11-豆包AI编程:零门槛的应用创作工具](http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506091&idx=1&sn=1d437725945818b8f792f584734e1ed4&chksm=cea96faaf9dee6bcef50719e72e0aa5cef7b234e20affb3cae9d500cad7600c89b7bdfb8e696#rd)
- [2025-03-14-Trae-x-稀土掘金:与-AI-结伴,编程不孤单,推荐&学习有好礼!](http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506085&idx=1&sn=b3add3c5011dda34f3ab6ba3346eff1a&chksm=cea96fa4f9dee6b22768572d68c962b96b9acfb2f3f130b49c191c91e1582a11abcd687ce7a1#rd)
- [2025-03-12-最佳实践-5倍效率+覆盖率90%,大部分程序员不知道的-Cursor-单测生成黑科技](http://mp.weixin.qq.com/s/xbsRnZSjx1rk9QlIAFsqqg)
- [查看更多 >](/details/字节前端-ByteFE.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="前端精读评论">
前端精读评论
</summary>
- [2024-09-09-手动算根号](http://mp.weixin.qq.com/s?__biz=MzU2ODg2NTcwMQ==&mid=2247487318&idx=1&sn=915a4eef6e0bb9515c669dd9c0cfac84&chksm=fc86299ccbf1a08ab3ab914739185f8b4cb8f0164ff3c70d117df28916684d37594c36f269cc#rd)
- [2024-06-13-完整实现神经网络:-实战演练](http://mp.weixin.qq.com/s?__biz=MzU2ODg2NTcwMQ==&mid=2247487301&idx=1&sn=5519c4d3938ccbcb475d9d51d0c85887&chksm=fc86298fcbf1a099d00da3f884bef3958a2a4ddda4af54f48aa883a2f2e54618d6a4ab2f5b7e#rd)
- [2024-04-15-反向传播:-揭秘神经网络的学习机制](https://mp.weixin.qq.com/s?__biz=MzU2ODg2NTcwMQ==&mid=2247487280&idx=1&sn=22bf893e73f32f5320231991abd75154&chksm=fc8629facbf1a0eca72712ee4ae6e3b04487b5f1659cc0ded114eec8dca4bdf6a399e7df9b7e#rd)
- [2024-03-18-实现万能近似函数:-神经网络的架构设计](https://mp.weixin.qq.com/s?__biz=MzU2ODg2NTcwMQ==&mid=2247487238&idx=1&sn=087d27362fccbd6dfef7d1327ac63a14&chksm=fc8629cccbf1a0da30de7bf277d6161d05e87c252c4a89edb227928714a00e7a24454a41fef9#rd)
- [2024-03-04-万能近似定理:-逼近任何函数的理论](https://mp.weixin.qq.com/s?__biz=MzU2ODg2NTcwMQ==&mid=2247487220&idx=1&sn=83048ced417f04242c31ec96cc149475&chksm=fc86283ecbf1a1288e70293e75b595def1ed771e8992167e63dacf3eec29f2f6b2a61458afcb#rd)
- [2024-02-19-机器学习简介:-寻找函数的艺术](https://mp.weixin.qq.com/s?__biz=MzU2ODg2NTcwMQ==&mid=2247487203&idx=1&sn=eeaa40b30e237ed97b56033d783096b2&chksm=fc862829cbf1a13f9fb05e05a12bf651c59f996e13e4e8e07d30e6f0d383746cda62a23b9f2b#rd)
- [2023-12-25-个人养老金利与弊](https://mp.weixin.qq.com/s?__biz=MzU2ODg2NTcwMQ==&mid=2247487185&idx=1&sn=50f6858a0fe0c46d81375822fc9f644c&chksm=fc86281bcbf1a10d276e06b94c4741426311a281a4042e601ee5068de7380fe3064ccb2105dd#rd)
- [2023-11-06-精读《算法题---二叉树中的最大路径和》](https://mp.weixin.qq.com/s?__biz=MzU2ODg2NTcwMQ==&mid=2247487175&idx=1&sn=f49d1556d5be9b49487bf564a1861536&chksm=fc86280dcbf1a11b620f84dd288988886a0a70184c377cd19216ee7823ee38c72e5df275a8db#rd)
- [2023-10-23-精读《算法题---编辑距离》](https://mp.weixin.qq.com/s?__biz=MzU2ODg2NTcwMQ==&mid=2247487161&idx=1&sn=eeaec682d0a496df7d1b4e18d3182361&chksm=fc862873cbf1a16552d67f7db255c8e303478ee4f65f87717915dd3cdf86830f6ccd67c18a28#rd)
- [2023-10-16-精读《VisActor-数据可视化工具》](https://mp.weixin.qq.com/s?__biz=MzU2ODg2NTcwMQ==&mid=2247487154&idx=1&sn=0439632d0d071f62a02b93d45c535927&chksm=fc862878cbf1a16e491acb2f67139ac6016fbdb924f0c6b464f2cf3d6869e07ba2ea0a55d302#rd)
- [查看更多 >](/details/前端精读评论.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="前端从进阶到入院">
前端从进阶到入院
</summary>
- [2026-04-01-紧急!Axios-被投毒,3亿项目受到影响!教你怎么自查!](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517566&idx=1&sn=92989a760237d4a09b0b767c33b0406b&chksm=eb07bd07dc70341109910e6c0ad1e3a0b21e242f24b400cd18655b6ae5cf1ab66d70c1a93365#rd) [📖](https://fed.chanceyu.com?id=3c0173f671e84a4e648a2e9f9bd67f24)
- [2026-03-31-Pretext:前端文本布局的下一个范式转变](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517564&idx=1&sn=2cd94053d488764d59c532d486d3ad2c&chksm=eb07bd05dc703413e05333028b748e1e4f0f3624682de5898deba9a50dfd447c806fc0451991#rd) [📖](https://fed.chanceyu.com?id=c021bab3277c43008ce85dc8fcfcfb74)
- [2026-03-30-CDP-是什么?理解它,才算真正懂了前端调试工具链](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517562&idx=1&sn=b173e453dec75217a35c0a9012ed65a7&chksm=eb07bd03dc703415615de2a6c9e1f7d226d052e2846f87d96a2f3e7e5305874f3dd0caf08388#rd) [📖](https://fed.chanceyu.com?id=157a5d153cc12fb187a0ab5365569f94)
- [2026-03-27-刚刚,Vite-两个新工具发布!Webpack-完!](http://mp.weixin.qq.com/s/kRAX2YZ7Kw9r5Wr4j6B_rw) [📖](https://fed.chanceyu.com?id=2235194d632f5ccd6d92556e4123dca2)
- [2026-03-14-刚刚,尤雨溪宣布正式推出Vite-Plus,竟不收费?](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517558&idx=1&sn=6f9b01aa83df8ec54d293cda8da71bce&chksm=eb07bd0fdc7034198e0243735dcb1cf228975f48e68770f0885d650b785e0dea6738ab9d10e8#rd) [📖](https://fed.chanceyu.com?id=0471c93eac7a646b8a19f18319c38046)
- [2026-03-13-Vite-8.0-来了:2.0-以来的最大更新!](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517556&idx=1&sn=3840b8b034f235fe1e12070077343f4d&chksm=eb07bd0ddc70341bf549beee0b1d7ba736c651d991410b820a3e0bdae464e4c414d1f87c9cf4#rd) [📖](https://fed.chanceyu.com?id=7462d69a40f0f1d48a4b2d0fd747a6f3)
- [2026-03-11-重磅!Vite-DevTools-发布!功能一览!](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517554&idx=1&sn=d5a1a3391bf0336d7efc8f592d0d39bb&chksm=eb07bd0bdc70341d241360d2cb7b07599a5da1a0aaaa6826824abdfbb536287cfd4321260e5c#rd) [📖](https://fed.chanceyu.com?id=749d90fa5286e50273b5668ed1eac610)
- [2026-03-10-web前端230道场景题,背完你的offer就炸了](http://mp.weixin.qq.com/s/0hJRbl2RInw6jQaU7hevGg) [📖](https://fed.chanceyu.com?id=8ec7977fe819cad14d012a11d946e774)
- [2026-03-06-Vue-Native-最新进展!](http://mp.weixin.qq.com/s/UuX1cI0spiYbZuHkKJ7ltA) [📖](https://fed.chanceyu.com?id=edc814033f32e63879e3d861784d3ae0)
- [2026-02-27-刚刚,Webpack-官宣-2026-年大动作!](http://mp.weixin.qq.com/s/UkgAvcj6B9-eUQc880rf4g) [📖](https://fed.chanceyu.com?id=6728388e9d0c549ddf11740685752a82)
- [查看更多 >](/details/前端从进阶到入院.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="前端侦探">
前端侦探
</summary>
- [2025-09-22-Safari又出bug了?临时修复-iOS-26弹出键盘后定位错乱问题](http://mp.weixin.qq.com/s/rWmsDUdhRvp8KlPBhvJZaQ)
- [2025-08-25-CSS-小技巧:如何将-img-转换成-background-image](http://mp.weixin.qq.com/s?__biz=MzIyMDc1NTYxNg==&mid=2247490764&idx=1&sn=5fb164e0dc41dfdbc33b63c23df3455e&chksm=97c67f03a0b1f615220c599cc96c08468ed3e0347620bd05a9c2dba7bbd0b177546e7913378c#rd)
- [2025-08-18-借助CSS实现自适应屏幕边缘的tooltip](http://mp.weixin.qq.com/s/4ifcSObrhWr62QUjwZxqDw)
- [2025-08-11-借助CSS实现点赞粒子动效](http://mp.weixin.qq.com/s/82J8TWEKnJnx1A47R9zMRw)
- [2025-05-19-原子化的未来?了解一下全面进化的CSS-attr函数](http://mp.weixin.qq.com/s?__biz=MzIyMDc1NTYxNg==&mid=2247490394&idx=1&sn=dc419620ec0db25afa73c1fdc0e3fb20&chksm=97c67895a0b1f183020d68e2c6d7e0d4d01fb352811c9b323c943e95749b91582017d82f6917#rd)
- [2025-05-12-万能的渐变!CSS-渐变实现自适应进度条](http://mp.weixin.qq.com/s?__biz=MzIyMDc1NTYxNg==&mid=2247490229&idx=1&sn=bf5ff47df0c7940838869880022c5e74&chksm=97c6797aa0b1f06c38701c7112363dedd824a7bc430102cebeed63249389c0af6ad29c9ff9a1#rd)
- [2025-04-27-CSS-grid-布局如何添加分隔线?](http://mp.weixin.qq.com/s?__biz=MzIyMDc1NTYxNg==&mid=2247490207&idx=1&sn=53e0167f3ded40d5c0c7fc3f8f8a7084&chksm=97c67950a0b1f046b393284ad289cbefcaba1d673b506d867e00f49e72bb7e23b48d5907895a#rd)
- [2025-04-22-使用CSS给标题添加书名号并超出省略](http://mp.weixin.qq.com/s?__biz=MzIyMDc1NTYxNg==&mid=2247490176&idx=1&sn=15070209f0d63e249dce32e355e58d7d&chksm=97c6794fa0b1f0598cea456be394ae9af12f566daf7360525066304ca6b1c5ad7c12a9de7c91#rd)
- [2024-12-11-太神奇了,仅使用彩色字体实现代码高亮](http://mp.weixin.qq.com/s?__biz=MzIyMDc1NTYxNg==&mid=2247490128&idx=1&sn=a5eaba73454ccf49749c042e84214185&chksm=97c6799fa0b1f08959fbc0a34bcb77e684a49963d8522790edcee77819f0231552b7b9915b6f#rd)
- [2024-12-02-CSS-如何模拟“真实”的进度条?](http://mp.weixin.qq.com/s?__biz=MzIyMDc1NTYxNg==&mid=2247490108&idx=1&sn=0bec47995422abff5453d6bb3d984c64&chksm=97c679f3a0b1f0e54d1b6ebf016f6497e223c56ddf165c019652c405652dc9e66ff2657b6480#rd)
- [查看更多 >](/details/前端侦探.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="淘系前端团队">
淘系前端团队
</summary>
- [2026-04-03-淘宝跨端体验优化-AI-演进之路](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543161&idx=1&sn=203f52399657de9f0c1d8eced67d9cab&chksm=8390dc21b4e755379d46e4ed6d670ec4c0a5243dad60a4d1f9ed2950d918804a9e3ba5b491f6#rd) [📖](https://fed.chanceyu.com?id=8cb07e172464c706363d4f3db943a7e0)
- [2026-04-01-淘宝营销会场智能测试平台的AI落地实践](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543144&idx=1&sn=721c1a6266e58ecc7d383e368a04bebd&chksm=8390dc30b4e755269829f70d5ea10ea7281c2b27b5b34cde97c6560b11a7b6f96a82599eee60#rd) [📖](https://fed.chanceyu.com?id=8bebb64e26b7f9fa38e89f0f97c6d4db)
- [2026-03-30-从-Vibe-Coding-到范式编程:用-Spec-打造淘系交易的-AI-领域专家](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543105&idx=1&sn=fec6d68f9c81df650d53981a460b0b11&chksm=8390dc19b4e7550f67eb5bcfedd406594f70904fdf5452690d8cda3c1bb13e3fa36df0dcc3ba#rd) [📖](https://fed.chanceyu.com?id=42c72adf37ba71a073269ab89ee10968)
- [2026-03-27-97.9%采纳率,胶水编程:业务需求出码最佳实践【天猫AI-Coding实践系列】](http://mp.weixin.qq.com/s/G3aKbzdGUyD2h1aVjvbr2g) [📖](https://fed.chanceyu.com?id=90a1d805c6821557f4eccd1be2d00e16)
- [2026-03-25-拒绝“感觉有效”:用数据证明-AI-Coding-的真实团队价值【天猫AI-Coding实践系列】](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543083&idx=1&sn=b4759cb5c4ce079dc214782e37a0a506&chksm=8390dbf3b4e752e5cec248facb974fa83af70c2a3c4021fe6accbe8174be9a26e302a54ca2e7#rd) [📖](https://fed.chanceyu.com?id=acbe2ba947f22e139471594b060866c3)
- [2026-03-23-知识基座:让“AI-越用越懂业务”的团队经验实践【天猫AI-Coding实践系列】](http://mp.weixin.qq.com/s/P-p4-BH8AAOnTBRcpsoKeQ) [📖](https://fed.chanceyu.com?id=cde722a2acae70ca2f77074b5169c471)
- [2026-03-20-请查收!淘天集团27届实习生校招大事件,文末阅读原文报名宣讲会](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543062&idx=2&sn=f51acd46793fdb53f4a6a998ad935311&chksm=8390dbceb4e752d8f6f15f2c6142ec0c1f56922602b252327390ed41b38f2db956508c610ec5#rd) [📖](https://fed.chanceyu.com?id=a64f992b9cdd45519de054f96f72991a)
- [2026-03-20-淘天集团2027届实习生招聘正式启动,文末阅读原文查看详情](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543062&idx=1&sn=d453e985ad6644093c56b572c267209c&chksm=8390dbceb4e752d8e6ef8d405f12d66c6edb05bf83e4aa0d085b78794ff60f6fb4340c3e71ff#rd) [📖](https://fed.chanceyu.com?id=992c3c1c2fc9e37948813e7e28c2ef1a)
- [2026-03-18-AI-Coding前端实践后的复盘总结](http://mp.weixin.qq.com/s/CqYqbE0HdL7GzLGe_vbMmA) [📖](https://fed.chanceyu.com?id=50bab1a2ffce0d9bee59f3398a642b69)
- [2026-03-16-软件为何越做越乱?万字长文讲透软件的“复杂性”](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543011&idx=1&sn=0e7c2b240306fdf1cd61c79b717845a7&chksm=8390dbbbb4e752ad7217c16a822a053d02da8c68bb6b3fded5ed9411b7607e352f509b180475#rd) [📖](https://fed.chanceyu.com?id=1734e0eba7ecfabfcf1d915cdf3fe717)
- [查看更多 >](/details/淘系前端团队.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="张鑫旭-鑫空间-鑫生活">
张鑫旭-鑫空间-鑫生活
</summary>
- [2026-03-30-CSS-corner-shape与背景底纹技术](https://www.zhangxinxu.com/wordpress/2026/03/css-corner-shape-background-pattern/) [📖](https://fed.chanceyu.com?id=1e8e5062081b938e9e4df67bf38cc395)
- [2026-03-17-浅学WebTransport-API:下一代Web双向通信技术](https://www.zhangxinxu.com/wordpress/2026/03/webtransport-api/) [📖](https://fed.chanceyu.com?id=a5d45f581f6b0f5577946686c52eb8c3)
- [2026-03-09-HTML-dialog元素新支持了closedBy属性](https://www.zhangxinxu.com/wordpress/2026/03/html-dialog-closedby/) [📖](https://fed.chanceyu.com?id=9f9bb84eeb91885069a4c7e57a58b7cc)
- [2026-03-04-HTML-interestfor属性与悬停popover交互效果](https://www.zhangxinxu.com/wordpress/2026/03/css-interestfor-invoker-target-source/) [📖](https://fed.chanceyu.com?id=822c0f3fd53f1b05b135738d40620e01)
- [2026-02-25-点击图片放大查看交互效果的最佳实现](https://www.zhangxinxu.com/wordpress/2026/02/image-preview-best-practice/) [📖](https://fed.chanceyu.com?id=5f82b6fecb642fa5b8e3b8bf3b4cf826)
- [2026-02-12-JS正则表达式y标识符之粘性匹配](https://www.zhangxinxu.com/wordpress/2026/02/js-regexp-y-sticky-flags/) [📖](https://fed.chanceyu.com?id=58bcd114470f0267ae4de2942db13c23)
- [2026-02-06-CSS-text-box属性又是干嘛用的?](https://www.zhangxinxu.com/wordpress/2026/02/css-text-box/) [📖](https://fed.chanceyu.com?id=10034adbb21fc099e6c8e73efcf6be15)
- [2026-01-30-告别insertBefore,使用moveBefore移动DOM元素](https://www.zhangxinxu.com/wordpress/2026/01/insertbefore-movebefore-dom/) [📖](https://fed.chanceyu.com?id=03ec37e24f1ef7cb060bbedd69b6b39a)
- [2026-01-22-Promise.try和Promise.withResolvers作用速览](https://www.zhangxinxu.com/wordpress/2026/01/promise-try-withresolvers/) [📖](https://fed.chanceyu.com?id=075d29e0b1067a2d6e38cdc48862924f)
- [2026-01-19-CSS-text-decoration-inset属性首发简介](https://www.zhangxinxu.com/wordpress/2026/01/css-text-decoration-inset/) [📖](https://fed.chanceyu.com?id=ca0f23412fec2060ad6ef026c9ac4a2d)
- [查看更多 >](/details/张鑫旭-鑫空间-鑫生活.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="阮一峰的网络日志">
阮一峰的网络日志
</summary>
- [2026-04-03-科技爱好者周刊(第-391-期):AI-的贫富分化](http://www.ruanyifeng.com/blog/2026/04/weekly-issue-391.html) [📖](https://fed.chanceyu.com?id=0d84ac0a58d1188959ec6e9608c9b063)
- [2026-03-27-科技爱好者周刊(第-390-期):没有语料,大模型就是智障](http://www.ruanyifeng.com/blog/2026/03/weekly-issue-390.html) [📖](https://fed.chanceyu.com?id=073df49de2977e08665689ac04a543d0)
- [2026-03-21-套壳中国大模型撑起500亿美元估值?扒一扒-Cursor-的"套壳"疑云](http://www.ruanyifeng.com/blog/2026/03/kimi-cursor.html) [📖](https://fed.chanceyu.com?id=d1e28186cf9550731b0af785cbcf89ba)
- [2026-03-20-科技爱好者周刊(第-389-期):未来如何招聘程序员](http://www.ruanyifeng.com/blog/2026/03/weekly-issue-389.html) [📖](https://fed.chanceyu.com?id=3fc7a8ae3d8d2fb67633f194f6aea69e)
- [2026-03-13-科技爱好者周刊(第-388-期):测试是新的护城河](http://www.ruanyifeng.com/blog/2026/03/weekly-issue-388.html) [📖](https://fed.chanceyu.com?id=6c79a88a7991391d1f88497e0d26613b)
- [2026-03-12-零安装的"云养虾":ArkClaw-使用指南](http://www.ruanyifeng.com/blog/2026/03/arkclaw.html) [📖](https://fed.chanceyu.com?id=5f4d9da2958a384bf8c9921dcbc65d28)
- [2026-03-06-科技爱好者周刊(第-387-期):你是领先的](http://www.ruanyifeng.com/blog/2026/03/weekly-issue-387.html) [📖](https://fed.chanceyu.com?id=555aecb7a1061f01f63db9951be1a979)
- [2026-02-27-科技爱好者周刊(第-386-期):当外卖员接入-AI](http://www.ruanyifeng.com/blog/2026/02/weekly-issue-386.html) [📖](https://fed.chanceyu.com?id=241ea1cb865af38b998fc2766990e63a)
- [2026-02-14-字节全家桶-Seed-2.0-+-TRAE-玩转-Skill](http://www.ruanyifeng.com/blog/2026/02/seed-2.0.html) [📖](https://fed.chanceyu.com?id=418457a7479a2ed56a623a9e423edb3c)
- [2026-02-13-科技爱好者周刊(第-385-期):马斯克害怕中国车企吗?](http://www.ruanyifeng.com/blog/2026/02/weekly-issue-385.html) [📖](https://fed.chanceyu.com?id=78751a1c9871497549312d9d1549e541)
- [查看更多 >](/details/阮一峰的网络日志.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="凹凸实验室">
凹凸实验室
</summary>
- [2023-09-14-给-Web-前端工程师看的用-Rust-开发-wasm-组件实战](https://mp.weixin.qq.com/s?__biz=MzIxMzExMjYwOQ==&mid=2651899748&idx=1&sn=5050cdc23621871a0ffd4a723f62c471&chksm=8c5fa52abb282c3c597ad04839cbe92a5076972fc77c4c9868dd0dc62ee0c3bfe003784677ea#rd)
- [2023-08-25-不一样的"代码拆分"+"预加载"实现应用性能及体验兼得](https://mp.weixin.qq.com/s?__biz=MzIxMzExMjYwOQ==&mid=2651899728&idx=1&sn=39b1222c086d4fb6a14ae906e71611c0&chksm=8c5fa51ebb282c083c831ac790b71450808576b603aabe36653db6249522ded42543e380f352#rd)
- [2023-06-01-大型-3D-互动项目开发和优化实践](https://mp.weixin.qq.com/s?__biz=MzIxMzExMjYwOQ==&mid=2651899652&idx=1&sn=52054970054de61813c43c7d714d1075&chksm=8c5fa54abb282c5cf65441b58dc0ecb35e86d5d4d2f5a67defb0cce8ccc6879bc274209123cb#rd)
- [2023-04-13-实践指南-前端性能提升-270%](https://mp.weixin.qq.com/s?__biz=MzIxMzExMjYwOQ==&mid=2651899638&idx=1&sn=5571863db55c99029f68075f72008067&chksm=8c5fa4b8bb282dae576cb38ff03e85dc138ac38806be06627c753ae431d6b81851a4468728b9#rd)
- [2023-03-31-多编译内核生态下的极速研发体验](https://mp.weixin.qq.com/s?__biz=MzIxMzExMjYwOQ==&mid=2651899577&idx=1&sn=320a8fb4fe66bb3391b7c95e45df30ce&chksm=8c5fa4f7bb282de1b84ab3256bee010e616741e09e1f11bba2ef3909512dc5dafb9b9539e856#rd)
- [2023-02-23-2023-年的-Web-Worker-项目实践](https://mp.weixin.qq.com/s?__biz=MzIxMzExMjYwOQ==&mid=2651899575&idx=1&sn=0310a88f72f7b63bfebc0835a2df291f&chksm=8c5fa4f9bb282def5a8296269ed7e874a367809aefe3a88bca262672ad6fb34932c27f97ad0a#rd)
- [2023-02-16-Taro-正式发布-3.6-版本:支持跨端路由、请求库,新增鸿蒙、Web-端平台插件,小程序持续集成-CI-能力升级](https://mp.weixin.qq.com/s?__biz=MzIxMzExMjYwOQ==&mid=2651899551&idx=1&sn=0d30a7d3949dc41470d0c5926d5537ef&chksm=8c5fa4d1bb282dc7b352803a0128c69863fcc4d21f9716e4aeb200ae259a750af40a84948162#rd)
- [2023-02-16-3-个自定义防抖-Hooks-的实现原理](https://mp.weixin.qq.com/s?__biz=MzIxMzExMjYwOQ==&mid=2651899551&idx=2&sn=f1ad58e250d11fc9cc1752bf2ce16446&chksm=8c5fa4d1bb282dc75a4823bd1cdb04d1554058b7eb3957a56291e059a255da55a8f5c87bbdba#rd)
- [2023-02-02-使用-Skia-绘制-2D-图形](https://mp.weixin.qq.com/s?__biz=MzIxMzExMjYwOQ==&mid=2651899487&idx=1&sn=2db5b197775b854a343201fdc42eaba7&chksm=8c5fa411bb282d076b68229cf2ec751e196228ff97db6db1ca89a9640f80e14284f45ec89651#rd)
- [2022-11-17-从组件化角度聊聊设计工程化](https://mp.weixin.qq.com/s?__biz=MzIxMzExMjYwOQ==&mid=2651899425&idx=1&sn=6ca4b5c483b04afa75d9873faf7fe4f4&chksm=8c5fa46fbb282d79278c2f25a9c3e31229c938ad048bee74ba9094b79e88a8c5e394fd55523a#rd)
- [查看更多 >](/details/凹凸实验室.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="风痕·術&思">
风痕·術&思
</summary>
- [2026-02-03-2025-年终总结](https://fenghen.me/posts/2026/02/03/_2025-year-end-summary/) [📖](https://fed.chanceyu.com?id=ad3fa045da9a0b2ffd9bdd839d3c46b1)
- [2025-10-17-笔记软件孕育数字分身](https://fenghen.me/posts/2025/10/17/notes-and-digital-twin/)
- [2025-09-09-终端开发环境搭建](https://fenghen.me/posts/2025/09/09/terminal-dev-env/) [📖](https://fed.chanceyu.com?id=8bdf56f0343341866fbcbeb1ac546518)
- [2025-09-01-ZingAI.video-Online-Talking-Head-Video-Editing](https://fenghen.me/posts/2025/09/01/zingai-overview-en/)
- [2025-09-01-ZingAI.video-在线口播视频剪辑](https://fenghen.me/posts/2025/09/01/overview/)
- [2025-08-20-互联网“骨干协议”-——-BGP-协议](https://fenghen.me/posts/2025/08/20/bgp/)
- [2025-07-07-视频编辑,如何选择技术方案](https://fenghen.me/posts/2025/07/07/choosing-the-right-technical-approach-for-video-editing/)
- [2025-04-11-商务合作(帅气博主在线接单)](https://fenghen.me/posts/2025/04/11/business-cooperation/)
- [2025-03-29-2025-裸辞计划](https://fenghen.me/posts/2025/03/29/_2025-plan/)
- [2025-03-12-AI-媒介将如何重塑人类社会(风痕、Claude-访谈记录)](https://hughfenghen.github.io/posts/2025/03/12/how-ai-media-will-reshaping-human-society/)
- [查看更多 >](/details/风痕·術&思.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="nodejs技术栈">
Nodejs技术栈
</summary>
- [2026-04-03-刚刚,Cursor-3.0-发布!编辑器没了,变成了-Agent-指挥部](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523156&idx=1&sn=13c401936934f668f2bd9c8c4fdd6024&chksm=e80fd41adf785d0cb06cb551a5c734f8c4f5303c950327706f877b3157571f0272e41b659460#rd) [📖](https://fed.chanceyu.com?id=d139e544d2f29bcdacbd3f92b687ca12)
- [2026-04-02-GitHub-Star-暴涨!前-React-核心成员出手,把浏览器-30-年算不好的文字布局问题解决了](http://mp.weixin.qq.com/s/ibVkVGk62j3kCPO_wxBYlg) [📖](https://fed.chanceyu.com?id=f17f69647c6d7eb365318f05d053617f)
- [2026-04-01-504块咖啡钱,和一个价值千万的-AI-梦](http://mp.weixin.qq.com/s/9mKWwG8pg1Pn447ETZTfZw) [📖](https://fed.chanceyu.com?id=b0837207e4fe100e308eea3c68d1f49a)
- [2026-03-31-速查!周下载-3-亿次的前端神包,刚被植入了远控木马](http://mp.weixin.qq.com/s/f0C-Vqbz8h6oBhg8RUL2ew) [📖](https://fed.chanceyu.com?id=746d49acbd08484e3235ef14365cc7d0)
- [2026-03-31-干不过就加入!OpenAI-把-Codex-搞成插件塞进了-Claude-Code](http://mp.weixin.qq.com/s/w-5jGIW9r-i4DZsZ7687Iw) [📖](https://fed.chanceyu.com?id=f3f7bb0ef7353c9e91180508b4bfb0b3)
- [2026-03-30-Codex-Plugins-来了,我拿它做了第一个实验:把微信接进去,体验翻车了](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523103&idx=1&sn=2bebf517423637d86b233186716e7493&chksm=e80fd451df785d479cd744f05176c87e94ffe567e513c89fee93f6643f2f1506f3dd26bbdb82#rd) [📖](https://fed.chanceyu.com?id=14203a7f69ff3459705e49aaaf6c4005)
- [2026-03-28-Next.js-不写给人类了?新版本的四个改动,全是给-AI-Agent-准备的](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523085&idx=1&sn=ea65083747c5e02cb075955ff60794a5&chksm=e80fd443df785d557c1c665080169b63a803afdfe3974a2b2a91305d0e81a25f2ef873f3372c#rd) [📖](https://fed.chanceyu.com?id=a5f0f644afc6d1965c71fd7683a11422)
- [2026-03-26-一个-19000-行的-PR-引爆了-Node.js-社区,集体请愿禁止-AI-代码](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523079&idx=1&sn=e80d92be76cdc8f2f96138d009218278&chksm=e80fd449df785d5fefd16cd89ae6ec64c327e43844f7b714d366549ad0625d0c6b369f42575a#rd) [📖](https://fed.chanceyu.com?id=2c78cb925a901f326a5d100c3b24fffa)
- [2026-03-25-Anthropic-连放两个大招:昨天接管你的电脑,今天甩掉你的审批](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523072&idx=1&sn=76f679904672f2fc2a96a265c76e6288&chksm=e80fd44edf785d586f5c7017b4e62a6db766570f3e7ad14bdbe638c0041be1a2ba8520904bc5#rd) [📖](https://fed.chanceyu.com?id=e0faeb9c4ad36b0766fcf81be20ddf68)
- [2026-03-24-web前端230道场景题,背完你的offer就炸了](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523039&idx=1&sn=797548474ec8c4f08dcfbbe6cbaaf95e&chksm=e80fd591df785c873b2f4f463f74775cdabd620cb81b5d6954230ce3ccf8bebc8739b3e6e1d3#rd) [📖](https://fed.chanceyu.com?id=61e2e122e3c5a54c1c5412671b2e3288)
- [查看更多 >](/details/Nodejs技术栈.md)
<div align="right"><a href="#文章来源">⬆ 返回顶部</a></div>
</details>
================================================
FILE: TAGS.md
================================================
> 提示:只是根据文章标题简单匹配分类
:alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)
## 文章分类
- [AI](#ai)
- [React](#react)
- [Vue](#vue)
- [TypeScript](#typescript)
- [JavaScript](#javascript)
- [NodeJS](#nodejs)
- [CSS](#css)
- [HTML](#html)
- [构建打包](#构建打包)
- [小程序](#小程序)
- [手机应用](#手机应用)
- [桌面应用](#桌面应用)
- [浏览器](#浏览器)
- [游戏开发](#游戏开发)
- [图形图像](#图形图像)
- [音视频](#音视频)
- [性能优化](#性能优化)
- [前端进阶](#前端进阶)
- [服务端](#服务端)
- [版本控制](#版本控制)
- [招聘面试](#招聘面试)
- [其它](#其它)
## 文章链接
<details open>
<summary id="ai">
AI
</summary>
<p></p>
> 关键字:`AI`、`LLM`、`GPT`、`GLM`、`大模型`、`人工智能`、`机器学习`、`深度学习`、`神经网络`、`ChatGPT`、`Cursor`、`Codex`、`Claude`、`Copilot`、`Agent`、`Gemini`、`Llama`、`RAG`、`Prompt`、`提示词`、`向量数据库`、`Embedding`、`LangChain`、`LlamaIndex`、`AIGC`、`多模态`、`微调`、`自然语言处理`、`NLP`、`大语言模型`、`智能体`、`MCP`、`OpenClaw`
- [【Nodejs技术栈】刚刚,Cursor-3.0-发布!编辑器没了,变成了-Agent-指挥部](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523156&idx=1&sn=13c401936934f668f2bd9c8c4fdd6024&chksm=e80fd41adf785d0cb06cb551a5c734f8c4f5303c950327706f877b3157571f0272e41b659460#rd) [📖](https://fed.chanceyu.com?id=d139e544d2f29bcdacbd3f92b687ca12)
- [【阮一峰的网络日志】科技爱好者周刊(第-391-期):AI-的贫富分化](http://www.ruanyifeng.com/blog/2026/04/weekly-issue-391.html) [📖](https://fed.chanceyu.com?id=0d84ac0a58d1188959ec6e9608c9b063)
- [【淘系前端团队】淘宝跨端体验优化-AI-演进之路](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543161&idx=1&sn=203f52399657de9f0c1d8eced67d9cab&chksm=8390dc21b4e755379d46e4ed6d670ec4c0a5243dad60a4d1f9ed2950d918804a9e3ba5b491f6#rd) [📖](https://fed.chanceyu.com?id=8cb07e172464c706363d4f3db943a7e0)
- [【前端早读课】【早说】Ghostty创始人的AI之旅](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278865&idx=1&sn=72b0e18b749cbe4817d6f7b96c6ea7ed&chksm=bc0ba13f35cf0cd8f410ef892d3490d0c93ca1c3c35d2469057a5c6b627953e4a637451bd6f5&scene=0#rd) [📖](https://fed.chanceyu.com?id=a293f09b5b10f96424be936e261e049a)
- [【程序员成长指北】Skills-乱麻了!这款开源神器彻底终结噩梦,Cursor/Claude-一键全同步](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529642&idx=1&sn=16005d3929226468a8ee1b8c0ed4c8b6&chksm=f992727bcee5fb6d914cc77966c95fe1545ed7ad4d303da32eade65e5d892b826e4acc3e8b26#rd) [📖](https://fed.chanceyu.com?id=19f3ce33241b6645338b72c2f8cd13f1)
- [【前端早读课】【早说】AI指数级进化时代的产品管理](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278860&idx=1&sn=54fc7b48572bb519431e0f10a96445c9&chksm=bcb6d6dbe3d7fe42cd6a8ffb84b072ab7ce5b5787129441ac8e8f430178090eed732c782546d&scene=0#rd) [📖](https://fed.chanceyu.com?id=f893f77eeda4a1b71e0dc2c0c0f133a6)
- [【Nodejs技术栈】504块咖啡钱,和一个价值千万的-AI-梦](http://mp.weixin.qq.com/s/9mKWwG8pg1Pn447ETZTfZw) [📖](https://fed.chanceyu.com?id=b0837207e4fe100e308eea3c68d1f49a)
- [【淘系前端团队】淘宝营销会场智能测试平台的AI落地实践](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543144&idx=1&sn=721c1a6266e58ecc7d383e368a04bebd&chksm=8390dc30b4e755269829f70d5ea10ea7281c2b27b5b34cde97c6560b11a7b6f96a82599eee60#rd) [📖](https://fed.chanceyu.com?id=8bebb64e26b7f9fa38e89f0f97c6d4db)
- [【程序员成长指北】LangGraph:基于图的-AI-Agent-编排框架深度解析](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529639&idx=1&sn=325db2a072d5d2f17c7d84734ca0683a&chksm=f9927276cee5fb60652b3ddc219a4b464b07ab5c86df4eb4486fce3b6a47e8fd6b57dd8a4469#rd) [📖](https://fed.chanceyu.com?id=27b84778555eecfdf7f5fbfca2c4730e)
- [【Nodejs技术栈】干不过就加入!OpenAI-把-Codex-搞成插件塞进了-Claude-Code](http://mp.weixin.qq.com/s/w-5jGIW9r-i4DZsZ7687Iw) [📖](https://fed.chanceyu.com?id=f3f7bb0ef7353c9e91180508b4bfb0b3)
- [查看更多 >](/details/tags/ai.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="react">
React
</summary>
<p></p>
> 关键字:`React`、`Rax`、`Nerv`、`Redux`、`useState`、`useEffect`、`useCallback`、`useRef`、`useMemo`、`Hooks`、`Suspense`、`zustand`、`Next.js`、`Remix`、`JSX`、`Fiber`、`RSC`、`useContext`、`useReducer`、`useLayoutEffect`、`useImperativeHandle`、`useDeferredValue`、`useTransition`、`useId`、`useSyncExternalStore`、`Recoil`、`MobX`、`Jotai`、`Valtio`、`SWR`
- [【Nodejs技术栈】GitHub-Star-暴涨!前-React-核心成员出手,把浏览器-30-年算不好的文字布局问题解决了](http://mp.weixin.qq.com/s/ibVkVGk62j3kCPO_wxBYlg) [📖](https://fed.chanceyu.com?id=f17f69647c6d7eb365318f05d053617f)
- [【Nodejs技术栈】Next.js-不写给人类了?新版本的四个改动,全是给-AI-Agent-准备的](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523085&idx=1&sn=ea65083747c5e02cb075955ff60794a5&chksm=e80fd443df785d557c1c665080169b63a803afdfe3974a2b2a91305d0e81a25f2ef873f3372c#rd) [📖](https://fed.chanceyu.com?id=a5f0f644afc6d1965c71fd7683a11422)
- [【JavaScript-Weekly】TypeScript-6.0,-Next.js-16.2,-and-a-new-Node.js-runtime](https://javascriptweekly.com/issues/778) [📖](https://fed.chanceyu.com?id=d0951a7d0cbc423bd09580c6dc93539d)
- [【Nodejs技术栈】硬刚-Next.js!尤雨溪隆重推出-Void,Vite-生态终于有自己的"Vercel"了](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247522878&idx=1&sn=3d8fcf18a18ab30c35aeca4d4d0789fc&chksm=e80fd570df785c66f45596f8c329a91fc3a562acd31551149195c027ccaa38278df32aa9ccb7#rd) [📖](https://fed.chanceyu.com?id=a17e0ef80e109ad39c0e2cd56e4f01b2)
- [【前端早读课】【第3667期】React-Fiber-原理解析:从递归渲染到可中断调度](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278693&idx=1&sn=d28febc993118336805e8e800732415f&chksm=bc46f874bf464ddf092000d682657d2a75395fdec127c6d16151410592aa79003a12342ae2fd&scene=0#rd) [📖](https://fed.chanceyu.com?id=be1cd0faf3780bde25f0305b305c2600)
- [【程序员成长指北】仅一周!Cloudflare用AI重写了-Next.js,提速-4-倍,打包体积锐减-57%](http://mp.weixin.qq.com/s/Qv8y_x_o6ITIiYV4NnW5rQ) [📖](https://fed.chanceyu.com?id=189749723a42f8c71797e9bb7e3a6a8b)
- [【程序员成长指北】React-多年第一,被一只龙虾-100-天干翻了!](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529334&idx=1&sn=01107838d42e78d358c5618f6d537f3b&chksm=f99275a7cee5fcb1ff61c1fe2b00da91cb29986aa9c48e0a849b79b356e38c0cce5ff53c39d0#rd) [📖](https://fed.chanceyu.com?id=bba09257d08b2a44277c3ad609e14b66)
- [【程序员成长指北】Next.js-16-微前端新玩法:Multi-Zone-本地搭建全流程](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529327&idx=1&sn=04f4fe12dc7c5234c8dbdfe35dcc4b72&chksm=f99275becee5fca8737b4cb839fdf5de22fb5cfc146e86d067f32cdc7b6bf8857180da84ea1d#rd) [📖](https://fed.chanceyu.com?id=5b4722723b8501a60760d4d3fc3aa107)
- [【前端早读课】【早阅】一周重写-Next.js:AI-如何造出更快更小的-vinext?](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278624&idx=1&sn=80be0485ee26431b040c99831ee892e0&chksm=bcf95c28d991f67474cce195039d75a5241bd6533bab509e5656b4773967b30e5986571d1f5c&scene=0#rd) [📖](https://fed.chanceyu.com?id=388eb338223f9ea29479fef775b411b5)
- [【前端早读课】【第3656期】深入理解-React-的-useEffectEvent:彻底告别闭包陷阱](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278602&idx=1&sn=a51d047d864a2178ae921057e3c6073a&chksm=bc9272de0be98fb2c805de1009c49533e36ab4372d0ac97fe4a9bf388071940a0ef54221799d&scene=0#rd) [📖](https://fed.chanceyu.com?id=e2896530dc5e769ad2d7c41b340ebe06)
- [查看更多 >](/details/tags/react.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="vue">
Vue
</summary>
<p></p>
> 关键字:`Vue`、`Vuex`、`Pinia`、`Nuxt`、`ElementUI`、`Vant`
- [【前端大全】我天,Vue3-已沦为老二。。](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624136&idx=1&sn=87d16846a10202c62a08e9376a8ff791&chksm=80224509b755cc1ff87c51453ee25722134a185da529c2d13a8835b9b7fa6fa58b895a5998bc#rd) [📖](https://fed.chanceyu.com?id=9a5e2d54acfe4ca96c0b199e39b6a48c)
- [【程序员成长指北】AI-加持的-Vue-3-开发神器](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529404&idx=1&sn=7898b00d5b6b493cd18b8ccf6593fef8&chksm=f992756dcee5fc7b234bab0669f5a91d2b1980ef60c59fc93ef39d572f5b79b10fd290855fc3#rd) [📖](https://fed.chanceyu.com?id=d56d6a3d6679d37c8491ff5444d544d0)
- [【前端从进阶到入院】Vue-Native-最新进展!](http://mp.weixin.qq.com/s/UuX1cI0spiYbZuHkKJ7ltA) [📖](https://fed.chanceyu.com?id=edc814033f32e63879e3d861784d3ae0)
- [【前端从进阶到入院】vue-难受了!solid.js-一个小改变,让-vue-vapor-很尴尬](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517499&idx=1&sn=6c933f1e0cf2c0a78518513fabb9dd23&chksm=eb07bd42dc7034542a7eb89914f2d8545c985a818e1662cdcafb120a175e7fa6072e77b95fad#rd) [📖](https://fed.chanceyu.com?id=8b9d3e6489acd1a1b57b5135ea1419d6)
- [【程序员成长指北】我写了一个超级简单的浏览器插件Vue开发模板](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247528659&idx=1&sn=9e055a98475aa14ba0189323b96e0483&chksm=f9927602cee5ff141c026c9c66ffeb8e4a2d3c848fc8887cff23d25f4b6f69726ef75c82dfdd#rd) [📖](https://fed.chanceyu.com?id=94805289292530ba2ce02a1d6c433624)
- [【前端大全】Vue-3.6-进“养老模式”了吗?一年没大更新,稳得让我有点慌](http://mp.weixin.qq.com/s/LWztYtxPpjK-heK5x3DfUA) [📖](https://fed.chanceyu.com?id=be8b1613d0b5c6bfda0fd916c1f93672)
- [【前端大全】再见了Vue3,前端AI已成气候!](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651623292&idx=1&sn=ca6905ab1eac95d9507c1bbf0c66cb3a&chksm=802258bdb755d1abc7b42695a9599d6a92d25600f93efa71d270b7fce5f3d5aa7adea84ec0ea#rd)
- [【前端大全】我天,Vue3-已沦为老二。。](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651623222&idx=1&sn=d6beb9df545fb84d61b4fec79eb63ce6&chksm=802258f7b755d1e138435b6195c96ba1e0a6f6307a3888061080cb5f22fe6a155f776559419b#rd)
- [【程序员成长指北】10-个提升-Vue3-性能的实用技巧](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247528057&idx=1&sn=9b22a4ddd4da9b1606703a8c1233efe9&chksm=f99268a8cee5e1be7c855e2ac12f41590d3581f5e9ac75a09d0c997b18c3e997db8af3877d31#rd)
- [【前端早读课】【第3602期】fluth-vue:-融合响应式的promise流](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651277690&idx=1&sn=6b53700a4cbdd5a593e6827b222da4c2&chksm=bcc2719b2424bffaf8f08828318cf1780bad9d3c98cce0f00cd06a980c29afedfd64dac38575&scene=0#rd)
- [查看更多 >](/details/tags/vue.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="typescript">
TypeScript
</summary>
<p></p>
> 关键字:`TypeScript`、`TS`、`interface`、`enum`、`tsconfig`、`tsc`、`泛型`、`装饰器`、`类型推断`、`类型守卫`、`联合类型`、`交叉类型`、`映射类型`、`条件类型`
- [【前端大全】Claude-Code-意外泄露-51.2万行-TS-核心代码](http://mp.weixin.qq.com/s/fX7ux-Ob1vM4KroYl4a7DA) [📖](https://fed.chanceyu.com?id=1fa5240fac911e4f9fa05daff20c5b30)
- [【Node-Weekly】How-TypeScript-6.0-affects-Node-developers](https://nodeweekly.com/issues/617) [📖](https://fed.chanceyu.com?id=e55606bacdcabcc0e704fb0b287afa99)
- [【JavaScript-Weekly】TypeScript-6.0,-Next.js-16.2,-and-a-new-Node.js-runtime](https://javascriptweekly.com/issues/778) [📖](https://fed.chanceyu.com?id=d0951a7d0cbc423bd09580c6dc93539d)
- [【Nodejs技术栈】Node.js-发布节奏大变!从此每年只发一个大版本,每个版本都是-LTS](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247522990&idx=1&sn=fee4f843aecff4d3b1c557a289b6cd4c&chksm=e80fd5e0df785cf6f1c2520e8bd913f1f7c9be4d76acd281ce4f2b8920ed184bc16efe0c6c9f#rd) [📖](https://fed.chanceyu.com?id=d2e2d2cec6ea162ff49890f8e41b909f)
- [【前端早读课】Agent-入门书《Hello-Agents》导读](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278731&idx=2&sn=3d6210252a57211aab3d63b95c748296&chksm=bc59d17f198dbb327bb67922735cea50afdecf5aabc781afef4371a9a1ec809dd3a94901e687&scene=0#rd) [📖](https://fed.chanceyu.com?id=c8e8363eb5427eb91739241fb52a506f)
- [【JavaScript-Weekly】TypeScript-6.0-RC-and-Solid-2.0-beta-arrive](https://javascriptweekly.com/issues/776) [📖](https://fed.chanceyu.com?id=7b61aa9e6536635f9f97e85864c97d2c)
- [【程序员成长指北】TypeScript-6.0:迄今最值得关注的变化](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529296&idx=1&sn=0537fbc2930833c213ed81ae06a2e747&chksm=f9927581cee5fc97541fe05a1697bcddf2b60040c71c04c40a8c252c391d1b18a055c7eff1a7#rd) [📖](https://fed.chanceyu.com?id=1e4cd57aeabad3cd373bc9f97a654cd2)
- [【JavaScript-Weekly】Babel-8-RC-Arrives,-Gatsby-Lives,-Lodash-Resets](https://javascriptweekly.com/issues/771) [📖](https://fed.chanceyu.com?id=6b807c8dbdb15b9f73f44d022f2b72df)
- [【前端大全】50万行代码不敢交给AI?TypeScript之父直言:它就像是个“高级复读机”](http://mp.weixin.qq.com/s/wkOTDVd0x4RSfkrw_9ldiA) [📖](https://fed.chanceyu.com?id=a29487b52e38e2014929b92421248795)
- [【程序员成长指北】十条经过实战检验的-TypeScript-monorepo-约定](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247528946&idx=1&sn=2f015af3de78ea7ed6b651445eba100c&chksm=f9927723cee5fe356f24ef1f187966a6a9359bbb799691d2c894e4ec7fcf56a2d376eab233d5#rd) [📖](https://fed.chanceyu.com?id=3bf8a99eacdacd8d15843b788473e17f)
- [查看更多 >](/details/tags/typescript.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="javascript">
JavaScript
</summary>
<p></p>
> 关键字:`JavaScript`、`ECMAScript`、`JS`、`ES6`、`ES7`、`ES8`、`ES9`、`ES10`、`Promise`、`document`、`await`、`async`、`decorator`、`module`、`import`、`catch`、`typeof`、`console`、`setInterval`、`setTimeout`、`Worker`、`Object`、`fetch`、`DataTransfer`、`Array`、`ArrayBuffer`、`DOM`、`H5`、`HTML5`、`Proxy`、`Reflect`、`组件`、`正则`、`数组`、`事件`、`深拷贝`、`数据代理`、`变量`、`函数式`、`声明式`、`异步`、`表单`、`滚动`、`scroll`、`防抖`、`适配`、`路由`、`模块化`、`axios`
- [【Node-Weekly】Node.js-25.9-brings---max-heap-size-and-better,-iterable-streams](https://nodeweekly.com/issues/618) [📖](https://fed.chanceyu.com?id=a0092eef8f955b266308b261ede471c7)
- [【前端从进阶到入院】紧急!Axios-被投毒,3亿项目受到影响!教你怎么自查!](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517566&idx=1&sn=92989a760237d4a09b0b767c33b0406b&chksm=eb07bd07dc70341109910e6c0ad1e3a0b21e242f24b400cd18655b6ae5cf1ab66d70c1a93365#rd) [📖](https://fed.chanceyu.com?id=3c0173f671e84a4e648a2e9f9bd67f24)
- [【前端大全】紧急预警:Axios-供应链被投毒(附排查与紧急补救指南)](http://mp.weixin.qq.com/s/5clC4GUlYIkzDhd_ROVcpw) [📖](https://fed.chanceyu.com?id=b7cd5137bbc85a4fc44d24079287f289)
- [【前端早读课】【第3679期】Worker-线程拯救-Node.js-心跳:Inngest-Connect-架构演进](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278851&idx=1&sn=345f03042da9e8e147067ba61f53d100&chksm=bcc60dfe4387d045b658feb6d099daafe18623fc66183e42aa721ad37cfa667b479c1b68a077&scene=0#rd) [📖](https://fed.chanceyu.com?id=eac84651b97ef33fa50224a1f98a61e3)
- [【前端大全】Node.js核心成员请愿:项目里应禁止AI辅助开发](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624290&idx=1&sn=666bae099de90637678d7f9bf9774341&chksm=802244a3b755cdb5ee6d01f08e7d8ff082b8a7fdf5cc4e5eb902c29b0d448474d415767e3b83#rd) [📖](https://fed.chanceyu.com?id=22a373d7671fb10e7a9990190a93f260)
- [【前端早读课】【第3678期】JavaScript-解析-VIN-码:三种方案全面对比](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278846&idx=1&sn=0d115156c8935e60b834012164ef7800&chksm=bceeca05c71c678764c030775f4d20f2c35ef6630abbaaf57b63d2cf160ee4dd7aca2ec8b20f&scene=0#rd) [📖](https://fed.chanceyu.com?id=bcdcba287995a70e4c3da99c0c0e96ed)
- [【JavaScript-Weekly】A-new,-major-npm-supply-chain-attack-via-Axios](https://javascriptweekly.com/issues/779) [📖](https://fed.chanceyu.com?id=a38f983bf5b0be6c9bef393b01c22eca)
- [【Nodejs技术栈】Next.js-不写给人类了?新版本的四个改动,全是给-AI-Agent-准备的](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523085&idx=1&sn=ea65083747c5e02cb075955ff60794a5&chksm=e80fd443df785d557c1c665080169b63a803afdfe3974a2b2a91305d0e81a25f2ef873f3372c#rd) [📖](https://fed.chanceyu.com?id=a5f0f644afc6d1965c71fd7683a11422)
- [【Nodejs技术栈】一个-19000-行的-PR-引爆了-Node.js-社区,集体请愿禁止-AI-代码](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523079&idx=1&sn=e80d92be76cdc8f2f96138d009218278&chksm=e80fd449df785d5fefd16cd89ae6ec64c327e43844f7b714d366549ad0625d0c6b369f42575a#rd) [📖](https://fed.chanceyu.com?id=2c78cb925a901f326a5d100c3b24fffa)
- [【前端早读课】【第3676期】Node.js-终于有了虚拟文件系统:node:vfs-来了](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278813&idx=1&sn=80c81e8f0c210a87af8d57d50eb75cea&chksm=bc3e9b6577e3c0ccdb6665b94de47c558fb761c9df30bd83777f9048d053c6592c2788804ad7&scene=0#rd) [📖](https://fed.chanceyu.com?id=812473d5e9668d2f7f7b8b84f1c8462e)
- [查看更多 >](/details/tags/javascript.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="nodejs">
NodeJS
</summary>
<p></p>
> 关键字:`Node`、`Node.js`、`Deno`、`Bun`、`Express`、`Koa`、`egg.js`、`pandora.js`、`Puppeteer`、`V8`、`NestJS`、`Fastify`、`Hapi`、`Midway`、`ThinkJS`、`Adonis`、`Strapi`、`Prisma`、`TypeORM`、`Sequelize`、`Mongoose`、`PM2`、`nodemon`、`CommonJS`、`ESM`、`Buffer`、`Stream`、`EventEmitter`、`Cluster`、`事件循环`、`非阻塞`、`异步IO`
- [【Node-Weekly】Node.js-25.9-brings---max-heap-size-and-better,-iterable-streams](https://nodeweekly.com/issues/618) [📖](https://fed.chanceyu.com?id=a0092eef8f955b266308b261ede471c7)
- [【前端早读课】【第3679期】Worker-线程拯救-Node.js-心跳:Inngest-Connect-架构演进](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278851&idx=1&sn=345f03042da9e8e147067ba61f53d100&chksm=bcc60dfe4387d045b658feb6d099daafe18623fc66183e42aa721ad37cfa667b479c1b68a077&scene=0#rd) [📖](https://fed.chanceyu.com?id=eac84651b97ef33fa50224a1f98a61e3)
- [【前端大全】Node.js核心成员请愿:项目里应禁止AI辅助开发](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624290&idx=1&sn=666bae099de90637678d7f9bf9774341&chksm=802244a3b755cdb5ee6d01f08e7d8ff082b8a7fdf5cc4e5eb902c29b0d448474d415767e3b83#rd) [📖](https://fed.chanceyu.com?id=22a373d7671fb10e7a9990190a93f260)
- [【Nodejs技术栈】一个-19000-行的-PR-引爆了-Node.js-社区,集体请愿禁止-AI-代码](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523079&idx=1&sn=e80d92be76cdc8f2f96138d009218278&chksm=e80fd449df785d5fefd16cd89ae6ec64c327e43844f7b714d366549ad0625d0c6b369f42575a#rd) [📖](https://fed.chanceyu.com?id=2c78cb925a901f326a5d100c3b24fffa)
- [【Node-Weekly】How-TypeScript-6.0-affects-Node-developers](https://nodeweekly.com/issues/617) [📖](https://fed.chanceyu.com?id=e55606bacdcabcc0e704fb0b287afa99)
- [【前端早读课】【第3676期】Node.js-终于有了虚拟文件系统:node:vfs-来了](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278813&idx=1&sn=80c81e8f0c210a87af8d57d50eb75cea&chksm=bc3e9b6577e3c0ccdb6665b94de47c558fb761c9df30bd83777f9048d053c6592c2788804ad7&scene=0#rd) [📖](https://fed.chanceyu.com?id=812473d5e9668d2f7f7b8b84f1c8462e)
- [【JavaScript-Weekly】TypeScript-6.0,-Next.js-16.2,-and-a-new-Node.js-runtime](https://javascriptweekly.com/issues/778) [📖](https://fed.chanceyu.com?id=d0951a7d0cbc423bd09580c6dc93539d)
- [【程序员成长指北】Node.js-宣布了重大发布调整](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529469&idx=1&sn=c6836110923971eeda1d9516731eec3f&chksm=f992752ccee5fc3af15d3747575db160087a8d46e940b0655966c0a7de9d47c683e199ea44ec#rd) [📖](https://fed.chanceyu.com?id=280bc968c57237b25fdb721ca68fff27)
- [【Node-Weekly】Petition-calls-for-ban-on-AI-generated-code-in-Node.js-core](https://nodeweekly.com/issues/616) [📖](https://fed.chanceyu.com?id=8304d5c228ab03ce9e37e82e6708c689)
- [【Nodejs技术栈】酷!Node.js-终于支持虚拟文件系统了,作者直言:没有-Claude-Code-这不可能发生](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523001&idx=1&sn=3179cde6adc1d1a7a75098696090c6c4&chksm=e80fd5f7df785ce120efa401bdf11ed5cd2f27bc047ef6c4eba442a6617f324a9fce89bd760f#rd) [📖](https://fed.chanceyu.com?id=c8542333b149b5bcf1f2fa92469df451)
- [查看更多 >](/details/tags/nodejs.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="css">
CSS
</summary>
<p></p>
> 关键字:`CSS`、`Sass`、`SCSS`、`Less`、`Stylus`、`PostCSS`、`Tailwind`、`transform`、`transition`、`animation`、`居中`、`动画`、`特效`、`布局`、`响应式`、`媒体查询`、`伪类`、`伪元素`、`选择器`、`盒模型`、`BFC`、`层叠上下文`、`渐变`、`阴影`、`滤镜`、`混合模式`、`变量`、`自定义属性`、`calc`
- [【Nodejs技术栈】GitHub-Star-暴涨!前-React-核心成员出手,把浏览器-30-年算不好的文字布局问题解决了](http://mp.weixin.qq.com/s/ibVkVGk62j3kCPO_wxBYlg) [📖](https://fed.chanceyu.com?id=f17f69647c6d7eb365318f05d053617f)
- [【前端从进阶到入院】Pretext:前端文本布局的下一个范式转变](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517564&idx=1&sn=2cd94053d488764d59c532d486d3ad2c&chksm=eb07bd05dc703413e05333028b748e1e4f0f3624682de5898deba9a50dfd447c806fc0451991#rd) [📖](https://fed.chanceyu.com?id=c021bab3277c43008ce85dc8fcfcfb74)
- [【张鑫旭-鑫空间-鑫生活】CSS-corner-shape与背景底纹技术](https://www.zhangxinxu.com/wordpress/2026/03/css-corner-shape-background-pattern/) [📖](https://fed.chanceyu.com?id=1e8e5062081b938e9e4df67bf38cc395)
- [【前端早读课】【第3672期】CSS-迎来原生随机能力:random-与-random-item-初探](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278766&idx=1&sn=f12dfaf066546ca3b50d6cd4d9462fcd&chksm=bcdcdd4b365f2e4cb85cb53e213515970d946d9f9fad8114ec27121d1b4163008ee2aa50a97c&scene=0#rd) [📖](https://fed.chanceyu.com?id=3a1da667d87bc95250c84b03b23faaab)
- [【前端早读课】【第3661期】不用-JS-也能精准定位?CSS-Anchor-Positioning-实战解析](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278643&idx=1&sn=7161d1df5a6028408bd1968b3c91308c&chksm=bcfb969acf5ac5efdc7dec3205f2962a47dc76cd023f50fe961ac2a391654dcaa7e618a05aa5&scene=0#rd) [📖](https://fed.chanceyu.com?id=61471213fef239bbf0b4a395bf72e4d8)
- [【张鑫旭-鑫空间-鑫生活】CSS-text-box属性又是干嘛用的?](https://www.zhangxinxu.com/wordpress/2026/02/css-text-box/) [📖](https://fed.chanceyu.com?id=10034adbb21fc099e6c8e73efcf6be15)
- [【前端早读课】【第3654期】不再重置!在-React-/-Next.js-中实现跨页面“持续进化”的动画效果](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278586&idx=1&sn=3cfb81a18bdbbe2a205a4a70f3d3b6ae&chksm=bc10b91f6f40078b03db48e1e4e12d8c0dfec8c89a16dd5d8117705b7d6afb7ac22146c2efe3&scene=0#rd) [📖](https://fed.chanceyu.com?id=e8a73a87b97a7c296716fbbe85ae2456)
- [【前端大全】别再像新手一样使用-Tailwind-了!](http://mp.weixin.qq.com/s/Z0iy-a_9B1gX0SvNWWRdfg) [📖](https://fed.chanceyu.com?id=cc69335def1cc26842d22bd7841ebf3e)
- [【程序员成长指北】CSS-技巧:完美居中任何元素(不再烦恼!)](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529104&idx=1&sn=18e6d1f2bf7497aebbe262f228e05e54&chksm=f9927441cee5fd57330c27427336898c1ae182576edb1abbb1fc04417dfd9d3768a4a7adf9f5#rd) [📖](https://fed.chanceyu.com?id=d36342c3013625b810de71e3170b731a)
- [【程序员成长指北】别再像新手一样使用-Tailwind-了!](http://mp.weixin.qq.com/s/ikIFRP19KJZJFu78_eqwrw) [📖](https://fed.chanceyu.com?id=dd11cf44c8d18f0a9ca534cba65a465e)
- [查看更多 >](/details/tags/css.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="html">
HTML
</summary>
<p></p>
> 关键字:`HTML`、`HTML5`、`标签`、`meta`、`语义化`、`SEO`、`accessibility`、`无障碍`、`ARIA`、`canvas`、`video`、`audio`、`iframe`
- [【张鑫旭-鑫空间-鑫生活】HTML-dialog元素新支持了closedBy属性](https://www.zhangxinxu.com/wordpress/2026/03/html-dialog-closedby/) [📖](https://fed.chanceyu.com?id=9f9bb84eeb91885069a4c7e57a58b7cc)
- [【张鑫旭-鑫空间-鑫生活】HTML-interestfor属性与悬停popover交互效果](https://www.zhangxinxu.com/wordpress/2026/03/css-interestfor-invoker-target-source/) [📖](https://fed.chanceyu.com?id=822c0f3fd53f1b05b135738d40620e01)
- [【程序员成长指北】前端指纹技术是如何实现的?(Canvas、Audio、硬件API-核心原理解密)](http://mp.weixin.qq.com/s/IkxN7GkS5zgCH2LX5IHbsA) [📖](https://fed.chanceyu.com?id=96d3cf561da87fe6ff40c460cf1e47ba)
- [【淘系前端团队】AI-无障碍-CR-与自动适配实践:从问题洞察到效果验证](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650542406&idx=1&sn=c89b4adb5deec25be5dc4ea1a457135a&chksm=8390d95eb4e750482af226d6188dc30f517e75f23da16eb97be227d774d61a4ff201d402b8aa#rd) [📖](https://fed.chanceyu.com?id=0ed2045e03c011ac332f9f6ffcd5654e)
- [【前端从进阶到入院】Chrome-新-API:仅-6-行-HTML!页面秒开!](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517507&idx=1&sn=a6f4ad2cc56005263c666f6dbce84219&chksm=eb07bd3adc70342cbf1c3b729fdbf1d623262f31a883d3c6f9d62eb9648b879f68c696f97e26#rd) [📖](https://fed.chanceyu.com?id=7a2a483828d3582bded0c9a99428b6e5)
- [【程序员成长指北】浅谈-import.meta.env-和-process.env-的区别](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247528897&idx=1&sn=e2376b422982b6d06d72409e12feffde&chksm=f9927710cee5fe0698d7fad3b3474af024d6f52e994b5ccbb0dc301ae5ea2c4cf3366d476c33#rd) [📖](https://fed.chanceyu.com?id=967c27ada9a16ca702dbce7d202010a4)
- [【前端早读课】【第3637期】跨浏览器-Canvas-图像解码终极方案:让大图渲染也能丝滑不卡顿](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278364&idx=1&sn=0bbaa9b16eda03067396f840cdf1da4a&chksm=bc6ab77705a66d85ec463c3a50798c10f00e167a5ba7b7c9c722601a18c76d179c80f35e1ae2&scene=0#rd) [📖](https://fed.chanceyu.com?id=2ff39535b8c26628c9bff92be6871d4c)
- [【前端大全】一种新HTML页面转换成-PDF-技术方案](http://mp.weixin.qq.com/s/IgttNByrz1_3ESzIY0Y4mQ) [📖](https://fed.chanceyu.com?id=5f692b8b813a9acefc55a39d09a3e74d)
- [【JavaScript-Weekly】Building-JavaScript-tools-in-a-single-HTML-file](https://javascriptweekly.com/issues/765) [📖](https://fed.chanceyu.com?id=a868a823b965435cdfc962b4ac7605da)
- [【前端大全】HTML-的隐藏宝藏:-标签](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651623494&idx=2&sn=36dd6338dd0696f95fce0603d8a6f5d5&chksm=80224787b755ce91e88f1eedc8b3dc64fbf83819f97e037bff6ca8ffbd76f6138c510d8fd2b1#rd) [📖](https://fed.chanceyu.com?id=513427de9620f48c6c23bf81932935b9)
- [查看更多 >](/details/tags/html.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="构建打包">
构建打包
</summary>
<p></p>
> 关键字:`Webpack`、`Lerna`、`Rollup`、`Vite`、`esbuild`、`SWC`、`Turbopack`、`Rspack`、`NPM`、`NPX`、`Yarn`、`pnpm`、`Gulp`、`Grunt`、`Babel`、`ESLint`、`Prettier`、`TSLint`、`Polyfill`、`Jenkins`、`构建`、`打包`、`编译`、`转译`、`懒加载`、`预加载`、`HMR`、`热更新`、`monorepo`、`workspace`、`lerna`、`turborepo`
- [【JavaScript-Weekly】A-new,-major-npm-supply-chain-attack-via-Axios](https://javascriptweekly.com/issues/779) [📖](https://fed.chanceyu.com?id=a38f983bf5b0be6c9bef393b01c22eca)
- [【前端从进阶到入院】刚刚,Vite-两个新工具发布!Webpack-完!](http://mp.weixin.qq.com/s/kRAX2YZ7Kw9r5Wr4j6B_rw) [📖](https://fed.chanceyu.com?id=2235194d632f5ccd6d92556e4123dca2)
- [【程序员成长指北】别再纠结格式:5-种设计模式,构建真正可靠的-AI-Agent](http://mp.weixin.qq.com/s/hEBK6mHO5nM5Sm8MWEmQWQ) [📖](https://fed.chanceyu.com?id=3c42bde4e9133a33914dcc292053b348)
- [【前端早读课】【第3675期】别再纠结格式:5-种设计模式,构建真正可靠的-AI-Agent](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278808&idx=1&sn=cb062b1747e889741fc1ad1e42b7c225&chksm=bc82f8ebecd3a413e6a8963b5fc3378d07daac7e332cfb66b561307cfcea47b174050ae3c627&scene=0#rd) [📖](https://fed.chanceyu.com?id=48eb58d9afd8932362d9060125fd08d3)
- [【Nodejs技术栈】尤雨溪转发庆祝,Claude.ai-抛弃-SSR-拥抱-Vite,性能飙升!](http://mp.weixin.qq.com/s/e4KkP-hepeBqhhaIN9IiQg) [📖](https://fed.chanceyu.com?id=5d8328ad201c64d610e707ab0a9ee4ae)
- [【JavaScript-Weekly】It’s-about-time:-Temporal-advances,-Vite-accelerates](https://javascriptweekly.com/issues/777) [📖](https://fed.chanceyu.com?id=faf433affe522ee3f47ca53649f0423a)
- [【Nodejs技术栈】硬刚-Next.js!尤雨溪隆重推出-Void,Vite-生态终于有自己的"Vercel"了](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247522878&idx=1&sn=3d8fcf18a18ab30c35aeca4d4d0789fc&chksm=e80fd570df785c66f45596f8c329a91fc3a562acd31551149195c027ccaa38278df32aa9ccb7#rd) [📖](https://fed.chanceyu.com?id=a17e0ef80e109ad39c0e2cd56e4f01b2)
- [【Nodejs技术栈】重磅!尤雨溪宣布-Vite+-开源,所有人免费用,这次前端工具链真要大一统了](http://mp.weixin.qq.com/s/R0bDm03YyV2HycJRmuXRsg) [📖](https://fed.chanceyu.com?id=c15d6501c0e89f7ba3a9aaca59d467fa)
- [【Nodejs技术栈】Vite-8-正式发布!架构彻底推翻,构建从-46-秒干到-6-秒,前端工具链变天了](http://mp.weixin.qq.com/s/_D1rfO9HgKhs-ApOEWDVUg) [📖](https://fed.chanceyu.com?id=9ef8129deaadb6642fe95cce1a28058b)
- [【前端从进阶到入院】刚刚,尤雨溪宣布正式推出Vite-Plus,竟不收费?](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517558&idx=1&sn=6f9b01aa83df8ec54d293cda8da71bce&chksm=eb07bd0fdc7034198e0243735dcb1cf228975f48e68770f0885d650b785e0dea6738ab9d10e8#rd) [📖](https://fed.chanceyu.com?id=0471c93eac7a646b8a19f18319c38046)
- [查看更多 >](/details/tags/pack-build.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="小程序">
小程序
</summary>
<p></p>
> 关键字:`小程序`、`Taro`、`Uni-app`、`MPVue`、`Wepy`、`Chameleon`、`Remax`、`Kbone`
- [【前端早读课】【第3584期】微信小程序端智能项目工程化实践](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651277365&idx=1&sn=1892eb28b56af9a02e0704a3c7bc6987&chksm=bcd7a6e4cc8474ff2b89915305125fd6bb2fff1ffb5917dff6816961ce73f58324fcdece8a66&scene=0#rd) [📖](https://fed.chanceyu.com?id=56b82ad022d96a26c083954e06b59eb8)
- [【前端早读课】【第3535期】滴滴开源新项目Dimina星河,打造灵活轻量的小程序框架](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651276774&idx=1&sn=da2be17301bd3f4658e5e30bd23c5f68&chksm=bcda12c171720dbd833e2b629e4bc21521ca890e1251eb13c73f50fb84feac29b8b801f8218a&scene=0#rd) [📖](https://fed.chanceyu.com?id=7bc015689b352be08eab3bc754d840ae)
- [【程序员成长指北】怎么办?微信小程序主包又双叒叕不够用了!!!](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247526680&idx=2&sn=14d4f4052783ca4054e3839ab3fa4c39&chksm=f9926fc9cee5e6dfa0c96ae64dc092d840dca2a91e1c359be896f8e35844c7f65ea0f4232171#rd) [📖](https://fed.chanceyu.com?id=428ad84a430c8650184a179ce60875e5)
- [【程序员成长指北】撸一个小程序运行容器](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247526240&idx=1&sn=cb2266238ed296f8b3910f627e042800&chksm=f99261b1cee5e8a71a1b0478c806a38028e4fc906acff8d8ce8cad3f2a4fca1f6c8c47ae1708#rd)
- [【程序员成长指北】小程序页面跳转如何优雅携带大数据Array或Object](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247524787&idx=2&sn=b35f9f1240a930a4521b211b68e7a9ab&chksm=f9926762cee5ee74393197b25cedfc59bf659a665ead82c6cf99a8d573631c5b367cbf3c46f1#rd)
- [【前端早读课】【第3182期】在微信小程序里运行完整的-Flutter,怎么做到的?](https://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651268969&idx=1&sn=17cc541d980b8174b6adbda53b17452e&chksm=bd48feed8a3f77fb291686ae91ba947f79112d60ad0499f476c33ab9e2a1c300eb6f7887dca9#rd)
- [【前端早读课】【第3178期】使用-Taro-开发鸿蒙原生应用-——-快速上手,鸿蒙应用开发指南](https://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651268877&idx=1&sn=cec30208dfa165de88eeb5521ef5dff6&chksm=bd48fe898a3f779f92f7a08a03503fb6fd54c30867faaac2ee099e4b897d092e2f12179d848b#rd)
- [【前端早读课】【招聘】Taro-团队招人啦!](https://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651268278&idx=2&sn=901c867c8941353d1e30a1414f44fa1e&chksm=bd48fd328a3f74246dee8da55f3b1d260b4f914fd1d6283898601682026eed55096126825443#rd)
- [【前端早读课】【第3145期】使用-Taro-开发鸿蒙原生应用-——-探秘适配鸿蒙-ArkTS-的工作原理](https://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651268067&idx=1&sn=7b5c3dda59627e2f8d9eb946bee82b31&chksm=bd48fa678a3f7371a0365009a6271c6fee04314fb1c69c0c88c455737b23d6cbfe9eb981d271#rd)
- [【前端早读课】【第3142期】使用-Taro-开发鸿蒙原生应用-——-当-Taro-遇到纯血鸿蒙](https://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651267971&idx=1&sn=33b78daaa2923f53bc385c5091416e37&chksm=bd48fa078a3f7311ae127b0f4214d3db0e9a72c8226a4c7dd1ece893b006347e6218c46681a0#rd)
- [查看更多 >](/details/tags/miniprogram.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="手机应用">
手机应用
</summary>
<p></p>
> 关键字:`Flutter`、`Dart`、`ReactNative`、`Native`、`Android`、`Kotlin`、`Java`、`iOS`、`Swift`、`Objective-C`、`鸿蒙`、`HarmonyOS`、`ArkTS`、`手机应用`、`手机APP`、`移动端`、`移动开发`、`SwiftUI`、`UIKit`、`Expo`、`Ionic`、`Cordova`、`原生开发`、`混合开发`、`跨平台`
- [【前端从进阶到入院】Vue-Native-最新进展!](http://mp.weixin.qq.com/s/UuX1cI0spiYbZuHkKJ7ltA) [📖](https://fed.chanceyu.com?id=edc814033f32e63879e3d861784d3ae0)
- [【阮一峰的网络日志】AI-native-Workspace-也许是智能体的下一阶段](http://www.ruanyifeng.com/blog/2026/01/ai-native-workspace.html) [📖](https://fed.chanceyu.com?id=98cb285d3f906d24de258484860e76af)
- [【淘系前端团队】Jimi:打造Java程序员专属的开源ClaudeCode](http://mp.weixin.qq.com/s/a6zOCKaBey0IQnywOd4xWg) [📖](https://fed.chanceyu.com?id=c792f9d96a8f4dc858ca22b59dffdab5)
- [【前端早读课】【早阅】借助-Expo-Router,实现跨平台无缝部署](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651277624&idx=1&sn=fa0a2f25bf736f78891cf95b5e3df28c&chksm=bc4ceab6673ec09d12e6e25bfcd68b1bdb76933428d1133286f3ff32a5d502c8783e1c52f4af&scene=0#rd)
- [【前端侦探】Safari又出bug了?临时修复-iOS-26弹出键盘后定位错乱问题](http://mp.weixin.qq.com/s/rWmsDUdhRvp8KlPBhvJZaQ)
- [【前端之巅】XTransfer-开源-XRN-框架:基于-React-Native-的跨三端开发实践与鸿蒙-NEXT-攻坚](http://mp.weixin.qq.com/s/uCbEeWjY4lbZJTM7XVUqIA)
- [【淘系前端团队】MNN-LLM-Chat-iOS-流式输出优化实践](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650541282&idx=1&sn=c7e80789f8a1e4ebaebc8e34c55a9829&chksm=821fece97b1977f767eaf3c6395a452a2bbed66df7fa30cec3f08ce529a3c7391b9881ba5d45&scene=0#rd)
- [【淘系前端团队】面向互联网2C业务的分布式类Manus-Java框架](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650540954&idx=1&sn=c410728ed2bbd65603b02ee1130089ef&chksm=829c9cfe6a13ca67fd442292eb98b03d686550f0138892fd0a610b7f62905a3740345a8589f5&scene=0#rd)
- [【前端之巅】苹果12年首次大改UI,还炮轰“跨平台”框架!“液态玻璃”会是Flutter开发者的“至暗时刻”吗?](http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247526083&idx=1&sn=1af1bec8b7c437583c7ac41b77f5ffb1&chksm=f952d380ce255a969c6c107d754c8b9350aca5e780561b16e5cdfb0386bb0ec0cb9ebd7de54e#rd)
- [【前端大全】12年首次大改!真有人喜欢苹果的“液态玻璃”吗?至少Flutter-开发者的噩梦开始了](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651622608&idx=1&sn=338b635ee345e515f14f14b2a9ebd9ad&chksm=80225b11b755d20778d12ae0f60ad29e2f66f45a6cbb9835b3de04a18637770d199ff7911071#rd)
- [查看更多 >](/details/tags/dev-mobile.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="桌面应用">
桌面应用
</summary>
<p></p>
> 关键字:`Electron`、`NW.js`、`Tauri`、`桌面应用`、`桌面开发`、`跨平台`、`Windows`、`macOS`、`Linux`、`CEF`、`Qt`、`GTK`、`WPF`、`WinUI`、`AppKit`、`Cocoa`
- [【前端之巅】Meta-用一个烂-Web-应用替换原生-WhatsApp:Windows-用户活该将就?](http://mp.weixin.qq.com/s/Z4hMbWBazYbm8Ua45pb5Rw) [📖](https://fed.chanceyu.com?id=32eb586eafeb6eddaae1b0392c1c1ae0)
- [【前端早读课】【早阅】借助-Expo-Router,实现跨平台无缝部署](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651277624&idx=1&sn=fa0a2f25bf736f78891cf95b5e3df28c&chksm=bc4ceab6673ec09d12e6e25bfcd68b1bdb76933428d1133286f3ff32a5d502c8783e1c52f4af&scene=0#rd)
- [【前端从进阶到入院】尤雨溪:下一代-Vite!Windows-快-29%!Mac-快-45%!](http://mp.weixin.qq.com/s/dfJE-yVYdqaUI98HchPOqw)
- [【程序员成长指北】再也不用背命令!Cursor-终端-Cmd+K-=-你的专属-Linux-&-Git-智能助手](http://mp.weixin.qq.com/s/k1A7W0QwfqGZMFgfU50e_Q)
- [【前端之巅】16-年-macOS-程序员用-Claude-Code-搞副业:我只手敲了-1000-行,剩下-95%-代码靠自动生成](http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247526154&idx=1&sn=4b90107b67f10fea7bdc3fc38bcb03b6&chksm=f952d249ce255b5fb47b41b908f8a595e2940dd676003cf0a474a75cbe3f4319dbb6dd293512#rd)
- [【前端大全】Tauri-vs.-Electron:性能、体积与真实权衡](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651622639&idx=2&sn=34b9a33faaf10199f6b958c9e9cc24f3&chksm=80225b2eb755d238651e13c2e7461621aeff91aeb517f37f0d6f3688cedd04230cf9bcbf7698#rd) [📖](https://fed.chanceyu.com?id=4ad1afc8cd1f4bbe117cc4b3de594e7c)
- [【前端之巅】苹果12年首次大改UI,还炮轰“跨平台”框架!“液态玻璃”会是Flutter开发者的“至暗时刻”吗?](http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247526083&idx=1&sn=1af1bec8b7c437583c7ac41b77f5ffb1&chksm=f952d380ce255a969c6c107d754c8b9350aca5e780561b16e5cdfb0386bb0ec0cb9ebd7de54e#rd)
- [【前端早读课】【第3520期】Slack、Notion-和-VSCode-提升-Electron-应用性能的-6-种方法](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651276596&idx=1&sn=6fa52c16f7a0dac4f20c2a7936bcde79&chksm=bc10878faf652af3bf83372917403c55c6ed371a202f0020e9e1389b01d0cf78365d51327554&scene=0#rd) [📖](https://fed.chanceyu.com?id=88ea03b74e53153900d07293b6582ce2)
- [【前端早读课】【第3508期】FunProxy---使用-Rust-构建跨平台全链路测试抓包代理工具](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651276415&idx=1&sn=cb9e09054ffb312debb0041744653b0c&chksm=bc84ab0fdb0598b0c31390f180a0e8a94291d6e05a92ec23040167cdeb2044086ec2ca3422b3&scene=0#rd)
- [【程序员成长指北】Tauri-vs.-Electron:性能、体积与真实权衡](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247526089&idx=1&sn=5724d9630ea2caf402b25eb5c2d75c96&chksm=f9926018cee5e90ec675bb678c336924aa66da467bb92a9f59fd873fb922874440ece515f990#rd)
- [查看更多 >](/details/tags/dev-desktop.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="浏览器">
浏览器
</summary>
<p></p>
> 关键字:`Chrome`、`Chromium`、`IE`、`Firefox`、`Safari`、`Webkit`、`Blink`、`Edge`、`Opera`、`浏览器`、`CORS`、`CORB`、`COEP`、`COOP`、`CSP`、`XSS`、`CSRF`、`DevTools`、`跨域`、`状态码`、`重绘`、`重排`、`回流`、`PWA`
- [【Nodejs技术栈】GitHub-Star-暴涨!前-React-核心成员出手,把浏览器-30-年算不好的文字布局问题解决了](http://mp.weixin.qq.com/s/ibVkVGk62j3kCPO_wxBYlg) [📖](https://fed.chanceyu.com?id=f17f69647c6d7eb365318f05d053617f)
- [【前端从进阶到入院】重磅!Vite-DevTools-发布!功能一览!](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517554&idx=1&sn=d5a1a3391bf0336d7efc8f592d0d39bb&chksm=eb07bd0bdc70341d241360d2cb7b07599a5da1a0aaaa6826824abdfbb536287cfd4321260e5c#rd) [📖](https://fed.chanceyu.com?id=749d90fa5286e50273b5668ed1eac610)
- [【前端大全】Code-Review-将死于-2026-年](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624094&idx=1&sn=6d1b600f1d9b4f6b83fb7dbb98908b99&chksm=8022455fb755cc4920a8bd82b7f85de214177ad33d7fc139eb6331432a93486c28d7237ca807#rd) [📖](https://fed.chanceyu.com?id=28d7f0bdc6b8d7d4f216df7d3a0c0f0f)
- [【Node-Weekly】AdonisJS-v7-brings-batteries-included-framework-upgrades](https://nodeweekly.com/issues/613) [📖](https://fed.chanceyu.com?id=ccf8b8c331b88f29e0df7da443c3d11d)
- [【JavaScript-Weekly】Oxfmt-beta:-30x-faster-than-Prettier,-100%-compatible](https://javascriptweekly.com/issues/774) [📖](https://fed.chanceyu.com?id=050fad08bd23e55077593ec5761eac91)
- [【前端早读课】【第3655期】浏览器即运行时:一个零后端-AI-应用的前端架构实践](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278596&idx=1&sn=dea55dba00065e03f83445a08be9f33e&chksm=bc55038babe13da28ece499bc7c4e5f6365b481fdd22115746df39fd362cd740d8454d164bee&scene=0#rd) [📖](https://fed.chanceyu.com?id=c7e51c1463946c6de508c85ab31d766d)
- [【前端大全】谷歌Chrome觉醒!Gemini-3全面接管,38亿用户一夜进入Agent时代](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624053&idx=2&sn=37c808ca69f24f963618f33854406d88&chksm=802245b4b755cca2e0f14551796f3dab8fc46168187f21ef92086124a6da4aa7565de690207e#rd) [📖](https://fed.chanceyu.com?id=f19826c344ddc5ca5a2cb79a257b9438)
- [【前端之巅】烧掉数万亿-Token、数百-Agent-连跑一周:Cursor“从零写浏览器”,结果是拼装人类代码?](http://mp.weixin.qq.com/s/aXhDQk7Wxa6rZg4ms0I3Tg) [📖](https://fed.chanceyu.com?id=e8badfdbff6ecb2012bc76ee02cc7afd)
- [【程序员成长指北】Chrome-终于可以限制单个网络请求了!](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247528972&idx=1&sn=1add66f52a03aa22a56459af50ca391b&chksm=f99274ddcee5fdcbd6654bef1254568407938e54ac4089ee3aa3f9132bee07d9c5a31ed00294#rd) [📖](https://fed.chanceyu.com?id=f61c59b72aed850e6d65f424c394ebfa)
- [【前端大全】Cursor一夜翻车,AI-300万代码写浏览器被打假!全网群嘲「AI泔水」](http://mp.weixin.qq.com/s/3cFHuP9UZRkEIBe5X3rBCw) [📖](https://fed.chanceyu.com?id=3e0887dd3467c4526c445d2237894eb7)
- [查看更多 >](/details/tags/browser.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="游戏开发">
游戏开发
</summary>
<p></p>
> 关键字:`游戏`、`Three.js`、`Create.js`、`Matter.js`、`Phaser`、`PixiJS`、`Babylon.js`、`PlayCanvas`、`Cocos`、`Unity`、`Unreal`、`Godot`、`Laya`、`LayaBox`、`Egret`、`白鹭`、`WebGL`、`WebGPU`、`物理引擎`、`碰撞检测`、`粒子系统`、`骨骼动画`
- [【前端大全】员工一律禁用AI!50年老牌游戏公司下发最严禁令](http://mp.weixin.qq.com/s/zzG0u5XiRYOn5I-8sGyB7g) [📖](https://fed.chanceyu.com?id=983d3332c9ef8f211906ab255923660d)
- [【前端大全】我写出了-Threejs-版城市天际线?!](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651623197&idx=2&sn=cbf51fa66ef70b4083aeda9acee3ffdc&chksm=802258dcb755d1ca8074efe0f0e80ae9dd1cf7ea172b111e0a04d1b60dabb6a6027bc7f8759e#rd)
- [【前端早读课】【第3594期】拥抱新一代-Web-3D-引擎,Three.js-项目快速升级-Galacean-指南](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651277508&idx=1&sn=c3bf6d777fa78916a2def84141deeea5&chksm=bc435a6772de5b27066850c713fd01830189809ff74015308d7fe6c84f10df04823f9095fab9&scene=0#rd)
- [【前端大全】苹果炮轰推理模型全是假思考!4个游戏戳破神话,o3/DeepSeek高难度全崩溃](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651622594&idx=1&sn=23fe1edd76ee3d9483575903761f95d4&chksm=80225b03b755d2155ace1325039f34f8c167885b601a89ab395033065fd38e8d2251e59dc620#rd)
- [【淘系前端团队】2025淘宝春晚互动小游戏技术方案揭秘](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650539808&idx=1&sn=dff0ea411852ebbbb92108a2a3ae4d7b&chksm=8390cf38b4e7462e4816d3da3450c399dbc47ed5c701b83aee29b6c5e89f0edf66611eaba32a#rd)
- [【前端大全】老板花一万大洋让我写的艺术工作室官网?!-HeroSection-再度来袭!Three.js](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651622265&idx=1&sn=bb736dcb1d0656d847c9d85c76377b7b&chksm=80225cb8b755d5ae8a035a52e74a3c6e0dbc1c223d7501d7468da38854f63ea31e569ddbee82#rd)
- [【前端早读课】【第3477期】基于three.js的虚拟人阴影渲染优化方案](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651276062&idx=1&sn=3587469f07550ed878101b65b85c6ef1&chksm=bca9bbe8b6c4ea29dc28f446cc3d7c3bbce5fca76df57482c4b01b2588eeee5946ff8bb203b0&scene=0#rd)
- [【程序员成长指北】基于three.js的虚拟人阴影渲染优化方案](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247525585&idx=2&sn=8f40b6f9f5c5e10de2ce67129dbd7a3d&chksm=f9926200cee5eb1651cc4c08d3d5f166d8a7d1961022da8c41e16c42fa421235619af15d7fab#rd)
- [【程序员成长指北】用Three.js搞个炫酷风场图](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247524571&idx=1&sn=8ea78c7ca6679f73b8f308f137fdbef1&chksm=f992660acee5ef1cfde89358f6fe2d8e88996edbf5e5d8bb2436d98aec9e6c65c4d4a7763289#rd)
- [【字节前端-ByteFE】2024-抖音欢笑中国年五:Wasm、WebGL-在互动技术中的创新应用](http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247505746&idx=1&sn=68227cec19ed79ad5ba192025300ce09&chksm=cea96053f9dee9453c281bd6923194da6781c4834c8ef6f876f7d1b48528c2ed7f4e8616c62a#rd)
- [查看更多 >](/details/tags/dev-game.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="图形图像">
图形图像
</summary>
<p></p>
> 关键字:`Canvas`、`SVG`、`WebGL`、`WebGPU`、`2D`、`3D`、`PNG`、`JPEG`、`JPG`、`GIF`、`WebP`、`AVIF`、`HEIC`、`RGB`、`RGBA`、`HSL`、`GUI`、`图形`、`图像`、`绘制`、`渲染`、`滤镜`、`变换`、`矩阵`、`向量`、`着色器`、`Shader`、`纹理`、`材质`、`光照`、`阴影`、`抗锯齿`、`帧缓冲`、`离屏渲染`、`图片处理`、`图片压缩`、`图片裁剪`、`水印`、`ImageData`
- [【前端大全】前端圈沸腾!Claude造出15KB引擎,渲染狂飙1200倍:文字里能跑马里奥](http://mp.weixin.qq.com/s/vlhTJXe8JiHD7si24Kwszw) [📖](https://fed.chanceyu.com?id=d2b16fc47e976a71a3705cdb4cbf7885)
- [【前端从进阶到入院】刚刚,Vite-两个新工具发布!Webpack-完!](http://mp.weixin.qq.com/s/kRAX2YZ7Kw9r5Wr4j6B_rw) [📖](https://fed.chanceyu.com?id=2235194d632f5ccd6d92556e4123dca2)
- [【前端早读课】【第3667期】React-Fiber-原理解析:从递归渲染到可中断调度](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278693&idx=1&sn=d28febc993118336805e8e800732415f&chksm=bc46f874bf464ddf092000d682657d2a75395fdec127c6d16151410592aa79003a12342ae2fd&scene=0#rd) [📖](https://fed.chanceyu.com?id=be1cd0faf3780bde25f0305b305c2600)
- [【前端从进阶到入院】刚刚,Webpack-官宣-2026-年大动作!](http://mp.weixin.qq.com/s/UkgAvcj6B9-eUQc880rf4g) [📖](https://fed.chanceyu.com?id=6728388e9d0c549ddf11740685752a82)
- [【前端从进阶到入院】刚刚,Webpack-官宣-2026-年大动作!](http://mp.weixin.qq.com/s/X6eo9jKJsb3mP4u56wkXnQ) [📖](https://fed.chanceyu.com?id=6f08809af23a3683c36a0f0826510f6e)
- [【程序员成长指北】前端指纹技术是如何实现的?(Canvas、Audio、硬件API-核心原理解密)](http://mp.weixin.qq.com/s/IkxN7GkS5zgCH2LX5IHbsA) [📖](https://fed.chanceyu.com?id=96d3cf561da87fe6ff40c460cf1e47ba)
- [【Node-Weekly】A-new-guide-to-configuring-Node-packages](https://nodeweekly.com/issues/607) [📖](https://fed.chanceyu.com?id=cc60a67f3bd7d952bacd2b063e489b7e)
- [【前端早读课】【第3637期】跨浏览器-Canvas-图像解码终极方案:让大图渲染也能丝滑不卡顿](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278364&idx=1&sn=0bbaa9b16eda03067396f840cdf1da4a&chksm=bc6ab77705a66d85ec463c3a50798c10f00e167a5ba7b7c9c722601a18c76d179c80f35e1ae2&scene=0#rd) [📖](https://fed.chanceyu.com?id=2ff39535b8c26628c9bff92be6871d4c)
- [【程序员成长指北】公司-React-应用感觉很慢,我把没必要的重复渲染砍掉了-40%!](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247528820&idx=1&sn=3df4c71c941a7b1670226aabf7c2ee64&chksm=f99277a5cee5feb3bb43079b733ecab8b2a6606b56d748e1c17d6e80b1f51c14cbd6d2e226ce#rd) [📖](https://fed.chanceyu.com?id=bcf4d97bc4dd1a045e951baaebc14ef4)
- [【淘系前端团队】SIGGRAPH-Asia-2025-|-只用一部手机创建和渲染高质量3D数字人](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650542140&idx=1&sn=32fd8a79d171fe34d273bc8cc91d00fa&chksm=8390d824b4e75132fded65cf4c4be2cd0f11c7c18ed8c805388286e15780160ed8f1431df5df#rd) [📖](https://fed.chanceyu.com?id=35df1961b24c760200db9d6716691c94)
- [查看更多 >](/details/tags/canvas-image.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="音视频">
音视频
</summary>
<p></p>
> 关键字:`WebVR`、`WebXR`、`WebRTC`、`Video`、`Audio`、`MediaStream`、`MediaRecorder`、`音频`、`视频`、`直播`、`点播`、`推流`、`拉流`、`编解码`、`转码`、`H264`、`H265`、`VP8`、`VP9`、`AV1`、`AAC`、`MP3`、`Opus`、`HLS`、`DASH`、`FLV`、`RTMP`、`RTSP`、`FFmpeg`、`摄像头`、`麦克风`、`屏幕共享`、`播放器`、`音视频同步`、`字幕`、`弹幕`、`画中画`、`倍速`
- [【淘系前端团队】ICLR-2026-|-基于概念蒸馏的生成式视频复原算法Vivid-VR](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650542989&idx=1&sn=aca89d56d7cbdf0d27ad6a5ae0b7af5a&chksm=8390db95b4e75283b82f27430f2b3e70f4cc06b05b61917a478081e216dd3fa683969b9c7c22#rd) [📖](https://fed.chanceyu.com?id=11962c709d9d70b3ac12e38d170609bf)
- [【阮一峰的网络日志】智谱旗舰-GLM-5-实测:对比-Opus-4.6-和-GPT-5.3-Codex](http://www.ruanyifeng.com/blog/2026/02/glm-5.html) [📖](https://fed.chanceyu.com?id=be43eb6904111f368c99278e2cc44d4a)
- [【JavaScript-Weekly】Babel-8-RC-Arrives,-Gatsby-Lives,-Lodash-Resets](https://javascriptweekly.com/issues/771) [📖](https://fed.chanceyu.com?id=6b807c8dbdb15b9f73f44d022f2b72df)
- [【淘系前端团队】【淘宝直播数字人互动LLM】告别AI感:基于真人ASR数据的拟人化探索](http://mp.weixin.qq.com/s/0JJll2dNx_Kk2ke_5JrvFg) [📖](https://fed.chanceyu.com?id=61cdf2fe9e918efbf64da316b374dc91)
- [【程序员成长指北】前端指纹技术是如何实现的?(Canvas、Audio、硬件API-核心原理解密)](http://mp.weixin.qq.com/s/IkxN7GkS5zgCH2LX5IHbsA) [📖](https://fed.chanceyu.com?id=96d3cf561da87fe6ff40c460cf1e47ba)
- [【淘系前端团队】2025淘宝直播双11花花乐动画实现方案思考&分享](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650542021&idx=1&sn=7a6602a2f66c962b2d69934938727815&chksm=8390c7ddb4e74ecb7260738532c8257c0dde2b6a491ac68acddcf475b0bcab9abf3d75a88202#rd) [📖](https://fed.chanceyu.com?id=9885f1e3efd9e81f3a47d377e52e1e55)
- [【前端大全】把-Bug-曝光到全网,谷歌逼-FFmpeg-维护者“按时修复”,遭怒怼:别光用-AI-找-Bug,有本事你自己修啊!](http://mp.weixin.qq.com/s/n9LGY_SWO2jh_uV7nW3G_g) [📖](https://fed.chanceyu.com?id=1afb3b31b93c7d2c9fcd5a201917c2bd)
- [【前端大全】华为老粉称被鸿蒙-5-坑惨!升级不兼容,回退丢失数据。华为回应:视频创作者等群体慎更新](http://mp.weixin.qq.com/s/2BHkQdzON4TMPRvmsNFaYg)
- [【程序员成长指北】WebRTC实现网页直播](http://mp.weixin.qq.com/s/uA7BofPS-sT0yNMrhjWRfg)
- [【风痕·術&思】ZingAI.video-在线口播视频剪辑](https://fenghen.me/posts/2025/09/01/overview/)
- [查看更多 >](/details/tags/audio-video.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="性能优化">
性能优化
</summary>
<p></p>
> 关键字:`性能`、`优化`、`加载`、`速度`、`体验`、`SEO`、`LCP`、`FID`、`CLS`、`FCP`、`TTI`、`TBT`、`TTFB`、`Hints`、`Prefetch`、`Prerender`、`Preload`、`Preconnect`、`Lighthouse`、`PageSpeed`、`WebPageTest`、`性能监控`、`性能分析`、`性能指标`、`首屏`、`白屏`、`懒加载`、`预加载`、`按需加载`、`代码分割`、`压缩`、`Gzip`、`Brotli`、`CDN`、`缓存`、`资源优化`、`图片优化`、`字体优化`、`关键渲染路径`、`渲染优化`、`重绘`、`重排`、`防抖`、`节流`、`虚拟列表`、`长列表`、`内存泄漏`、`垃圾回收`
- [【淘系前端团队】淘宝跨端体验优化-AI-演进之路](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543161&idx=1&sn=203f52399657de9f0c1d8eced67d9cab&chksm=8390dc21b4e755379d46e4ed6d670ec4c0a5243dad60a4d1f9ed2950d918804a9e3ba5b491f6#rd) [📖](https://fed.chanceyu.com?id=8cb07e172464c706363d4f3db943a7e0)
- [【Nodejs技术栈】Codex-Plugins-来了,我拿它做了第一个实验:把微信接进去,体验翻车了](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523103&idx=1&sn=2bebf517423637d86b233186716e7493&chksm=e80fd451df785d479cd744f05176c87e94ffe567e513c89fee93f6643f2f1506f3dd26bbdb82#rd) [📖](https://fed.chanceyu.com?id=14203a7f69ff3459705e49aaaf6c4005)
- [【程序员成长指北】前端发版后页面白屏?一套解决用户停留旧页面问题的完整方案](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529538&idx=1&sn=fa53f719ba91ed033fae434acdf2f443&chksm=f9927293cee5fb856f554e25b5367b1f8bc638c899ff4eb73442b4781657d7f22797f6ee0ef0#rd) [📖](https://fed.chanceyu.com?id=f4f0cd4ace5924b2093000c24d1a7522)
- [【Nodejs技术栈】尤雨溪转发庆祝,Claude.ai-抛弃-SSR-拥抱-Vite,性能飙升!](http://mp.weixin.qq.com/s/e4KkP-hepeBqhhaIN9IiQg) [📖](https://fed.chanceyu.com?id=5d8328ad201c64d610e707ab0a9ee4ae)
- [【前端早读课】【第3670期】AI-工程实战:Claude-Code-团队总结的六条反直觉缓存法则](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278747&idx=1&sn=123edfcd2136f85e486428c863008faf&chksm=bca29b86b72625b42750c50b71e0dbb486984ae071eb3a19519ccdac9a84eec77ac006a756b8&scene=0#rd) [📖](https://fed.chanceyu.com?id=8717aa9d5a98377bd08ed373d10be35b)
- [【淘系前端团队】AI真人数字人语音对话性能优化实践总结](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650542923&idx=1&sn=1403311c090693a4dc684ca861e515b6&chksm=8390db53b4e75245e036e1e89b5f5fe80ee19d832b8c76c9946b9456f4091dfc134da88bdc54#rd) [📖](https://fed.chanceyu.com?id=a15520b450fa31079a5a615963d81e30)
- [【JavaScript-Weekly】Oxfmt-beta:-30x-faster-than-Prettier,-100%-compatible](https://javascriptweekly.com/issues/774) [📖](https://fed.chanceyu.com?id=050fad08bd23e55077593ec5761eac91)
- [【程序员成长指北】用-200-行-JS-实现“渐进式-JSON”——让网页加载速度快到飞起!](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529182&idx=1&sn=8a63421a91f196fdec83720fcca91152&chksm=f992740fcee5fd192da7e35bdc2635156ab7ba04fa894cac800b1d49823db248d6bada4514c7#rd) [📖](https://fed.chanceyu.com?id=401cc16ac7d193859aeea2d481cdea09)
- [【淘系前端团队】一次大文件处理性能优化实录](http://mp.weixin.qq.com/s/iVLS1iK_RtWpca3qzUtQ0w) [📖](https://fed.chanceyu.com?id=33e4237e5952cd691cff7abd0ba35522)
- [【前端早读课】【第3652期】告别-dotenv?Node.js-原生支持-.env-文件加载了](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278565&idx=1&sn=8fad43e39b148409f33d76e2510ec82d&chksm=bc8319b363047c43cdfea2d5ea7e500679426fc3fc229b4a7d4ad9d9de353458256466835113&scene=0#rd) [📖](https://fed.chanceyu.com?id=08d823bd9d3fcd86d40fa6509c7d79c6)
- [查看更多 >](/details/tags/optimization.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="前端进阶">
前端进阶
</summary>
<p></p>
> 关键字:`AST`、`抽象语法树`、`GPU`、`WebAssembly`、`WASM`、`Vim`、`Emacs`、`HTTP`、`QUIC`、`TCP`、`UDP`、`IP`、`DNS`、`CDN`、`算法`、`数据结构`、`设计模式`、`架构`、`全栈`、`工程化`、`自动化`、`可视化`、`图表`、`ECharts`、`D3.js`、`AntV`、`微前端`、`qiankun`、`micro-app`、`single-spa`、`源码`、`原理`、`实现`、`前端架构`、`大前端`、`Serverless`、`云函数`、`边缘计算`、`低代码`、`无代码`、`可视化搭建`、`编辑器`、`Monaco`、`CodeMirror`、`Ace`、`富文本`、`Quill`、`Slate`、`ProseMirror`、`TipTap`、`正则表达式`、`函数式编程`、`响应式编程`、`RxJS`、`设计系统`、`组件库`、`Monorepo`、`依赖管理`、`版本管理`、`发布`、`部署`、`CI/CD`、`测试`、`单元测试`、`集成测试`、`E2E`、`Jest`、`Vitest`、`Mocha`、`Chai`、`Cypress`、`Playwright`、`调试`、`性能分析`、`安全`、`加密`、`认证`、`授权`、`OAuth`、`JWT`、`SSO`、`RBAC`
- [【Nodejs技术栈】刚刚,Cursor-3.0-发布!编辑器没了,变成了-Agent-指挥部](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523156&idx=1&sn=13c401936934f668f2bd9c8c4fdd6024&chksm=e80fd41adf785d0cb06cb551a5c734f8c4f5303c950327706f877b3157571f0272e41b659460#rd) [📖](https://fed.chanceyu.com?id=d139e544d2f29bcdacbd3f92b687ca12)
- [【淘系前端团队】淘宝营销会场智能测试平台的AI落地实践](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543144&idx=1&sn=721c1a6266e58ecc7d383e368a04bebd&chksm=8390dc30b4e755269829f70d5ea10ea7281c2b27b5b34cde97c6560b11a7b6f96a82599eee60#rd) [📖](https://fed.chanceyu.com?id=8bebb64e26b7f9fa38e89f0f97c6d4db)
- [【前端早读课】【第3679期】Worker-线程拯救-Node.js-心跳:Inngest-Connect-架构演进](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278851&idx=1&sn=345f03042da9e8e147067ba61f53d100&chksm=bcc60dfe4387d045b658feb6d099daafe18623fc66183e42aa721ad37cfa667b479c1b68a077&scene=0#rd) [📖](https://fed.chanceyu.com?id=eac84651b97ef33fa50224a1f98a61e3)
- [【前端早读课】【第3678期】JavaScript-解析-VIN-码:三种方案全面对比](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278846&idx=1&sn=0d115156c8935e60b834012164ef7800&chksm=bceeca05c71c678764c030775f4d20f2c35ef6630abbaaf57b63d2cf160ee4dd7aca2ec8b20f&scene=0#rd) [📖](https://fed.chanceyu.com?id=bcdcba287995a70e4c3da99c0c0e96ed)
- [【JavaScript-Weekly】A-new,-major-npm-supply-chain-attack-via-Axios](https://javascriptweekly.com/issues/779) [📖](https://fed.chanceyu.com?id=a38f983bf5b0be6c9bef393b01c22eca)
- [【前端从进阶到入院】CDP-是什么?理解它,才算真正懂了前端调试工具链](http://mp.weixin.qq.com/s?__biz=MzI3NTM5NDgzOA==&mid=2247517562&idx=1&sn=b173e453dec75217a35c0a9012ed65a7&chksm=eb07bd03dc703415615de2a6c9e1f7d226d052e2846f87d96a2f3e7e5305874f3dd0caf08388#rd) [📖](https://fed.chanceyu.com?id=157a5d153cc12fb187a0ab5365569f94)
- [【前端从进阶到入院】刚刚,Vite-两个新工具发布!Webpack-完!](http://mp.weixin.qq.com/s/kRAX2YZ7Kw9r5Wr4j6B_rw) [📖](https://fed.chanceyu.com?id=2235194d632f5ccd6d92556e4123dca2)
- [【程序员成长指北】别再纠结格式:5-种设计模式,构建真正可靠的-AI-Agent](http://mp.weixin.qq.com/s/hEBK6mHO5nM5Sm8MWEmQWQ) [📖](https://fed.chanceyu.com?id=3c42bde4e9133a33914dcc292053b348)
- [【Node-Weekly】How-TypeScript-6.0-affects-Node-developers](https://nodeweekly.com/issues/617) [📖](https://fed.chanceyu.com?id=e55606bacdcabcc0e704fb0b287afa99)
- [【前端早读课】【第3675期】别再纠结格式:5-种设计模式,构建真正可靠的-AI-Agent](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278808&idx=1&sn=cb062b1747e889741fc1ad1e42b7c225&chksm=bc82f8ebecd3a413e6a8963b5fc3378d07daac7e332cfb66b561307cfcea47b174050ae3c627&scene=0#rd) [📖](https://fed.chanceyu.com?id=48eb58d9afd8932362d9060125fd08d3)
- [查看更多 >](/details/tags/front-end-advanced.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="服务端">
服务端
</summary>
<p></p>
> 关键字:`Nginx`、`Apache`、`Caddy`、`Traefik`、`Docker`、`Kubernetes`、`K8s`、`容器`、`镜像`、`编排`、`Server`、`服务器`、`云服务`、`AWS`、`Azure`、`GCP`、`阿里云`、`腾讯云`、`GraphQL`、`Apollo`、`REST`、`RESTful`、`API`、`gRPC`、`Protobuf`、`Thrift`、`消息队列`、`Kafka`、`RabbitMQ`、`Redis`、`Memcached`、`Rust`、`Java`、`Spring`、`Python`、`PHP`、`Laravel`、`Golang`、`Gin`、`Beego`、`Django`、`Flask`、`FastAPI`、`SSR`、`SSG`、`ISR`、`CSR`、`同构`、`渲染`、`MySQL`、`PostgreSQL`、`MongoDB`、`SQLite`、`ORM`、`SQL`、`NoSQL`、`数据库`、`索引`、`事务`、`锁`、`JWT`、`Session`、`Cookie`、`认证`、`授权`、`SSH`、`rsync`、`Bash`、`Shell`、`Zsh`、`Tmux`、`Screen`、`命令`、`脚本`、`Linux`、`Unix`、`CentOS`、`Ubuntu`、`Debian`、`运维`、`部署`、`监控`、`日志`、`备份`、`负载均衡`、`反向代理`、`正向代理`、`网关`、`微服务`、`分布式`、`集群`、`高可用`、`容灾`、`熔断`
- [【程序员成长指北】LangGraph:基于图的-AI-Agent-编排框架深度解析](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529639&idx=1&sn=325db2a072d5d2f17c7d84734ca0683a&chksm=f9927276cee5fb60652b3ddc219a4b464b07ab5c86df4eb4486fce3b6a47e8fd6b57dd8a4469#rd) [📖](https://fed.chanceyu.com?id=27b84778555eecfdf7f5fbfca2c4730e)
- [【程序员成长指北】【D2-演讲实录】从上下文工程到-Harness-Engineering](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529620&idx=1&sn=d0bba042bb1f4b84b07f5a3075b8f360&chksm=f9927245cee5fb530ec8bb3e239ab3b50b17deab10d2e0e1b9ce4b92516f38708a91e6c6a22a#rd) [📖](https://fed.chanceyu.com?id=45812a14c92e0214a946516285072199)
- [【前端大全】前端圈沸腾!Claude造出15KB引擎,渲染狂飙1200倍:文字里能跑马里奥](http://mp.weixin.qq.com/s/vlhTJXe8JiHD7si24Kwszw) [📖](https://fed.chanceyu.com?id=d2b16fc47e976a71a3705cdb4cbf7885)
- [【前端早读课】【第3678期】JavaScript-解析-VIN-码:三种方案全面对比](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278846&idx=1&sn=0d115156c8935e60b834012164ef7800&chksm=bceeca05c71c678764c030775f4d20f2c35ef6630abbaaf57b63d2cf160ee4dd7aca2ec8b20f&scene=0#rd) [📖](https://fed.chanceyu.com?id=bcdcba287995a70e4c3da99c0c0e96ed)
- [【Nodejs技术栈】Codex-Plugins-来了,我拿它做了第一个实验:把微信接进去,体验翻车了](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523103&idx=1&sn=2bebf517423637d86b233186716e7493&chksm=e80fd451df785d479cd744f05176c87e94ffe567e513c89fee93f6643f2f1506f3dd26bbdb82#rd) [📖](https://fed.chanceyu.com?id=14203a7f69ff3459705e49aaaf6c4005)
- [【程序员成长指北】别再裸用-Claude-Code-了!32-个亲测技能-+-8-个-MCP-服务器,开发效率直接拉满!](http://mp.weixin.qq.com/s/jPZsQsXt3ugysBn4bIHOag) [📖](https://fed.chanceyu.com?id=f657ee79c57bd9c719991e3b736f4de5)
- [【Nodejs技术栈】尤雨溪转发庆祝,Claude.ai-抛弃-SSR-拥抱-Vite,性能飙升!](http://mp.weixin.qq.com/s/e4KkP-hepeBqhhaIN9IiQg) [📖](https://fed.chanceyu.com?id=5d8328ad201c64d610e707ab0a9ee4ae)
- [【张鑫旭-鑫空间-鑫生活】浅学WebTransport-API:下一代Web双向通信技术](https://www.zhangxinxu.com/wordpress/2026/03/webtransport-api/) [📖](https://fed.chanceyu.com?id=a5d45f581f6b0f5577946686c52eb8c3)
- [【前端大全】48-小时欠谷歌-56-万!三人开发小团队要破产了,只因-Gemini-API-密钥被盗用](http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624176&idx=1&sn=ca425dffac3520b0df6b941f82248334&chksm=80224531b755cc27f6f0afb18a9498d5f497bfa7030a37d0eaa46a327ffbf7da1a0404d66803#rd) [📖](https://fed.chanceyu.com?id=8cea4c85760db8d1516948d6df9fdc27)
- [【前端早读课】【第3668期】从-Web-Streams-到-Async-Iterable:重新思考-JavaScript-流式-API](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278731&idx=1&sn=732699cc260b5c65c1a0f7f2a93080eb&chksm=bcf7ba8ddfd7d441a68f9d0d448072422250cf74be2b2c7ba3191dbe43dffd55593f84e4fe06&scene=0#rd) [📖](https://fed.chanceyu.com?id=f394d9fd24f952a1695673fb868ee777)
- [查看更多 >](/details/tags/server.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="版本控制">
版本控制
</summary>
<p></p>
> 关键字:`Git`、`Bitbucket`、`SVN`、`版本控制`、`版本管理`、`分支`、`合并`、`冲突`、`代码审查`、`Submodule`、`Subtree`、`Husky`、`commitlint`、`语义化版本`、`SemVer`、`changelog`
- [【Nodejs技术栈】GitHub-Star-暴涨!前-React-核心成员出手,把浏览器-30-年算不好的文字布局问题解决了](http://mp.weixin.qq.com/s/ibVkVGk62j3kCPO_wxBYlg) [📖](https://fed.chanceyu.com?id=f17f69647c6d7eb365318f05d053617f)
- [【程序员成长指北】Clawdbot-空前爆火!github-一天涨-5w+-Star!](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529096&idx=1&sn=9013f71a4c3a8ead2f6cd6a76993c29b&chksm=f9927459cee5fd4f964e0208d3a29163b45f609276e841023d9d717cdd1823ca0a241bb8a45c#rd) [📖](https://fed.chanceyu.com?id=971c10d06cb51f2941a4ee88d05122f9)
- [【前端大全】炸锅了!腾讯突然给-GitHub-发函,下架-4000-多个微信聊天记录相关开源仓库。网友:自己的记录都不让导出?](http://mp.weixin.qq.com/s/vmFVRqTeWou0h3F1PRlMVA) [📖](https://fed.chanceyu.com?id=075d66de474bfad95b00778a0b5d5403)
- [【JavaScript-Weekly】TypeScript-and-JavaScript-dominate-on-GitHub-in-2025](https://javascriptweekly.com/issues/759)
- [【前端早读课】【第3591期】GitHub-Spec-Kit:规范驱动开发走在正确的方向上---严谨、渐进式的-Vibe-Coding](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651277468&idx=1&sn=555fad29ddf6adaed65dd6b2032b9ec7&chksm=bc9f590d3277bd93b223f9755fb43bcb32e9dace3ddc5785447afa3791ea123e6420a2c601a2&scene=0#rd)
- [【前端大全】“领导趁我下班后偷看电脑,发现有git拉的代码,说我干私活要开除。。。辛苦一个月,天天加班,1.2万工资最后扣到4900”](http://mp.weixin.qq.com/s/6QyEDQin2RH87nKxH764Vg)
- [【程序员成长指北】再也不用背命令!Cursor-终端-Cmd+K-=-你的专属-Linux-&-Git-智能助手](http://mp.weixin.qq.com/s/k1A7W0QwfqGZMFgfU50e_Q)
- [【前端早读课】【第3579期】OODA-循环与-Git-Worktrees:来自-AI-辅助开发的九个启示](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651277328&idx=1&sn=36651ba5d2e57954793aadc84c1c434c&chksm=bc55c2f3ac82137f7afb625dc7d1b784a5a726b34ab1ce85d19ed42c713950179e35b1d494b3&scene=0#rd)
- [【前端早读课】【第3568期】使用-Git-Worktree-进行开发](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651277197&idx=1&sn=2e8ebe9823216531c8e5b28581ccf48d&chksm=bcd0bf2c406b54d67bca607fbe168579b019371f1ab6283e51060ab5d6e5d334419ecc1f9aae&scene=0#rd)
- [【前端之巅】GitHub-2.5万星!日本开发者打造的Hono火了:定义后React时代微框架的轻量未来](http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247526177&idx=1&sn=acde5cec1ba299efe633a3614de56bcd&chksm=f952d262ce255b74976e012d57d0fdbd2fcf2fc4540a3a875acc2888ead33dd0c5ee9df17c74#rd)
- [查看更多 >](/details/tags/git-svn.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="招聘面试">
招聘面试
</summary>
<p></p>
> 关键字:`招聘`、`招人`、`岗位`、`面试`、`笔试`、`内推`、`Offer`、`算法题`、`编程题`
- [【程序员成长指北】web前端230道场景题,背完你的offer就炸了](http://mp.weixin.qq.com/s/9HRDafcOghUyk1QFFW9R6Q) [📖](https://fed.chanceyu.com?id=1ae2a6c36cb02dc10dcb430de4fb8571)
- [【Nodejs技术栈】web前端230道场景题,背完你的offer就炸了](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523039&idx=1&sn=797548474ec8c4f08dcfbbe6cbaaf95e&chksm=e80fd591df785c873b2f4f463f74775cdabd620cb81b5d6954230ce3ccf8bebc8739b3e6e1d3#rd) [📖](https://fed.chanceyu.com?id=61e2e122e3c5a54c1c5412671b2e3288)
- [【Nodejs技术栈】离谱!王自如招个全栈前端工程师,JD-写了六个岗位的活](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523011&idx=1&sn=5598d790ee2c8f18d675e7fdc1f5f431&chksm=e80fd58ddf785c9b22b3db1b90ba57ab704ba2fce8d69fb1eaf7a4f9e87ceceb8156c36f5ba2#rd) [📖](https://fed.chanceyu.com?id=69889ea5ce6d532f915a7ccd49e4089f)
- [【阮一峰的网络日志】科技爱好者周刊(第-389-期):未来如何招聘程序员](http://www.ruanyifeng.com/blog/2026/03/weekly-issue-389.html) [📖](https://fed.chanceyu.com?id=3fc7a8ae3d8d2fb67633f194f6aea69e)
- [【淘系前端团队】淘天集团2027届实习生招聘正式启动,文末阅读原文查看详情](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543062&idx=1&sn=d453e985ad6644093c56b572c267209c&chksm=8390dbceb4e752d8e6ef8d405f12d66c6edb05bf83e4aa0d085b78794ff60f6fb4340c3e71ff#rd) [📖](https://fed.chanceyu.com?id=992c3c1c2fc9e37948813e7e28c2ef1a)
- [【程序员成长指北】web前端230道场景题,背完你的offer就炸了](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529455&idx=1&sn=9e4846fe4d0eac846bf48b24ccd4ba8e&chksm=f992753ecee5fc280e28334ac178e44c84756a680aa87d24fdb269eb0e97b64df89a614a5788#rd) [📖](https://fed.chanceyu.com?id=5c063faca5c7e7962434644f32114ad5)
- [【前端从进阶到入院】web前端230道场景题,背完你的offer就炸了](http://mp.weixin.qq.com/s/0hJRbl2RInw6jQaU7hevGg) [📖](https://fed.chanceyu.com?id=8ec7977fe819cad14d012a11d946e774)
- [【程序员成长指北】web前端230道场景题,背完你的offer就炸了](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529217&idx=1&sn=dc8d1ddaea4e0115261e61a32c535152&chksm=f99275d0cee5fcc6bd471364b733d6f800eb0fd5dc3e259a928d061c5cae8b42369ace4d67d2#rd) [📖](https://fed.chanceyu.com?id=8b373b60dc3b224731eeacf41f34ed94)
- [【前端大全】20-年-IT-老兵转行“收废品”!200+-场面试无果](http://mp.weixin.qq.com/s/oj2b_Sx2XVYOc4jC-gvpPA) [📖](https://fed.chanceyu.com?id=f35cff54a0394aa180fdb78209682f87)
- [【前端早读课】【招聘】杭州古茗招P6前端工程师](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278454&idx=2&sn=5dfc313b9d4fc13cbc9179258c2a65b0&chksm=bc7e55c34604a19d3ddc2c0b1ee5aa26dde6cd338b18dd37600e8344086f79d74371445c2baf&scene=0#rd) [📖](https://fed.chanceyu.com?id=daacd9dfb248954436b4d8d2c52d50d0)
- [查看更多 >](/details/tags/job-interview.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
<details open>
<summary id="其它">
其它
</summary>
<p></p>
- [【Nodejs技术栈】刚刚,Cursor-3.0-发布!编辑器没了,变成了-Agent-指挥部](http://mp.weixin.qq.com/s?__biz=MzIyNDU2NTc5Mw==&mid=2247523156&idx=1&sn=13c401936934f668f2bd9c8c4fdd6024&chksm=e80fd41adf785d0cb06cb551a5c734f8c4f5303c950327706f877b3157571f0272e41b659460#rd) [📖](https://fed.chanceyu.com?id=d139e544d2f29bcdacbd3f92b687ca12)
- [【阮一峰的网络日志】科技爱好者周刊(第-391-期):AI-的贫富分化](http://www.ruanyifeng.com/blog/2026/04/weekly-issue-391.html) [📖](https://fed.chanceyu.com?id=0d84ac0a58d1188959ec6e9608c9b063)
- [【淘系前端团队】淘宝跨端体验优化-AI-演进之路](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543161&idx=1&sn=203f52399657de9f0c1d8eced67d9cab&chksm=8390dc21b4e755379d46e4ed6d670ec4c0a5243dad60a4d1f9ed2950d918804a9e3ba5b491f6#rd) [📖](https://fed.chanceyu.com?id=8cb07e172464c706363d4f3db943a7e0)
- [【前端早读课】【早说】Ghostty创始人的AI之旅](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278865&idx=1&sn=72b0e18b749cbe4817d6f7b96c6ea7ed&chksm=bc0ba13f35cf0cd8f410ef892d3490d0c93ca1c3c35d2469057a5c6b627953e4a637451bd6f5&scene=0#rd) [📖](https://fed.chanceyu.com?id=a293f09b5b10f96424be936e261e049a)
- [【Nodejs技术栈】GitHub-Star-暴涨!前-React-核心成员出手,把浏览器-30-年算不好的文字布局问题解决了](http://mp.weixin.qq.com/s/ibVkVGk62j3kCPO_wxBYlg) [📖](https://fed.chanceyu.com?id=f17f69647c6d7eb365318f05d053617f)
- [【程序员成长指北】Skills-乱麻了!这款开源神器彻底终结噩梦,Cursor/Claude-一键全同步](http://mp.weixin.qq.com/s?__biz=MzUxNzk1MjQ0Ng==&mid=2247529642&idx=1&sn=16005d3929226468a8ee1b8c0ed4c8b6&chksm=f992727bcee5fb6d914cc77966c95fe1545ed7ad4d303da32eade65e5d892b826e4acc3e8b26#rd) [📖](https://fed.chanceyu.com?id=19f3ce33241b6645338b72c2f8cd13f1)
- [【前端早读课】【早说】AI指数级进化时代的产品管理](http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278860&idx=1&sn=54fc7b48572bb519431e0f10a96445c9&chksm=bcb6d6dbe3d7fe42cd6a8ffb84b072ab7ce5b5787129441ac8e8f430178090eed732c782546d&scene=0#rd) [📖](https://fed.chanceyu.com?id=f893f77eeda4a1b71e0dc2c0c0f133a6)
- [【Node-Weekly】Node.js-25.9-brings---max-heap-size-and-better,-iterable-streams](https://nodeweekly.com/issues/618) [📖](https://fed.chanceyu.com?id=a0092eef8f955b266308b261ede471c7)
- [【Nodejs技术栈】504块咖啡钱,和一个价值千万的-AI-梦](http://mp.weixin.qq.com/s/9mKWwG8pg1Pn447ETZTfZw) [📖](https://fed.chanceyu.com?id=b0837207e4fe100e308eea3c68d1f49a)
- [【淘系前端团队】淘宝营销会场智能测试平台的AI落地实践](http://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650543144&idx=1&sn=721c1a6266e58ecc7d383e368a04bebd&chksm=8390dc30b4e755269829f70d5ea10ea7281c2b27b5b34cde97c6560b11a7b6f96a82599eee60#rd) [📖](https://fed.chanceyu.com?id=8bebb64e26b7f9fa38e89f0f97c6d4db)
- [查看更多 >](/details/tags/other.md)
<div align="right"><a href="#文章分类">⬆ 返回顶部</a></div>
</details>
================================================
FILE: article-to-md/.gitignore
================================================
node_modules/
.env
================================================
FILE: article-to-md/README.md
================================================
# article-to-md
将 `data/links.json` 中的文章 URL 抓取并转换为 Markdown 文件。
## 技术栈
| 依赖 | 用途 |
|------|------|
| [playwright](https://playwright.dev) | 无头浏览器抓取页面 |
| [turndown](https://github.com/mixmark-io/turndown) | HTML → Markdown 转换 |
| [dayjs](https://day.js.org) | 日期处理与格式化 |
## 快速开始
```bash
# 安装依赖(含 Playwright 浏览器)
pnpm install
pnpm exec playwright install chromium
# 处理所有未处理的文章(按日期从新到旧)
pnpm start
# 只处理最新的 10 篇
pnpm start --limit=10
# 强制重新处理(忽略 processed.json)
pnpm start --force
# 非 headless 模式(可视化调试)
pnpm start --headless=false
```
## 输出结构
```
article-to-md/
├── output/ # 生成的 Markdown 文件,以 md5(url) 命名
│ └── <md5>.md
└── processed.json # 已处理记录:{ "<url>": "<md5>" }
```
每个 Markdown 文件包含 YAML front-matter:
```yaml
---
title: "文章标题"
link: "https://..."
date: 2026-01-01
md5: <md5(url)>
---
```
## 域名规则
在 `src/rules/` 目录下为各网站定制抓取规则
### 新增域名规则
新建 `src/rules/<name>.js`,导出一个规则对象:
```js
/** @type {import('./index.js').SiteRule} */
export default {
// 正文容器的 CSS 选择器
contentSelector: '.article-body',
// 抓取前需要移除的元素选择器
excludeSelectors: ['.ad', '.sidebar', 'footer'],
// Playwright waitUntil 选项(默认 'networkidle')
waitUntil: 'networkidle',
// 可选:页面加载后的自定义操作(如关闭弹窗)
async preProcess(page) {
await page.click('.cookie-dismiss').catch(() => {})
},
// 可选:自定义 Turndown 转换规则
turndownRules(td) {
td.addRule('customImage', { /* ... */ })
},
}
```
然后在 `src/rules/index.js` 的 `RULES` 对象中注册:
```js
import myRule from './mysite.js'
const RULES = {
// ...
'mysite.com': myRule,
}
```
## API
```js
import { processArticle } from './src/processor.js'
const result = await processArticle(
{ title: '文章标题', link: 'https://...', date: '2026-01-01' },
{ force: false, headless: true },
)
// result: { success, md5, outputPath } | { skipped, md5 } | { error, md5 }
```
================================================
FILE: article-to-md/package.json
================================================
{
"name": "article-to-md",
"version": "1.0.0",
"description": "Fetch articles by URL and convert to Markdown files",
"type": "module",
"scripts": {
"start": "node src/index.js",
"process": "node src/index.js",
"process:limit": "node src/index.js --limit=10",
"serve": "node src/server.js",
"once": "node src/once.js",
"upload": "node src/upload.js"
},
"pnpm": {
"onlyBuiltDependencies": [
"sharp"
]
},
"dependencies": {
"@hono/node-server": "^1.19.9",
"cheerio": "^1.2.0",
"dayjs": "^1.11.13",
"dotenv": "^17.3.1",
"fs-extra": "^11.3.3",
"hono": "^4.12.3",
"playwright": "^1.50.1",
"qiniu": "^7.12.0",
"sharp": "^0.33.5",
"turndown": "^7.2.0",
"turndown-plugin-gfm": "^1.0.2"
}
}
================================================
FILE: article-to-md/src/check-cloud.js
================================================
#!/usr/bin/env node
/**
* 读取 data/processed.json,判断每一个文章对应的 .md 文件在云存储上是否存在,输出日志。
* 缺失时输出文章标题和链接(标题来源于 data/links.json),并将缺失列表写入 data/check-missing.json。
* 云存储 URL:ARTICLE_DATA_HOST/data/articles/<md5>/page.md
*
* 环境变量:
* ARTICLE_DATA_HOST 云存储根地址,默认 https://fed-data.chanceyu.com
* PROCESSED_JSON_PATH 可选,processed.json 路径
* LINKS_JSON_PATH 可选,links.json 路径,用于解析标题
*/
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const DATA_DIR = path.join(__dirname, '..', '..', 'data')
const DEFAULT_PROCESSED = path.join(DATA_DIR, 'processed.json')
const DEFAULT_LINKS = path.join(DATA_DIR, 'links.json')
const MISSING_PATH = path.join(DATA_DIR, 'missing.json')
const DEFAULT_HOST = 'https://fed-data.chanceyu.com'
const ARTICLE_DATA_HOST = process.env.ARTICLE_DATA_HOST || DEFAULT_HOST
const PROCESSED_JSON_PATH = process.env.PROCESSED_JSON_PATH || DEFAULT_PROCESSED
const LINKS_JSON_PATH = process.env.LINKS_JSON_PATH || DEFAULT_LINKS
function loadProcessed() {
if (!fs.existsSync(PROCESSED_JSON_PATH)) {
console.error('processed.json 不存在:', PROCESSED_JSON_PATH)
process.exit(1)
}
const raw = fs.readFileSync(PROCESSED_JSON_PATH, 'utf8')
try {
return JSON.parse(raw)
} catch (e) {
console.error('processed.json 解析失败:', e.message)
process.exit(1)
}
}
/** 从 links.json 构建 link -> { title, link } 映射,用于缺失时输出标题和链接 */
function loadLinksMap() {
if (!fs.existsSync(LINKS_JSON_PATH)) {
return {}
}
const raw = fs.readFileSync(LINKS_JSON_PATH, 'utf8')
let sources
try {
sources = JSON.parse(raw)
} catch {
return {}
}
const map = {}
if (Array.isArray(sources)) {
for (const source of sources) {
const items = source.items || []
for (const item of items) {
if (item.link) {
map[item.link] = {...item}
}
}
}
}
return map
}
async function headExists(url) {
try {
const res = await fetch(url, { method: 'HEAD', redirect: 'follow' })
return res.ok
} catch (e) {
return false
}
}
async function main() {
const processed = loadProcessed()
const linksMap = loadLinksMap()
const entries = Object.entries(processed)
const base = ARTICLE_DATA_HOST.replace(/\/$/, '')
console.log('ARTICLE_DATA_HOST:', base)
console.log('processed.json 条目数:', entries.length)
console.log('links.json 条目数:', Object.keys(linksMap).length)
console.log('')
let exist = 0
let missing = 0
const missingList = []
for (let i = 0; i < entries.length; i++) {
const [url, md5] = entries[i]
const mdUrl = `${base}/data/articles/${md5}/page.md`
const ok = await headExists(mdUrl)
if (ok) {
exist++
} else {
missing++
const info = linksMap[url] || { title: '(无标题)', link: url }
missingList.push({ md5, ...info })
console.log('[MISSING]', md5)
console.log(' 标题:', info.title)
console.log(' 链接:', info.link)
}
if ((i + 1) % 50 === 0) {
console.log(` ... 已检查 ${i + 1}/${entries.length}`)
}
}
console.log('')
console.log('--- 汇总 ---')
console.log('存在:', exist)
console.log('缺失:', missing)
if (missingList.length > 0) {
fs.writeFileSync(MISSING_PATH, JSON.stringify(missingList, null, 2), 'utf8')
console.log('已保存缺失列表到:', MISSING_PATH)
console.log('缺失列表(标题 + 链接):')
missingList.forEach(({ md5, title, link }) => {
console.log(' ', md5)
console.log(' 标题:', title)
console.log(' 链接:', link)
})
} else {
if (fs.existsSync(MISSING_PATH)) {
fs.unlinkSync(MISSING_PATH)
console.log('无缺失,已删除旧文件:', MISSING_PATH)
}
}
}
main().catch((e) => {
console.error(e)
process.exit(1)
})
================================================
FILE: article-to-md/src/images.js
================================================
import sharp from 'sharp'
import fs from 'fs-extra'
const { ensureDirSync, writeFileSync } = fs
import { join, extname } from 'node:path'
import { createHash } from 'node:crypto'
/** Formats sharp can compress; others are saved as-is */
const COMPRESSIBLE = new Set(['jpeg', 'jpg', 'png', 'webp', 'avif'])
/**
* Derive a stable filename from a URL using md5.
* The extension is resolved from the actual image format detected by sharp so
* CDN URLs without a meaningful extension are handled correctly.
* @param {string} url
* @param {string} detectedExt - e.g. '.jpeg'
* @returns {string}
*/
function imageFilename(url, detectedExt) {
const hash = createHash('md5').update(url).digest('hex')
const urlExt = url.startsWith('data:') ? '' : extname(new URL(url).pathname).toLowerCase()
const ext = detectedExt ?? (urlExt || '.jpg')
return `${hash}${ext}`
}
const MAX_WIDTH = 1000
/**
* Compress raw image bytes with sharp and write to destPath.
* If original width > MAX_WIDTH, resizes to MAX_WIDTH with proportional scaling.
* @param {Buffer} raw
* @param {string} destPath
*/
async function compressAndSave(raw, destPath) {
const image = sharp(raw)
const meta = await image.metadata()
const format = meta.format ?? 'jpeg'
// Resize to max width with proportional scaling when larger (prevents distortion)
let pipeline = image
if (meta.width != null && meta.width > MAX_WIDTH) {
pipeline = pipeline.resize({ width: MAX_WIDTH })
}
if (COMPRESSIBLE.has(format)) {
switch (format) {
case 'jpeg':
case 'jpg':
await pipeline.jpeg({ quality: 80, progressive: true }).toFile(destPath)
break
case 'png':
await pipeline.png({ compressionLevel: 9, palette: true }).toFile(destPath)
break
case 'webp':
await pipeline.webp({ quality: 80 }).toFile(destPath)
break
case 'avif':
await pipeline.avif({ quality: 60 }).toFile(destPath)
break
default:
await pipeline.toFile(destPath)
}
} else {
if (meta.width != null && meta.width > MAX_WIDTH) {
await pipeline.toFile(destPath)
} else {
writeFileSync(destPath, raw)
}
}
return format
}
const FETCH_RETRY_MAX = 3
const FETCH_RETRY_DELAY_MS = 2000
const FETCH_CONCURRENCY = 2
const FETCH_DELAY_BETWEEN_MS = 400
function sleep(ms) {
return new Promise((r) => setTimeout(r, ms))
}
/**
* Fetch raw image bytes from a remote URL.
* Prefers Playwright's APIRequestContext (carries browser session cookies) so
* that CDNs requiring referrer/cookie auth (e.g. WeChat) work correctly.
* Falls back to Node's built-in fetch when no context is supplied.
* Retries on 429/503 with exponential backoff to avoid rate limits.
*
* @param {string} url
* @param {string} pageOrigin - used as Referer for the fallback fetch
* @param {import('playwright').BrowserContext|null} browserContext
* @returns {Promise<Buffer>}
*/
async function fetchImageBuffer(url, pageOrigin, browserContext) {
let lastErr
for (let attempt = 0; attempt < FETCH_RETRY_MAX; attempt++) {
try {
if (browserContext) {
const res = await browserContext.request.get(url, {
headers: {
Referer: pageOrigin,
Accept: 'image/avif,image/webp,image/apng,image/*,*/*;q=0.8',
},
timeout: 20_000,
})
if (res.status() === 429 || res.status() === 503) {
if (attempt === FETCH_RETRY_MAX - 1) throw new Error(`HTTP ${res.status()}`)
await sleep(FETCH_RETRY_DELAY_MS * Math.pow(2, attempt))
continue
}
if (!res.ok()) throw new Error(`HTTP ${res.status()}`)
return Buffer.from(await res.body())
}
const res = await fetch(url, {
signal: AbortSignal.timeout(20_000),
headers: {
'User-Agent':
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/131.0.0.0 Safari/537.36',
Referer: pageOrigin,
Accept: 'image/avif,image/webp,image/apng,image/*,*/*;q=0.8',
},
})
if (res.status === 429 || res.status === 503) {
if (attempt === FETCH_RETRY_MAX - 1) throw new Error(`HTTP ${res.status}`)
await sleep(FETCH_RETRY_DELAY_MS * Math.pow(2, attempt))
continue
}
if (!res.ok) throw new Error(`HTTP ${res.status}`)
return Buffer.from(await res.arrayBuffer())
} catch (err) {
lastErr = err
if (attempt < FETCH_RETRY_MAX - 1) {
await sleep(FETCH_RETRY_DELAY_MS * Math.pow(2, attempt))
} else {
throw lastErr
}
}
}
throw lastErr
}
/**
* Save a base64 data URL image to disk.
* @param {string} dataUrl - e.g. "data:image/png;base64,..."
* @param {string} destDir
* @returns {Promise<string>} local filename
*/
async function saveBase64Image(dataUrl, destDir) {
ensureDirSync(destDir)
const m = dataUrl.match(/^data:image\/(\w+);base64,(.+)$/)
if (!m) throw new Error('Invalid data URL')
const raw = Buffer.from(m[2], 'base64')
// Use a placeholder ext first; compressAndSave detects the real format
const placeholderExt = `.${m[1] === 'jpg' ? 'jpeg' : m[1]}`
const filename = imageFilename(dataUrl, placeholderExt)
const destPath = join(destDir, filename)
await compressAndSave(raw, destPath)
return filename
}
/**
* Download a remote image, compress it with sharp and save to disk.
* @param {string} url
* @param {string} destDir
* @param {string} pageOrigin
* @param {import('playwright').BrowserContext|null} browserContext
* @returns {Promise<string>} local filename
*/
async function downloadImage(url, destDir, pageOrigin, browserContext) {
ensureDirSync(destDir)
const raw = await fetchImageBuffer(url, pageOrigin, browserContext)
const image = sharp(raw)
const meta = await image.metadata()
const format = meta.format ?? 'jpeg'
const ext = `.${format === 'jpg' ? 'jpeg' : format}`
const filename = imageFilename(url, ext)
const destPath = join(destDir, filename)
await compressAndSave(raw, destPath)
return filename
}
/** Matches remote or base64 image URLs in Markdown ![]() syntax */
const MD_IMG_RE = /!\[([^\]]*)\]\(((?:https?:\/\/|data:image\/)[^)"\s]+)\)/g
/**
* Find all images in a Markdown string (remote https:// and base64 data: URLs),
* save & compress each one into `imagesDir`, then return the Markdown with URLs
* rewritten to relative `./images/<filename>` paths.
*
* @param {string} markdown
* @param {string} imagesDir - Absolute path to the images sub-directory
* @param {import('playwright').BrowserContext|null} [browserContext]
* @param {string} [pageOrigin] - Origin of the article page, used as Referer fallback
* @returns {Promise<string>}
*/
/** Run async tasks with limited concurrency; optional delay before each start */
async function runWithConcurrency(tasks, concurrency, delayBetweenMs = 0) {
const results = []
let next = 0
async function worker() {
while (next < tasks.length) {
const i = next++
if (delayBetweenMs && i > 0) await sleep(delayBetweenMs)
results[i] = await tasks[i]()
}
}
await Promise.all(Array.from({ length: Math.min(concurrency, tasks.length) }, () => worker()))
return results
}
export async function localizeImages(markdown, imagesDir, browserContext = null, pageOrigin = '') {
const urls = new Set()
for (const [, , url] of markdown.matchAll(MD_IMG_RE)) urls.add(url)
if (urls.size === 0) return markdown
const results = new Map()
const urlList = [...urls]
const tasks = urlList.map((url) => async () => {
try {
let filename
if (url.startsWith('data:image/')) {
filename = await saveBase64Image(url, imagesDir)
} else {
let origin = pageOrigin
try { origin = new URL(url).origin } catch {}
filename = await downloadImage(url, imagesDir, origin, browserContext)
}
results.set(url, filename)
return filename
} catch (err) {
console.warn(` [img] ✗ ${url.slice(0, 80)} — ${err.message}`)
results.set(url, null)
return null
}
})
await runWithConcurrency(tasks, FETCH_CONCURRENCY, FETCH_DELAY_BETWEEN_MS)
return markdown.replace(MD_IMG_RE, (full, alt, url) => {
const filename = results.get(url)
return filename ? `` : full
})
}
================================================
FILE: article-to-md/src/index.js
================================================
import { fileURLToPath } from 'node:url'
import { join } from 'node:path'
import fs from 'fs-extra'
const { readJsonSync } = fs
import dayjs from 'dayjs'
import { processArticle } from './processor.js'
import { loadProcessed } from './utils.js'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
// Resolve links.json relative to the project root (one level up from src/)
// The repository layout is: front-end-rss/article-to-md/src/index.js
// front-end-rss/data/links.json
const LINKS_PATH = join(__dirname, '..', '..', 'data', 'links.json')
/**
* @typedef {Object} Article
* @property {string} title
* @property {string} link
* @property {string} date
* @property {string} [rssTitle]
*/
/**
* @typedef {Object} ProcessAllOptions
* @property {number} [limit] - Max number of articles to process in one run
* @property {boolean} [skipProcessed=true] - Skip articles already in processed.json
* @property {boolean} [headless=true] - Run browser in headless mode
*/
/**
* Flatten all articles from links.json, sort newest-first, then process
* each one sequentially (shares a single browser launch per article via
* processArticle).
*
* @param {ProcessAllOptions} [options]
*/
export async function processAll(options = {}) {
const { limit, skipProcessed = true, headless = true } = options
const sources = readJsonSync(LINKS_PATH)
/** @type {Article[]} */
const articles = sources
.flatMap((source) => source.items ?? [])
.filter((item) => item.link && item.date)
.sort((a, b) => dayjs(b.date).valueOf() - dayjs(a.date).valueOf())
const processed = loadProcessed()
const pending = skipProcessed
? articles.filter((a) => !processed[a.link])
: articles
const target = limit ? pending.slice(0, limit) : pending
console.log(`Total articles : ${articles.length}`)
console.log(`Already done : ${Object.keys(processed).length}`)
console.log(`To process : ${target.length}`)
if (target.length === 0) {
console.log('Nothing to do.')
return
}
console.log('')
let successCount = 0
let skipCount = 0
let errorCount = 0
for (const article of target) {
const result = await processArticle(article, { headless })
if (result.success) successCount++
else if (result.skipped) skipCount++
else errorCount++
}
console.log(
`\nFinished — success: ${successCount}, skipped: ${skipCount}, errors: ${errorCount}`,
)
}
// ── CLI entry-point ──────────────────────────────────────────────────────────
// Parse simple --key=value flags from argv
function parseArgs(argv) {
const args = {}
for (const arg of argv) {
const m = arg.match(/^--([^=]+)(?:=(.*))?$/)
if (m) args[m[1]] = m[2] !== undefined ? m[2] : true
}
return args
}
const args = parseArgs(process.argv.slice(2))
const options = {
limit: args.limit ? Number(args.limit) : undefined,
skipProcessed: args.force ? false : true,
headless: args.headless !== 'false',
}
processAll(options).catch((err) => {
console.error(err)
process.exit(1)
})
================================================
FILE: article-to-md/src/once.js
================================================
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import fs from 'fs-extra'
const { existsSync, readJsonSync } = fs
import { processArticle } from './processor.js'
import { regenerateWritemd, QINIU_ENABLED } from './utils.js'
import { runUpload } from './upload.js'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
export default async function once(article) {
const result = await processArticle(article, { force: true, headless: true })
return result
}
// Only run directly when this file is the entry point, not when imported by server.js
if (process.argv[1].endsWith('once.js')) {
const newArticlesPath = join(__dirname, '..', '..', 'server', 'node_modules', 'new-articles.json')
if (existsSync(newArticlesPath)) {
const articles = readJsonSync(newArticlesPath)
console.log(`[once] Found ${articles.length} new article(s) to process`)
for (const article of articles) {
try {
console.log(`[once] Processing: ${article.title}`)
await once(article)
} catch (err) {
console.error(`[once] Failed: ${article.title} — ${err.message}`)
}
}
let newData = {
length: 0,
titles: [],
rss: {},
links: {},
articles: []
}
articles.forEach((curr) => {
newData.length++
newData.rss[curr.rssTitle] = true
newData.links[curr.link] = true
newData.titles.push(curr.title)
newData.articles.push({ ...curr })
})
regenerateWritemd(newData)
if (QINIU_ENABLED) {
try {
const { ok, skip, fail } = await runUpload()
console.log(`[once] Upload done: ok=${ok}, skip=${skip}, fail=${fail}`)
} catch (err) {
console.error('[once] Upload failed:', err.message)
}
}
console.log('[once] Done')
} else {
console.log('[once] No new-articles.json found, skipping')
}
}
================================================
FILE: article-to-md/src/processor.js
================================================
import { chromium } from 'playwright'
import TurndownService from 'turndown'
import dayjs from 'dayjs'
import fs from 'fs-extra'
const { existsSync, outputFileSync, readJsonSync, outputJsonSync, removeSync } = fs
import { fileURLToPath } from 'node:url'
import { join } from 'node:path'
import * as cheerio from 'cheerio'
import { urlToMd5, loadProcessed, withProcessedUpdate } from './utils.js'
import { getRuleForUrl } from './rules/index.js'
import { localizeImages } from './images.js'
import { STEALTH_ARGS, applyStealthScripts, simulateHuman } from './stealth.js'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
// Resolves to front-end-rss/data/articles/
const ARTICLES_DIR = join(__dirname, '..', '..', 'data', 'articles')
const DELETED_PATH = join(__dirname, '..', '..', 'data', 'deleted.json')
/** Per-link lock so concurrent /once requests for the same article serialize. */
const linkLockChains = new Map()
/**
* Run fn exclusively for this link (one at a time per link).
* @param {string} link
* @param {() => Promise<T>} fn
* @returns {Promise<T>}
* @template T
*/
async function withLinkLock(link, fn) {
const prev = linkLockChains.get(link) ?? Promise.resolve()
const run = prev.then(() => fn())
linkLockChains.set(link, run)
try {
return await run
} finally {
if (linkLockChains.get(link) === run) linkLockChains.delete(link)
}
}
const LINKS_PATH = join(__dirname, '..', '..', 'data', 'links.json')
/**
* Extract a normalised language identifier from a raw class string.
* Handles common patterns from highlight.js, Prism, SyntaxHighlighter, etc.
* @param {string} raw - combined class attribute value(s)
* @returns {string} e.g. "javascript", or "" when undetected
*/
function detectLang(raw = '') {
// language-xxx / lang-xxx (Prism, highlight.js, standard)
let m = raw.match(/\b(?:language|lang)-(\w+)/)
if (m) return m[1]
// hljs <lang> or <lang> hljs
m = raw.match(/\bhljs\b[^a-z]*(\w+)/) ?? raw.match(/(\w+)[^a-z]*\bhljs\b/)
if (m && m[1] !== 'hljs') return m[1]
// brush: <lang> (SyntaxHighlighter)
m = raw.match(/\bbrush:\s*(\w+)/)
if (m) return m[1]
// xxx__lang (e.g. code-snippet__js -> js)
m = raw.match(/code-snippet__(\w+)/)
if (m) {
const lang = m[1].toLowerCase()
return lang
}
// lang_xxx (e.g. js_darkmode__1 -> js)
m = raw.match(/\b(\w+)_darkmode__\w+/)
if (m) {
const lang = m[1].toLowerCase()
return lang
}
// prettyprint (Google Code Prettify keeps lang in a separate class)
m = raw.replace('prettyprint', '').match(/\b([\w+#-]{2,})\b/)
if (m && !['code', 'pre', 'wrap', 'copy'].includes(m[1])) return m[1]
return ''
}
/**
* Replace strings matching known secret / token patterns with a safe
* placeholder so GitHub push-protection never blocks a commit.
*
* Only well-known, high-entropy formats are targeted; plain prose that
* merely mentions a token *prefix* (e.g. "tokens start with xoxb-") is
* left untouched because it won't match the full pattern.
*
* @param {string} text
* @returns {string}
*/
function sanitizeSecrets(text) {
return (
text
// Slack xoxb- / xoxp- / xoxa- / xoxr- / xoxs-
.replace(/xox[baprs]-\d{10,13}-\d{10,13}-[0-9A-Za-z]{24,}/g, 'xox*-[REDACTED]')
// GitHub classic PAT ghp_ / gho_ / ghu_ / ghs_ / ghr_
.replace(/gh[pousr]_[0-9A-Za-z]{36,}/g, 'gh*_[REDACTED]')
// GitHub fine-grained PAT
.replace(/github_pat_[0-9A-Za-z_]{82}/g, 'github_pat_[REDACTED]')
// GitLab PAT
.replace(/glpat-[0-9A-Za-z_-]{20}/g, 'glpat-[REDACTED]')
// AWS Access Key ID
.replace(/\bAKIA[0-9A-Z]{16}\b/g, 'AKIA[REDACTED]')
// OpenAI secret key (classic format)
.replace(/\bsk-[A-Za-z0-9]{20}T3BlbkFJ[A-Za-z0-9]{20}\b/g, 'sk-[REDACTED]')
// npm token
.replace(/\bnpm_[A-Za-z0-9]{36}\b/g, 'npm_[REDACTED]')
)
}
/**
* @typedef {Object} Article
* @property {string} title
* @property {string} link
* @property {string} [date]
* @property {string} [rssTitle]
*/
/**
* @typedef {Object} ProcessOptions
* @property {boolean} [force=false] - Re-process even if already in processed.json
* @property {boolean} [headless=true] - Run browser in headless mode
* @property {boolean} [_retried=false] - Internal flag to prevent infinite retry loops
*/
/**
* @typedef {Object} ProcessResult
* @property {boolean} [success]
* @property {boolean} [skipped]
* @property {string} [error]
* @property {string} md5
* @property {string} [outputPath]
*/
/**
* Process a single article: fetch via Playwright, convert to Markdown,
* download & compress images, then save everything under
* data/articles/<md5>/page.md (images → data/articles/<md5>/images/).
*
* @param {Article} article
* @param {ProcessOptions} [options]
* @returns {Promise<ProcessResult>}
*/
export async function processArticle(article, options = {}) {
const { force = false, headless = true } = options
const { title, link, date } = article
const md5 = urlToMd5(link)
const processed = loadProcessed()
if (!force && processed[link]) {
console.log(`[skip] ${title}`)
return { skipped: true, md5 }
}
return withLinkLock(link, async () => {
// Clean up previous output so stale images don't accumulate
if (force) {
const articleDir = join(ARTICLES_DIR, md5)
if (existsSync(articleDir)) {
removeSync(articleDir)
console.log(`[clean] ${articleDir}`)
}
}
console.log(`[fetch] ${title}\n ${link}`)
const rule = getRuleForUrl(link)
let browser
try {
browser = await chromium.launch({
headless,
args: STEALTH_ARGS,
})
const context = await browser.newContext({
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
viewport: { width: 1920, height: 1080 },
locale: 'zh-CN',
timezoneId: 'Asia/Shanghai',
extraHTTPHeaders: {
'Accept-Language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7',
'sec-ch-ua': '"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"macOS"',
},
})
// Inject JS stealth patches before any page navigation
await applyStealthScripts(context)
const page = await context.newPage()
await page.goto(link, {
waitUntil: rule.waitUntil ?? 'networkidle',
timeout: 30_000,
})
// Simulate human interaction to pass timing-based checks
await simulateHuman(page)
// Optional domain-specific hook (e.g. dismiss overlays)
if (rule.preProcess) {
await rule.preProcess(page)
}
// Wait for the content element(s) to appear, best-effort
const contentSelectors = Array.isArray(rule.contentSelector)
? rule.contentSelector
: [rule.contentSelector]
await Promise.all(
contentSelectors.map((sel) =>
page.waitForSelector(sel, { timeout: 8_000 }).catch(() => {})
)
)
// Resolve lazy-loaded images: prefer data-src (and other common lazy attrs)
// Use setAttribute so the attribute is guaranteed to update in innerHTML.
// Normalize to an absolute URL to handle protocol-relative (//cdn…) srcs.
await page.evaluate(() => {
document.querySelectorAll('img').forEach((img) => {
const lazySrc =
img.dataset.src ||
img.dataset.original ||
img.dataset.lazySrc ||
img.dataset.lazyload
if (lazySrc) {
try {
img.setAttribute('src', new URL(lazySrc, location.href).href)
} catch {
img.setAttribute('src', lazySrc)
}
}
})
})
// Normalize code blocks so multi-line code is preserved correctly
await page.evaluate(() => {
// 1. <br> inside <pre>/<code> → real newline text node
document.querySelectorAll('pre, code').forEach((el) => {
el.querySelectorAll('br').forEach((br) => {
br.replaceWith(document.createTextNode('\n'))
})
})
// 2. Reconstruct <pre><code> from line-per-element patterns.
// Covers WeChat's own code block (each line is a <span> inside <code>)
// and other editors that wrap every line in a block element.
// Skip if the <pre> already carries an hljs class — in that case the
// child <span> elements are syntax-highlight tokens, not line containers.
document.querySelectorAll('pre').forEach((pre) => {
if (pre.classList.contains('hljs')) return
const code = pre.querySelector('code') ?? pre
if (code.classList.contains('hljs')) return
const lines = code.querySelectorAll(
':scope > span, :scope > p, :scope > div, :scope > li',
)
// Only rewrite when there are multiple line elements and the
// current textContent has no real newlines (i.e. it IS collapsed)
if (lines.length > 1 && !code.textContent.includes('\n')) {
const text = [...lines].map((l) => l.textContent).join('\n')
code.textContent = text
}
})
})
// Extract inner HTML of the content element(s) + log img situation.
// When contentSelector is an array, each matched element's innerHTML is
// concatenated in order and separated by a blank line.
// If an img's displayed width >= 30% of content container width, mark data-rss-block-img so it renders on its own line in Markdown.
const { contentHtml, contentImgs } = await page.evaluate((selectors) => {
const els = selectors
.map((sel) => document.querySelector(sel))
.filter(Boolean)
const imgSource = els.length ? els : [document.body]
// If img is inside strong/b, and every node from img up to that strong has only one child (max 4 levels), unwrap so we don't get **![]()** in Markdown
imgSource.forEach((el) => {
const imgs = [...el.querySelectorAll('img')]
imgs.forEach((img) => {
let p = img.parentNode
for (let level = 0; level < 4 && p; level++) {
if (p.children.length !== 1) break
if (p.nodeType === 1 && (p.tagName === 'STRONG' || p.tagName === 'B')) {
const parent = p.parentNode
if (parent) {
while (p.firstChild) {
parent.insertBefore(p.firstChild, p)
}
parent.removeChild(p)
}
break
}
p = p.parentNode
}
})
})
const blockImageRatio = 0.3
imgSource.forEach((el) => {
const contentWidth = el.getBoundingClientRect().width
el.querySelectorAll('img').forEach((img) => {
const displayWidth = img.getBoundingClientRect().width
if (contentWidth > 0 && displayWidth >= contentWidth * blockImageRatio) {
img.setAttribute('data-rss-block-img', '1')
}
})
})
const html = els.length
? els.map((el) => el.innerHTML).join('\n')
: document.body.innerHTML
const imgs = imgSource.flatMap((el) =>
[...el.querySelectorAll('img')].map((img) => ({
src: img.getAttribute('src') ?? '',
}))
)
return { contentHtml: html, contentImgs: imgs }
}, contentSelectors)
// Use cheerio to clean up the extracted HTML
const $ = cheerio.load(contentHtml)
// Remove excluded elements
;['script', 'style', ...(rule.excludeSelectors || [])].forEach((sel) => $(sel).remove())
// Normalize image src to absolute URLs using the article page as base.
// Handles protocol-relative (//cdn…) and relative (/path or ../path) srcs.
$('img[src]').each((_, el) => {
const src = $(el).attr('src')
if (src && !src.startsWith('http')) {
try {
$(el).attr('src', new URL(src, link).href)
} catch {}
}
})
// Convert <table> elements before Turndown runs (turndown-plugin-gfm uses
// browser-only table.rows / tr.cells which don't exist in Node.js DOM).
//
// Strategy:
// - Data table (has <th> or <thead>) → convert to GFM Markdown table
// - Layout table (no headers) → unwrap, keep cell content as-is
//
// Process from deepest nesting level first so inner tables are resolved
// before their parent tables are evaluated.
const tableMd = new Map() // placeholder → markdown string
let tableIdx = 0
const sortedTables = $('table').toArray().sort(
(a, b) => $(b).parents('table').length - $(a).parents('table').length
)
for (const table of sortedTables) {
const $table = $(table)
const hasExplicitHeader = $table.find('> * th, > * > * th, thead').length > 0
// Collect all rows first so we can check dimensions for implicit data tables
const rows = []
$table.find('tr').each((_, tr) => {
const cells = $(tr).children('th, td').map((_, cell) => {
return $(cell)
.text()
.replace(/[\n\r]+/g, ' ')
.replace(/\s{2,}/g, ' ')
.replace(/\|/g, '\\|')
.trim() || ' '
}).get()
if (cells.length) rows.push(cells)
})
// A table with 2+ columns and 2+ rows is treated as a data table even
// when it has no explicit <th>/<thead> markup (first row becomes header).
// Single-column or single-row tables without headers are layout tables.
const maxCols = rows.length ? Math.max(...rows.map((r) => r.length)) : 0
const isDataTable = hasExplicitHeader || (rows.length >= 2 && maxCols >= 2)
if (!isDataTable) {
// Layout table: unwrap each <td>/<th> content, discard table chrome
$table.find('tr').each((_, tr) => {
// Separate cells with a newline so content doesn't run together
const cellsHtml = $(tr).children('td, th').map((_, cell) => $(cell).html()).get().join('\n')
$(tr).replaceWith(cellsHtml + '\n')
})
$table.replaceWith($table.html() ?? '')
continue
}
if (!rows.length) { $table.remove(); continue }
const cols = maxCols
const normalised = rows.map((r) => {
while (r.length < cols) r.push(' ')
return r.slice(0, cols)
})
// Determine header row:
// 1. explicit <thead> row
// 2. first row whose cells are all <th>
// 3. no explicit header → use first row as implicit header
const headRowIdx = (() => {
const theadRow = $table.find('thead tr').first()
if (theadRow.length) {
let idx = 0
$table.find('tr').each((i, tr) => { if (tr === theadRow[0]) { idx = i } })
return idx
}
const firstTr = $table.find('tr').first()
if (firstTr.find('th').length) return 0
// No explicit header: treat first row as header
return 0
})()
const header = normalised[headRowIdx]
const body = normalised.filter((_, i) => i !== headRowIdx)
const mdLines = [
'| ' + header.join(' | ') + ' |',
'| ' + header.map(() => '---').join(' | ') + ' |',
...body.map((row) => '| ' + row.join(' | ') + ' |'),
]
const key = `\x02MDTABLE${tableIdx++}\x03`
tableMd.set(key, mdLines.join('\n'))
$table.replaceWith(`<p>${key}</p>`)
}
// Unwrap <blockquote> that only contains <pre> (code blocks wrapped in quotes)
$('blockquote').each((_, el) => {
const $bq = $(el)
const children = $bq.children()
if (children.length === 1 && children.first().is('pre')) {
$bq.replaceWith(children.first())
}
})
// Remove any direct children of <pre> that are not <code> (e.g. copy-button
// toolbars, line-number gutter spans injected by syntax highlighters).
$('pre').each((_, el) => {
// Remove non-<code> children, but keep elements that carry an hljs class
// (highlight.js sometimes places <span class="hljs ..."> directly under <pre>)
$(el).children(':not(code)').each((_, child) => {
if (child.type !== 'tag') return
const cls = $(child).attr('class') ?? ''
if (!/hljs/.test(cls)) $(child).remove()
})
})
// Merge multiple <code> siblings inside one <pre> into a single <code>.
// Some sites emit one <code> per line / section inside the same <pre>.
$('pre').each((_, el) => {
const $pre = $(el)
const $codes = $pre.children('code')
if ($codes.length <= 1) return
// Collect language class from first code that has one
let langClass = ''
$codes.each((_, code) => {
if (!langClass) {
const cls = $(code).attr('class') ?? ''
if (/language-\S+/.test(cls)) langClass = cls.match(/language-\S+/)[0]
}
})
// Join all code blocks with a newline, then replace with a single <code>
const merged = $codes.map((_, code) => $(code).html()).get().join('\n')
$pre.html(`<code${langClass ? ` class="${langClass}"` : ''}>${merged}</code>`)
})
// Ensure every <pre> that lacks a direct <code> child is wrapped in one
// so Turndown always produces a fenced code block
$('pre').each((_, el) => {
if (!$(el).children('code').length) {
$(el).html(`<code>${$(el).html()}</code>`)
}
})
// Normalise language hints to `language-xxx` on the <code> element so
// Turndown's fence rule picks up the identifier correctly.
// Handles: language-js, lang-js, hljs javascript, brush: js, etc.
$('pre').each((_, el) => {
const $pre = $(el)
const $code = $pre.children('code').first()
if (!$code.length) return
const raw = [$pre.attr('class'), $code.attr('class')].join(' ')
const lang = detectLang(raw)
if (lang) $code.attr('class', `language-${lang}`)
})
// Normalise invalid list nesting: ul/ol as direct child of ul/ol (sibling of li)
// e.g. <ul><li>A</li><li>B</li><ul><li>B-1</li></ul></ul> → move inner ul into previous li (B)
// Process deepest first so inner lists are re-parented before outer.
const orphanLists = $('ul > ul, ul > ol, ol > ul, ol > ol').toArray()
.sort((a, b) => $(b).parents('ul,ol').length - $(a).parents('ul,ol').length)
orphanLists.forEach((el) => {
const $el = $(el)
const prev = $el.prev()[0]
if (!prev || prev.type !== 'tag') return
const prevTag = prev.name.toUpperCase()
if (prevTag === 'LI') {
$(prev).append($el)
} else if (prevTag === 'UL' || prevTag === 'OL') {
const lastLi = $(prev).children('li').last()[0]
if (lastLi) $(lastLi).append($el)
}
})
const cleanedHtml = $('body').html() ?? contentHtml
// HTML → Markdown
const td = new TurndownService({
headingStyle: 'atx',
codeBlockStyle: 'fenced',
bulletListMarker: '-',
hr: '---',
})
// Override list item rule:
// - single space after the bullet marker (Turndown default is 3)
// - strip trailing whitespace from each item
// - use 2-space indent per nesting level for continuation lines (ul/ol nested in ul/ol)
// - do not strip leading bullet/ordinal when content is multi-line (nested list)
td.addRule('listItem', {
filter: 'li',
replacement(content, node, options) {
const parent = node.parentNode
const isOrdered = parent.nodeName === 'OL'
// Count list nesting depth (ul/ol ancestors) for correct indent of nested lists
let depth = 0
let p = node.parentNode
while (p && p.nodeType === 1) {
if (p.nodeName === 'UL' || p.nodeName === 'OL') depth++
p = p.parentNode
}
const indent = ' '.repeat(depth)
content = content
.replace(/^\n+/, '')
.replace(/\n+$/, '')
.replace(/\n/gm, `\n${indent}`)
// Only strip leading bullet/ordinal on single-line content (site-injected noise).
// Multi-line content may start with a nested list marker; leave it intact.
const isSingleLine = !content.includes('\n')
if (isSingleLine) {
if (isOrdered) {
content = content.replace(/^\d+\\?[.、\)]\s*/, '').replace(/^[①②③④⑤⑥⑦⑧⑨⑩]\s*/, '')
} else {
content = content.replace(/^\\?[•·\-–—]\s*/, '')
}
}
const prefix = isOrdered
? `${Array.prototype.indexOf.call(parent.children, node) + 1}. `
: `${options.bulletListMarker} `
return prefix + content + (node.nextSibling ? '\n' : '')
},
})
// Preserve <video> tags as raw HTML — Markdown has no native video syntax
td.addRule('video', {
filter: 'video',
replacement(_, node) {
const src = node.getAttribute('src') || ''
if (!src) return ''
const poster = node.getAttribute('poster') || ''
const posterAttr = poster ? ` poster="${poster}"` : ''
return `\n\n<video controls src="${src}"${posterAttr} style="max-width:100%"></video>\n\n`
},
})
function isBlockImg(node) {
return node.nodeName === 'IMG' && node.getAttribute('data-rss-block-img') === '1'
}
function hasAnchorParent(node) {
let p = node.parentNode
for (let level = 0; level < 3 && p; level++) {
if (p.nodeName === 'A') return true
p = p.parentNode
}
return false
}
// Block-image NOT inside <a>: output on its own line.
td.addRule('blockImage', {
filter(node) {
return isBlockImg(node) && !hasAnchorParent(node)
},
replacement(_, node) {
const alt = node.getAttribute('alt') || ''
const src = node.getAttribute('src') || ''
return `\n\n\n\n`
},
priority: 2,
})
// <a> wrapping a block-image: output [](href) on its own line.
td.addRule('blockImageLink', {
filter(node) {
if (node.nodeName !== 'A') return false
const img = node.querySelector('img[data-rss-block-img="1"]')
if (!img) return false
const hasText = node.textContent.replace(img.textContent, '').trim()
return !hasText
},
replacement(_, node) {
const href = node.getAttribute('href') || ''
const img = node.querySelector('img[data-rss-block-img="1"]')
const alt = img?.getAttribute('alt') || ''
const src = img?.getAttribute('src') || ''
return `\n\n[](${href})\n\n`
},
priority: 3,
})
if (rule.turndownRules) {
rule.turndownRules(td)
}
let body = td.turndown(cleanedHtml)
// Restore Markdown tables from placeholders
for (const [key, md] of tableMd) {
body = body.replace(key, `\n\n${md}\n`)
}
// Remove anchor links, e.g. [#](#section-id) that many static-site generators inject
body = body.replace(/\[#\]\(#[^)]*\)\s*/g, '')
// Remove --- horizontal rule line when it appears immediately before # / ## / ### heading
body = body.replace(/(^|\n)---\s*\n+(\s*#{1,3}\s)/g, '$1\n$2')
// Remove leading blank line inside fenced code blocks
body = body.replace(/(^```[^\n]*\n)\n/gm, '$1')
// Domain-specific markdown post-processing
if (rule.postProcess) {
body = rule.postProcess(body)
}
// Remove any H1 lines within the first 5 lines of body — "# title" is always
// prepended separately in fileContent, so body itself must not start with one.
{
const lines = body.split('\n')
for (let i = 0; i < Math.min(5, lines.length); i++) {
if (/^# /.test(lines[i])) {
lines.splice(i, 1)
i--
}
}
body = lines.join('\n')
}
// Collapse "orphan bullet" pattern: a lone •/· on its own line followed by
// blank line(s) then the actual content. Converts:
// • → - 内容
// (blank)
// 内容
body = body.replace(/^[•·] *\n\n+(.+)/gm, '- $1')
// Remove empty list item lines: "- " / "* " / "1. " with nothing but spaces after
body = body.replace(/^[ \t]*(?:[-*+]|\d+[.)])[ \t]*$/gm, '')
// Remove blank lines between adjacent list items (loose list → tight list)
body = body.replace(/^([ \t]*[-*+\d][\.\s].+)\n\n(?=[ \t]*[-*+\d][\.\s])/gm, '$1\n')
// Collapse reference-number + blank-line(s) + http URL, keeping any prefix on its own line.
// e.g. "参考资料\[5\]\n\nhttps://..." → "参考资料\n\[5\] https://..."
// e.g. "\[5\]\n\nhttps://..." → "\[5\] https://..."
body = body.replace(/^(.*?)(\\?\[\d+\\?\])\s*\n\n+(https?:\/\/.+)/gm, (_, prefix, ref, url) => {
return prefix ? `${prefix}\n\n${ref} ${url}` : `${ref} ${url}`
})
// Rule-configured retry: e.g. anti-scraping / environment-check page detected
if (rule.retryOn?.(body)) {
if (!options._retried) {
console.warn(`[retry] ${link} — retryOn matched, retrying once…`)
return processArticle(article, { ...options, force: true, _retried: true })
}
console.warn(`[skip] ${link} — retryOn still matches after one retry`)
return { error: 'retry condition persists after one retry', md5 }
}
// Rule-configured delete: e.g. article removed by its author
if (rule.deleteOn?.(body)) {
console.warn(`[delete] ${link} — deleteOn matched, cleaning up…`)
const articleDir = join(ARTICLES_DIR, md5)
if (existsSync(articleDir)) {
removeSync(articleDir)
}
await withProcessedUpdate((p) => { if (link in p) delete p[link] })
try {
const sources = readJsonSync(LINKS_PATH)
let changed = false
for (const source of sources) {
const before = source.items?.length ?? 0
source.items = (source.items ?? []).filter((item) => item.link !== link)
if (source.items.length < before) changed = true
}
if (changed) {
outputJsonSync(LINKS_PATH, sources)
const deleted = existsSync(DELETED_PATH) ? readJsonSync(DELETED_PATH) : []
if (Array.isArray(deleted) && !deleted.includes(link)) {
deleted.push(link)
outputJsonSync(DELETED_PATH, deleted)
console.log(`[processor] Appended to deleted.json`)
}
}
} catch {}
return { deleted: true, md5 }
}
// Download & compress images, rewrite references to local paths.
// Pass the Playwright context so cookies/headers from the browser session
// are used — required for CDNs that gate images behind referrer/cookies
// (e.g. WeChat's mmbiz.qpic.cn).
const articleDir = join(ARTICLES_DIR, md5)
const imagesDir = join(articleDir, 'images')
const pageOrigin = new URL(link).origin
body = await localizeImages(body, imagesDir, context, pageOrigin)
// Redact any token-like strings that would trigger GitHub secret scanning
body = sanitizeSecrets(body)
// Build YAML front-matter
const formattedDate = date
? dayjs(date).format('YYYY-MM-DD')
: dayjs().format('YYYY-MM-DD')
const safeTitle = title.replace(/"/g, '\\"')
const frontmatter = [
'---',
`title: "${safeTitle}"`,
`link: "${link}"`,
`date: ${formattedDate}`,
`md5: ${md5}`,
'---',
].join('\n')
const fileContent = `${frontmatter}\n\n# ${title}\n\n${body}\n`
// Write to data/articles/<md5>/page.md
const outputPath = join(articleDir, 'page.md')
outputFileSync(outputPath, fileContent, 'utf-8')
// Persist to processed map (mutex so concurrent /once requests don't overwrite)
await withProcessedUpdate((p) => { p[link] = md5 })
console.log(`[done] → ${outputPath}`)
return { success: true, md5, outputPath }
} catch (err) {
console.error(`[error] ${link}\n ${err.message}`)
return { error: err.message, md5 }
} finally {
await browser?.close()
}
})
}
================================================
FILE: article-to-md/src/rules/fenghen.js
================================================
/** @type {import('./index.js').SiteRule} */
export default {
contentSelector: '.content__default',
excludeSelectors: [],
waitUntil: 'load',
}
================================================
FILE: article-to-md/src/rules/index.js
================================================
import weixinRule from './weixin.js'
import fenghenRule from './fenghen.js'
import nodeweeklyRule from './nodeweekly.js'
import javascriptweeklyRule from './javascriptweekly.js'
import zhangxinxuRule from './zhangxinxu.js'
import ruanyifengRule from './ruanyifeng.js'
/**
* @typedef {Object} SiteRule
* @property {string | string[]} contentSelector - CSS selector(s) for the main article content; array items are concatenated in order
* @property {string[]} excludeSelectors - CSS selectors for elements to remove before conversion
* @property {'load'|'domcontentloaded'|'networkidle'|'commit'} [waitUntil] - Playwright waitUntil option
* @property {(page: import('playwright').Page) => Promise<void>} [preProcess] - Custom hook run after page load
* @property {(td: import('turndown')) => void} [turndownRules] - Add custom Turndown rules
* @property {(markdown: string) => string} [postProcess] - Transform the final Markdown string
* @property {(markdown: string) => boolean} [retryOn] - Return true to retry the article once (e.g. anti-scraping page detected)
* @property {(markdown: string) => boolean} [deleteOn] - Return true to delete all saved data for this article (e.g. article removed by author)
*/
/**
* Domain -> rule mapping.
* Keys are matched as substrings of the article hostname so subdomains are
* covered automatically.
* @type {Record<string, SiteRule>}
*/
const RULES = {
'mp.weixin.qq.com': weixinRule,
'fenghen.me': fenghenRule,
'nodeweekly.com': nodeweeklyRule,
'javascriptweekly.com': javascriptweeklyRule,
'zhangxinxu.com': zhangxinxuRule,
'ruanyifeng.com': ruanyifengRule,
}
/**
* Return the rule for a given article URL.
* Throws if no matching rule is found — add the domain to RULES first.
* @param {string} url
* @returns {SiteRule}
*/
export function getRuleForUrl(url) {
let hostname
try {
hostname = new URL(url).hostname
} catch {
throw new Error(`无效的文章 URL:${url}`)
}
for (const [domain, rule] of Object.entries(RULES)) {
if (hostname.includes(domain)) {
return { ...rule }
}
}
throw new Error(
`未找到域名 "${hostname}" 的抓取规则。\n` +
`请先在 src/rules/ 下新建规则文件,并在 src/rules/index.js 的 RULES 中注册该域名。`,
)
}
export { RULES }
================================================
FILE: article-to-md/src/rules/javascriptweekly.js
================================================
export const weeklyRule = {
contentSelector: '#content',
excludeSelectors: [
'#together',
],
waitUntil: 'load',
async preProcess(page, options) {
const delay = (ms) => new Promise((r) => setTimeout(r, ms))
// Give JS time to settle after 'load'
await delay(1500 + Math.random() * 1000)
if (options?.onDelayEnd) {
await options.onDelayEnd(page)
}
// Replace top table content with <blockquote>
await page.evaluate(() => {
document.querySelectorAll('#content .el-splitbar + table.el-content').forEach((table) => {
const tds = table.querySelectorAll('td')
if (tds.length !== 1) return
const blockquote = document.createElement('blockquote')
blockquote.innerHTML = tds[0].innerHTML
table.parentNode.replaceChild(blockquote, table)
})
})
// Remove leading splitbar table if it's the first child of #content
await page.evaluate(() => {
const content = document.querySelector('#content')
const first = content?.firstElementChild
if (first?.tagName === 'TABLE' && first.classList.contains('el-splitbar')) {
first.remove()
}
})
// Replace layout tables used as section headings with proper <h2> elements
await page.evaluate(() => {
document.querySelectorAll('#content table').forEach((table) => {
const tds = table.querySelectorAll('td')
if (tds.length !== 1) return
const ps = tds[0].querySelectorAll('p')
if (ps.length !== 1) return
const p = ps[0]
const fontSizeStyle = p.style.fontSize
if (!fontSizeStyle) return
const fontSize = parseFloat(fontSizeStyle)
if (fontSize < 1.4) return
const text = p.textContent?.trim()
if (!text) { table.remove(); return }
const h2 = document.createElement('h2')
h2.textContent = text
table.parentNode.replaceChild(h2, table)
})
})
// Replace classifieds subtable with <blockquote>: el-content td content in <strong>, then el-md td content
await page.evaluate(() => {
document.querySelectorAll('#content table.el-subtable.classifieds').forEach((table) => {
const parts = []
table.querySelectorAll('table.el-content').forEach((t) => {
t.querySelectorAll('td').forEach((td) => {
parts.push('<strong>' + td.innerText.trim() + '</strong>')
})
})
table.querySelectorAll('table.el-md').forEach((t) => {
t.querySelectorAll('td').forEach((td) => {
parts.push(td.innerHTML)
})
})
const blockquote = document.createElement('blockquote')
blockquote.innerHTML = parts.join(' ')
table.parentNode.replaceChild(blockquote, table)
})
})
// Replace single-cell blue content tables with <blockquote>
await page.evaluate(() => {
document.querySelectorAll('#content table.el-content.blue').forEach((table) => {
const tds = table.querySelectorAll('td')
if (tds.length !== 1) return
const blockquote = document.createElement('blockquote')
blockquote.innerHTML = tds[0].innerHTML
table.parentNode.replaceChild(blockquote, table)
})
})
// Replace single-cell item tables that contain both .name and .desc with <li>
// .name is wrapped in <strong><em>, followed by .desc content
await page.evaluate(() => {
document.querySelectorAll('#content table.el-item').forEach((table) => {
const tds = table.querySelectorAll('td')
if (tds.length !== 1) return
const td = tds[0]
const nameEl = td.querySelector('.name')
const descEl = td.querySelector('.desc')
if (!nameEl || !descEl) return
const li = document.createElement('li')
const strong = document.createElement('strong')
const em = document.createElement('em')
em.innerHTML = '--- ' + nameEl.innerHTML
strong.appendChild(em)
li.insertAdjacentHTML('afterbegin', descEl.innerHTML + ' ')
li.appendChild(strong)
const br = document.createElement('br')
table.parentNode.insertBefore(br, table)
table.parentNode.replaceChild(li, table)
})
})
// Replace single-cell tables whose <td> children are all <p> elements,
// each containing both <a> and <cite>, with a <ul>.
// Each <p> becomes a <li> with its content; <cite> is wrapped in <strong><em>.
await page.evaluate(() => {
document.querySelectorAll('#content table.el-md').forEach((table) => {
const tds = table.querySelectorAll('td')
if (tds.length !== 1) return
const td = tds[0]
const children = [...td.children]
if (!children.length || !children.every((el) => el.tagName === 'P')) return
if (!children.every((p) => p.querySelector('a') && p.querySelector('cite'))) return
const ul = document.createElement('ul')
children.forEach((p) => {
p.querySelectorAll('cite').forEach((cite) => {
const strong = document.createElement('strong')
const em = document.createElement('em')
em.innerHTML = '--- ' + cite.innerHTML
strong.appendChild(em)
cite.replaceWith(strong)
})
const li = document.createElement('li')
li.innerHTML = p.innerHTML
ul.appendChild(li)
})
const br = document.createElement('br')
table.parentNode.insertBefore(br, table)
table.parentNode.replaceChild(ul, table)
})
})
},
}
/** @type {import('./index.js').SiteRule} */
export default {
...weeklyRule,
async preProcess(page) {
await weeklyRule.preProcess(page, {
onDelayEnd: async (page) => {
// Remove the JavaScript Weekly banner from the masthead
await page.evaluate(() => {
const table = document.querySelector('table.el-masthead')
if (!table) return
const td = table.querySelector('td')
if (!td) return
if (td.innerText.trim() === 'JavaScript Weekly') {
table.parentNode.removeChild(table)
}
})
}
})
}
}
================================================
FILE: article-to-md/src/rules/nodeweekly.js
================================================
import { weeklyRule } from './javascriptweekly.js'
/** @type {import('./index.js').SiteRule} */
export default {
...weeklyRule,
excludeSelectors: [
...weeklyRule.excludeSelectors,
'img[src="https://res.cloudinary.com/cpress/image/upload/v1653576619/lgfqinzbdqttwmhvljxb.png"]'
],
}
================================================
FILE: article-to-md/src/rules/ruanyifeng.js
================================================
/** @type {import('./index.js').SiteRule} */
export default {
contentSelector: '#main-content',
excludeSelectors: [],
waitUntil: 'load',
async preProcess(page) {
const delay = (ms) => new Promise((r) => setTimeout(r, ms))
// Give JS time to settle after 'load'
await delay(1500 + Math.random() * 1000)
// Replace submission-invitation paragraphs with <blockquote>
await page.evaluate(() => {
document.querySelectorAll('#main-content p').forEach((p) => {
const text = p.textContent
if (!text.includes('欢迎投稿') || !text.includes('@gmail.com')) return
const blockquote = document.createElement('blockquote')
blockquote.innerHTML = p.innerHTML
p.replaceWith(blockquote)
})
})
},
}
================================================
FILE: article-to-md/src/rules/weixin.js
================================================
/** @type {import('./index.js').SiteRule} */
export default {
contentSelector: '#js_content',
excludeSelectors: [
'#js_pc_qr_code',
'#js_author_name',
'.rich_media_tool',
'.rich_media_area_extra',
'.rich_media_read_later',
'#content_bottom_area',
'#js_profile_qrcode',
'.qr_code_pc_outer',
'.video_iframe',
'script',
'style',
],
// Use 'load' instead of 'networkidle' — WeChat keeps long-polling connections
// open which prevents networkidle from ever firing.
waitUntil: 'load',
async preProcess(page) {
const delay = (ms) => new Promise((r) => setTimeout(r, ms))
// Give JS time to settle after 'load'
await delay(1500 + Math.random() * 1000)
// Remove any verification / paywall overlay injected after load
await page.evaluate(() => {
const selectors = [
'#verify_box_new',
'.weui-mask',
'.weui-half-screen-dialog',
'#login_modal_area',
'.weui-dialog',
'#js_dialog_tips',
'.video_iframe',
]
selectors.forEach((sel) => {
document.querySelectorAll(sel).forEach((el) => el.remove())
})
// Restore content visibility in case WeChat blurred/hid it
const content = document.getElementById('js_content')
if (content) {
content.style.visibility = 'visible'
content.style.filter = 'none'
content.style.opacity = '1'
}
})
await delay(300)
// If the article has swiper indicator dots (.swiper_indicator_item_pc), extract
// the full-size image URL from each dot's background-image style and append
// the images to #js_content.
//
// Dot style: background-image: url("https://mmbiz.qpic.cn/.../300?wx_fmt=jpeg&wxfrom=12")
// Full-size: https://mmbiz.qpic.cn/.../0?wx_fmt=jpeg
await page.evaluate(() => {
const indicators = document.querySelectorAll(
'.share_media_swiper_wrp .swiper_indicator_item_pc'
)
if (!indicators.length) return
const content = document.getElementById('js_content')
if (!content) return
indicators.forEach((dot) => {
const style = dot.getAttribute('style') || dot.style.cssText || ''
// Extract URL from background-image: url("...") or url('...')
const match = style.match(/background-image\s*:\s*url\(\s*["']?([^"')]+)["']?\s*\)/)
if (!match) return
let url = match[1]
// Decode HTML entities (" → ")
.replace(/"/g, '"')
.replace(/&/g, '&')
.trim()
// Convert thumbnail size to full-size:
// .../300?wx_fmt=jpeg&wxfrom=12 → .../0?wx_fmt=jpeg
url = url.replace(/\/\d+\?wx_fmt=(\w+).*$/, '/0?wx_fmt=$1')
const img = document.createElement('img')
img.setAttribute('src', url)
const p = document.createElement('p')
p.appendChild(img)
content.appendChild(p)
})
})
},
retryOn: (body) => /## 环境异常/.test(body) && /当前环境异常,完成验证后即可继续访问/.test(body),
deleteOn: (body) => (/该内容已被发布者删除/.test(body) && /\[微信公众平台运营中心\]/.test(body)) || (/内容因违规无法查看/.test(body) && /\[微信公众平台运营中心\]/.test(body)),
}
================================================
FILE: article-to-md/src/rules/zhangxinxu.js
================================================
/** @type {import('./index.js').SiteRule} */
export default {
contentSelector: '#content article',
excludeSelectors: [
'.navigation.nav_swipe',
'p:contains("(本篇完)")',
'.wwads-cn',
'.hljs a.copy',
'.hljs a.revert',
],
waitUntil: 'load',
async preProcess(page) {
const delay = (ms) => new Promise((r) => setTimeout(r, ms))
// Give JS time to settle after 'load'
await delay(1500 + Math.random() * 1000)
// Replace the first child of <article> if it's a .link containing "zhangxinxu" with a <blockquote>
await page.evaluate(() => {
const article = document.querySelector('#content article')
if (!article) return
const first = article.firstElementChild
if (!first || !first.classList.contains('link')) return
if (!first.textContent.includes('zhangxinxu')) return
const blockquote = document.createElement('blockquote')
blockquote.innerHTML = first.innerHTML
first.replaceWith(blockquote)
})
},
}
================================================
FILE: article-to-md/src/server.js
================================================
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import fs from 'fs-extra'
const { existsSync, removeSync, readJsonSync, outputJsonSync } = fs
import { fileURLToPath } from 'node:url'
import { join } from 'node:path'
import once from './once.js'
import { uploadArticleFolder, uploadProcessedJson, uploadLinksJson } from './upload.js'
import { urlToMd5, withProcessedUpdate, regenerateSiteFiles, regenerateWritemd, QINIU_ENABLED } from './utils.js'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const ARTICLES_DIR = join(__dirname, '..', '..', 'data', 'articles')
const LINKS_PATH = join(__dirname, '..', '..', 'data', 'links.json')
const RSS_PATH = join(__dirname, '..', '..', 'data', 'rss.json')
const DELETED_PATH = join(__dirname, '..', '..', 'data', 'deleted.json')
const PROJECT_ROOT = join(__dirname, '..', '..')
const PORT = 8081
const app = new Hono()
app.use('*', cors())
app.post('/article-to-md/once', async (c) => {
const article = await c.req.json().catch(() => null)
if (!article?.link) {
return c.json({ error: 'Missing required field: link' }, 400)
}
console.log(`[server] Processing: ${article.title ?? article.link}`)
try {
const result = await once(article)
if (QINIU_ENABLED) {
if (result?.md5) {
try {
const uploadResult = await uploadArticleFolder(result.md5)
result.upload = uploadResult
} catch (uploadErr) {
console.error('[server] Upload article folder failed:', uploadErr.message)
result.upload = { error: uploadErr.message }
}
}
try {
const pjResult = await uploadProcessedJson()
result.uploadProcessedJson = pjResult
} catch (pjErr) {
console.error('[server] Upload processed.json failed:', pjErr.message)
result.uploadProcessedJson = { error: pjErr.message }
}
}
return c.json(result)
} catch (err) {
console.error('[server] Error:', err.message)
return c.json({ error: err.message }, 500)
}
})
app.post('/article-to-md/remove', async (c) => {
const body = await c.req.json().catch(() => null)
if (!body?.link) {
return c.json({ error: 'Missing required field: link' }, 400)
}
const { link } = body
const md5 = urlToMd5(link)
console.log(`[server] Removing: ${link}`)
// 1. Delete article directory
const articleDir = join(ARTICLES_DIR, md5)
if (existsSync(articleDir)) {
removeSync(articleDir)
console.log(`[server] Deleted directory: ${articleDir}`)
}
// 2. Remove from processed.json (mutex for concurrent safety)
await withProcessedUpdate((p) => { if (link in p) delete p[link] })
console.log(`[server] Removed from processed.json`)
// 3. Remove from links.json
let removedFromLinks = false
try {
const sources = readJsonSync(LINKS_PATH)
for (const source of sources) {
const before = source.items?.length ?? 0
source.items = (source.items ?? []).filter((item) => item.link !== link)
if (source.items.length < before) removedFromLinks = true
}
if (removedFromLinks) {
outputJsonSync(LINKS_PATH, sources)
console.log(`[server] Removed from links.json`)
}
} catch (err) {
console.warn(`[server] Failed to update links.json: ${err.message}`)
}
// 4. Append deleted URL to data/deleted.json
if (removedFromLinks) {
try {
const deleted = existsSync(DELETED_PATH) ? readJsonSync(DELETED_PATH) : []
if (Array.isArray(deleted) && !deleted.includes(link)) {
deleted.push(link)
outputJsonSync(DELETED_PATH, deleted)
console.log(`[server] Appended to deleted.json`)
}
} catch (err) {
console.warn(`[server] Failed to update deleted.json: ${err.message}`)
}
}
try {
await uploadProcessedJson()
await uploadLinksJson()
console.log('[server] Uploaded processed.json/links.json')
} catch (err) {
console.error('[server] Upload processed.json/links.json failed:', err.message)
}
let distRegenerated = false
let writemdRegenerated = false
if (removedFromLinks) {
distRegenerated = regenerateSiteFiles().ok
writemdRegenerated = regenerateWritemd().ok
}
return c.json({ ok: true, md5, removedFromLinks, distRegenerated, writemdRegenerated })
})
app.post('/article-to-md/remove-source', async (c) => {
const body = await c.req.json().catch(() => null)
const title = body?.title
if (!title || typeof title !== 'string') {
return c.json({ error: 'Missing or invalid field: title' }, 400)
}
console.log(`[server] Removing source: ${title}`)
// 1. Remove from rss.json
let rssRemoved = false
try {
const rssList = readJsonSync(RSS_PATH)
const filtered = rssList.filter((item) => item.title !== title)
if (filtered.length < rssList.length) {
outputJsonSync(RSS_PATH, filtered, { spaces: 2 })
rssRemoved = true
console.log(`[server] Removed from rss.json`)
}
} catch (err) {
console.warn(`[server] Failed to update rss.json: ${err.message}`)
return c.json({ error: `rss.json: ${err.message}` }, 500)
}
// 2. Get all article links for this source from links.json, then remove the source entry
let linksToRemove = []
try {
const sources = readJsonSync(LINKS_PATH)
const sourceIndex = sources.findIndex((s) => s.title === title)
if (sourceIndex === -1) {
const detailPath = join(PROJECT_ROOT, 'details', title.replace(/[\\/]/g, '') + '.md')
if (existsSync(detailPath)) removeSync(detailPath)
const writemdRegenerated = rssRemoved && regenerateWritemd().ok
const distRegenerated = rssRemoved && regenerateSiteFiles().ok
return c.json({
ok: true,
rssRemoved,
articlesRemoved: 0,
writemdRegenerated,
distRegenerated,
})
}
const source = sources[sourceIndex]
linksToRemove = (source.items ?? []).map((item) => item.link).filter(Boolean)
sources.splice(sourceIndex, 1)
outputJsonSync(LINKS_PATH, sources)
console.log(`[server] Removed source from links.json, ${linksToRemove.length} article(s)`)
} catch (err) {
console.warn(`[server] Failed to update links.json: ${err.message}`)
return c.json({ error: `links.json: ${err.message}` }, 500)
}
// 3. For each article: remove from processed.json and delete article directory
for (const link of linksToRemove) {
const md5 = urlToMd5(link)
await withProcessedUpdate((p) => { if (link in p) delete p[link] })
const articleDir = join(ARTICLES_DIR, md5)
if (existsSync(articleDir)) {
removeSync(articleDir)
console.log(`[server] Deleted directory: ${articleDir}`)
}
}
// 4. Delete the source's detail page (details/<title>.md)
const detailPath = join(PROJECT_ROOT, 'details', title.replace(/[\\/]/g, '') + '.md')
if (existsSync(detailPath)) {
removeSync(detailPath)
console.log(`[server] Deleted detail page: ${detailPath}`)
}
// 5. Regenerate README/TAGS/details (writemd) and site dist (createFiles)
const writemdRegenerated = regenerateWritemd().ok
const distRegenerated = regenerateSiteFiles().ok
return c.json({
ok: true,
rssRemoved,
articlesRemoved: linksToRemove.length,
writemdRegenerated,
distRegenerated,
})
})
serve({ fetch: app.fetch, port: PORT }, () => {
console.log(`[server] Listening on http://localhost:${PORT}`)
console.log(`[server] POST /article-to-md/once { title?, link, date? }`)
console.log(`[server] POST /article-to-md/remove { link }`)
console.log(`[server] POST /article-to-md/remove-source { title }`)
})
================================================
FILE: article-to-md/src/stealth.js
================================================
/**
* Chromium launch args that suppress automation signals.
* @type {string[]}
*/
export const STEALTH_ARGS = [
'--disable-blink-features=AutomationControlled',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--disable-gpu',
'--window-size=1920,1080',
'--start-maximized',
]
/**
* Inject stealth patches into every new page before any navigation.
* Removes all traces that Playwright / Chrome DevTools left in the JS runtime.
*
* @param {import('playwright').BrowserContext} context
*/
export async function applyStealthScripts(context) {
await context.addInitScript(() => {
// 1. Hide webdriver flag
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined,
configurable: true,
})
// 2. Spoof plugins so the list is non-empty (headless Chrome has none)
const makePlugin = (name, desc, filename, mimeTypes) => {
const plugin = Object.create(Plugin.prototype)
Object.defineProperties(plugin, {
name: { value: name },
description: { value: desc },
filename: { value: filename },
length: { value: mimeTypes.length },
})
mimeTypes.forEach((mt, i) => {
const mime = Object.create(MimeType.prototype)
Object.defineProperties(mime, {
type: { value: mt.type },
suffixes: { value: mt.suffixes },
description: { value: mt.desc },
enabledPlugin: { value: plugin },
})
plugin[i] = mime
})
return plugin
}
const fakePlugins = [
makePlugin('PDF Viewer', 'Portable Document Format', 'internal-pdf-viewer', [
{ type: 'application/pdf', suffixes: 'pdf', desc: '' },
{ type: 'text/pdf', suffixes: 'pdf', desc: '' },
]),
makePlugin('Chrome PDF Viewer', 'Portable Document Format', 'internal-pdf-viewer', [
{ type: 'application/pdf', suffixes: 'pdf', desc: '' },
]),
makePlugin('Chromium PDF Viewer', 'Portable Document Format', 'internal-pdf-viewer', [
{ type: 'application/pdf', suffixes: 'pdf', desc: '' },
]),
]
Object.defineProperty(navigator, 'plugins', {
get: () => {
const arr = [...fakePlugins]
arr.__proto__ = PluginArray.prototype
return arr
},
configurable: true,
})
// 3. Spoof language list
Object.defineProperty(navigator, 'languages', {
get: () => ['zh-CN', 'zh', 'en-US', 'en'],
configurable: true,
})
// 4. Add window.chrome runtime that real Chrome exposes
if (!window.chrome) {
Object.defineProperty(window, 'chrome', {
value: {
app: { isInstalled: false, InstallState: {}, RunningState: {} },
runtime: { PlatformOs: {}, PlatformArch: {}, PlatformNaclArch: {}, RequestUpdateCheckStatus: {} },
loadTimes: function () {},
csi: function () {},
},
configurable: true,
writable: true,
})
}
// 5. Patch Permissions API (headless used to always return 'denied')
if (navigator.permissions && navigator.permissions.query) {
const originalQuery = navigator.permissions.query.bind(navigator.permissions)
navigator.permissions.query = (params) => {
if (params && params.name === 'notifications') {
return Promise.resolve({
state: Notification.permission === 'default' ? 'prompt' : Notification.permission,
onchange: null,
})
}
return originalQuery(params)
}
}
// 6. Fix hairline feature detection used by some fingerprint scripts
if (window.outerWidth === 0) {
Object.defineProperty(window, 'outerWidth', { get: () => 1920 })
Object.defineProperty(window, 'outerHeight', { get: () => 1080 })
}
// 7. Remove CDP-related properties that fingerprint scripts look for
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise
delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol
})
}
/**
* Simulate human-like behavior: random delays + light mouse movement.
* Call this after page.goto() to reduce timing-based detection.
*
* @param {import('playwright').Page} page
*/
export async function simulateHuman(page) {
const delay = (ms) => new Promise((r) => setTimeout(r, ms))
// Random short pause after load
await delay(800 + Math.random() * 1200)
// Move the mouse to a few random positions
const moves = 3 + Math.floor(Math.random() * 3)
for (let i = 0; i < moves; i++) {
await page.mouse.move(
200 + Math.random() * 800,
200 + Math.random() * 400,
{ steps: 10 + Math.floor(Math.random() * 20) },
)
await delay(100 + Math.random() * 300)
}
// Gentle scroll down then back
await page.evaluate(() => window.scrollBy({ top: 300, behavior: 'smooth' }))
await delay(500 + Math.random() * 500)
await page.evaluate(() => window.scrollBy({ top: -100, behavior: 'smooth' }))
await delay(300 + Math.random() * 300)
}
================================================
FILE: article-to-md/src/upload.js
================================================
/**
* 将项目根目录 data/ 下所有文件上传到七牛云存储。
* - 独立运行:在 article-to-md 目录执行 pnpm run upload
* - 被调用:可在 once.js 处理完成后执行 runUpload()
*
* 环境变量(必填,可在 .env 中配置):
* QINIU_ACCESS_KEY 七牛 AK
* QINIU_SECRET_KEY 七牛 SK
* QINIU_BUCKET 空间名
*
* 可选:
* QINIU_PREFIX 对象 key 前缀,默认 "data/"
* QINIU_REGION 区域 ID,如 z0(华东),不设则自动解析
*/
import 'dotenv/config'
import { join, dirname, extname } from 'node:path'
import { fileURLToPath } from 'node:url'
import { createRequire } from 'node:module'
import { createHash } from 'node:crypto'
import fs from 'fs-extra'
const { readFileSync, readdirSync, statSync, pathExistsSync } = fs
const QINIU_CDN_HOST = 'https://fed-data.chanceyu.com'
/** 计算本地文件的 MD5 */
function getFileMD5(filePath) {
return createHash('md5').update(readFileSync(filePath)).digest('hex')
}
const __dirname = dirname(fileURLToPath(import.meta.url))
// 项目根目录的 data(article-to-md 在 repo 根下)
const DATA_DIR = join(__dirname, '..', '..', 'data')
const ARTICLES_DIR = join(DATA_DIR, 'articles')
const PROCESSED_JSON_PATH = join(DATA_DIR, 'processed.json')
const PROCESSED_JSON_KEY = 'processed.json'
const LINKS_JSON_PATH = join(DATA_DIR, 'links.json')
const LINKS_JSON_KEY = 'links.json'
const SKIP_NAMES = new Set([
'.DS_Store',
'Thumbs.db',
'desktop.ini',
])
function isMdKey(key) {
return key.replace(/\\/g, '/').toLowerCase().endsWith('.md')
}
/** 按扩展名返回上传用的 MIME 类型,避免 .md 等被误判为 image/png */
function getMimeType(key) {
const ext = extname(key).toLowerCase()
const map = {
'.md': 'text/markdown; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.html': 'text/html; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.js': 'application/javascript; charset=utf-8',
'.txt': 'text/plain; charset=utf-8',
}
return map[ext] || undefined
}
function getAllFiles(dir, base = '') {
const list = []
const names = readdirSync(dir)
for (const name of names) {
if (SKIP_NAMES.has(name)) continue
const full = join(dir, name)
const rel = base ? join(base, name) : name
const stat = statSync(full)
if (stat.isDirectory()) {
list.push(...getAllFiles(full, rel))
} else if (stat.isFile()) {
list.push({ fullPath: full, key: rel })
}
}
return list
}
/** 创建七牛上传上下文,所有上传均使用 scope bucket:key 覆盖策略 */
function getQiniuContext() {
const accessKey = process.env.QINIU_ACCESS_KEY
const secretKey = process.env.QINIU_SECRET_KEY
const bucket = process.env.QINIU_BUCKET || 'fed-data'
const regionId = process.env.QINIU_REGION
const prefix = (process.env.QINIU_PREFIX || 'data/').replace(/\/?$/, '/')
if (!accessKey || !secretKey || !bucket) {
throw new Error('缺少环境变量:请设置 QINIU_ACCESS_KEY, QINIU_SECRET_KEY, QINIU_BUCKET')
}
const require = createRequire(import.meta.url)
const qiniu = require('qiniu')
const mac = new qiniu.auth.digest.Mac(accessKey, secretKey)
/** 按对象 key 生成允许覆盖的上传凭证(scope = bucket:key) */
const getOverwriteToken = (objectKey) => {
const putPolicy = new qiniu.rs.PutPolicy({ scope: `${bucket}:${objectKey}` })
return putPolicy.uploadToken(mac)
}
const config = new qiniu.conf.Config()
if (regionId) {
config.regionsProvider = qiniu.httpc.Region.fromRegionId(regionId)
}
const formUploader = new qiniu.form_up.FormUploader(config)
const putExtra = new qiniu.form_up.PutExtra()
const bucketManager = new qiniu.rs.BucketManager(mac, config)
const cdnManager = new qiniu.cdn.CdnManager(mac)
const cdnBase = QINIU_CDN_HOST.replace(/\/$/, '')
return { formUploader, putExtra, bucketManager, cdnManager, cdnBase, getOverwriteToken, bucket, prefix }
}
/** 上传文件列表到七牛,返回统计。若远程已存在该 .md 且本次覆盖上传成功,则刷新 CDN 缓存。 */
async function uploadFileList(files, ctx) {
const { formUploader, putExtra, bucketManager, cdnManager, cdnBase, getOverwriteToken, bucket, prefix } = ctx
let ok = 0
let skip = 0
let fail = 0
for (const { fullPath, key } of files) {
const objectKey = prefix + key.replace(/\\/g, '/')
try {
const localMD5 = getFileMD5(fullPath)
let statResp
try {
statResp = await bucketManager.stat(bucket, objectKey)
} catch {
statResp = null
}
const remoteExisted = statResp?.resp?.statusCode === 200 && statResp?.data?.md5
if (remoteExisted) {
const remoteMD5 = String(statResp.data.md5).trim()
if (remoteMD5 === localMD5) {
skip++
continue
}
}
putExtra.mimeType = getMimeType(key)
const uploadToken = getOverwriteToken(objectKey)
const { data, resp } = await formUploader.putFile(
uploadToken,
objectKey,
fullPath,
putExtra
)
if (resp.statusCode === 200) {
ok++
if (isMdKey(key) && remoteExisted && cdnBase) {
const refreshUrl = `${cdnBase}/${objectKey}`
try {
await new Promise((resolve, reject) => {
cdnManager.refreshUrls([refreshUrl], (err, body, info) => {
if (err) return reject(err)
if (info?.statusCode !== 200) return reject(new Error(`CDN refresh ${info?.statusCode}: ${JSON.stringify(body)}`))
resolve()
})
})
console.log(` [cdn] refreshed ${objectKey}`)
} catch (refreshErr) {
console.warn(` [cdn] refresh ${objectKey}:`, refreshErr.message)
}
}
} else {
fail++
console.error(` [fail] ${objectKey} status=${resp.statusCode}`, data)
}
} catch (err) {
fail++
console.error(` [err] ${objectKey}`, err.message)
}
}
return { ok, skip, fail }
}
/**
* 仅上传 data/processed.json 到七牛(key: data/processed.json)。
* 可在 runUpload 末尾或 /article-to-md/once 单篇处理完成后调用。
* @returns {{ ok: number, skip: number, fail: number }}
*/
export async function uploadProcessedJson() {
if (!pathExistsSync(PROCESSED_JSON_PATH) || !statSync(PROCESSED_JSON_PATH).isFile()) {
return { ok: 0, skip: 0, fail: 0 }
}
const ctx = getQiniuContext()
return uploadFileList([{ fullPath: PROCESSED_JSON_PATH, key: PROCESSED_JSON_KEY }], ctx)
}
/**
* 仅上传 data/links.json 到七牛(key: data/links.json)。
* @returns {{ ok: number, skip: number, fail: number }}
*/
export async function uploadLinksJson() {
if (!pathExistsSync(LINKS_JSON_PATH) || !statSync(LINKS_JSON_PATH).isFile()) {
return { ok: 0, skip: 0, fail: 0 }
}
const ctx = getQiniuContext()
return uploadFileList([{ fullPath: LINKS_JSON_PATH, key: LINKS_JSON_KEY }], ctx)
}
/**
* 仅上传指定文章目录 data/articles/:md5 下的所有文件(供接口单篇上传后调用)。
* @param {string} md5 - 文章目录名(即 data/articles 下的子目录名)
* @returns {{ ok: number, skip: number, fail: number }}
*/
export async function uploadArticleFolder(md5) {
if (!md5) {
return { ok: 0, skip: 0, fail: 0 }
}
const articleDir = join(ARTICLES_DIR, md5)
if (!pathExistsSync(articleDir) || !statSync(articleDir).isDirectory()) {
return { ok: 0, skip: 0, fail: 0 }
}
const files = getAllFiles(articleDir, `articles/${md5}`)
if (files.length === 0) {
return { ok: 0, skip: 0, fail: 0 }
}
const ctx = getQiniuContext()
return uploadFileList(files, ctx)
}
/**
* 执行上传:将 data/ 下文件上传到七牛,成功后可选删除 data/articles。
* @returns {{ ok: number, skip: number, fail: number }}
*/
export async function runUpload() {
if (!pathExistsSync(DATA_DIR)) {
throw new Error('data 目录不存在: ' + DATA_DIR)
}
const ctx = getQiniuContext()
const files = getAllFiles(DATA_DIR)
console.log(`共 ${files.length} 个文章目录待上传`)
let ok = 0
let skip = 0
let fail = 0
const mainResult = await uploadFileList(files, ctx)
ok += mainResult.ok
skip += mainResult.skip
fail += mainResult.fail
const pjResult = await uploadProcessedJson()
ok += pjResult.ok
skip += pjResult.skip
fail += pjResult.fail
console.log(`\n完成:成功 ${ok},跳过(已存在且 md5 一致) ${skip},失败 ${fail}`)
if (fail === 0 && pathExistsSync(ARTICLES_DIR)) {
await fs.remove(join(ARTICLES_DIR))
console.log('已清理 data/articles 目录')
}
return { ok, skip, fail }
}
// 直接运行(pnpm run upload)时执行上传并退出
const isMain = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]
if (isMain) {
runUpload()
.then(({ fail }) => process.exit(fail > 0 ? 1 : 0))
.catch((err) => {
console.error(err.message || err)
process.exit(1)
})
}
================================================
FILE: article-to-md/src/utils.js
================================================
import 'dotenv/config'
import { createHash } from 'node:crypto'
import { fileURLToPath } from 'node:url'
import { join } from 'node:path'
import { spawnSync } from 'node:child_process'
import fs from 'fs-extra'
const { existsSync, readJsonSync, outputJsonSync } = fs
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const PROCESSED_PATH = join(__dirname, '..', '..', 'data', 'processed.json')
const PROJECT_ROOT = join(__dirname, '..', '..')
export const QINIU_ENABLED = process.env.QINIU_ACCESS_KEY && process.env.QINIU_SECRET_KEY
/**
* Convert a URL string to its MD5 hex digest
* @param {string} url
* @returns {string}
*/
export function urlToMd5(url) {
return createHash('md5').update(url).digest('hex')
}
/**
* Load the processed map from disk (url -> md5)
* @returns {Record<string, string>}
*/
export function loadProcessed() {
if (!existsSync(PROCESSED_PATH)) return {}
try {
return readJsonSync(PROCESSED_PATH)
} catch {
return {}
}
}
/**
* Persist the processed map to disk
* @param {Record<string, string>} data
*/
export function saveProcessed(data) {
outputJsonSync(PROCESSED_PATH, data)
}
let processedLock = Promise.resolve()
/**
* Run a read-modify-write on processed.json under a mutex so concurrent
* callers (e.g. multiple /article-to-md/once requests) do not overwrite each other.
* @param {(data: Record<string, string>) => void} update - Mutate the loaded object; it will be saved after.
* @returns {Promise<void>}
*/
export function withProcessedUpdate(update) {
const next = processedLock.then(() => {
const data = loadProcessed()
update(data)
saveProcessed(data)
})
processedLock = next.catch(() => {})
return next
}
/**
* Regenerate site dist/data JSON files (articles, indexes, etc.) by running createFiles.
* @returns {{ ok: boolean, stdout?: string, error?: string }}
*/
export function regenerateSiteFiles() {
try {
const proc = spawnSync('node', ['-e', "require('./site/build/createFiles.js')()"], {
cwd: PROJECT_ROOT,
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'pipe'],
})
if (proc.status === 0) {
console.log(`[server] Regenerated dist/data JSON files`)
return { ok: true, stdout: proc.stdout?.trim() }
}
const err = proc.stderr?.trim() || `exit ${proc.status}`
console.warn(`[server] createFiles failed: ${err}`)
return { ok: false, error: err }
} catch (err) {
console.warn(`[server] regenerateSiteFiles: ${err.message}`)
return { ok: false, error: err.message }
}
}
/**
* Regenerate README.md, TAGS.md, deta
gitextract_01fb6mri/
├── .github/
│ └── workflows/
│ └── server.yml
├── .gitignore
├── README.md
├── TAGS.md
├── article-to-md/
│ ├── .gitignore
│ ├── README.md
│ ├── package.json
│ └── src/
│ ├── check-cloud.js
│ ├── images.js
│ ├── index.js
│ ├── once.js
│ ├── processor.js
│ ├── rules/
│ │ ├── fenghen.js
│ │ ├── index.js
│ │ ├── javascriptweekly.js
│ │ ├── nodeweekly.js
│ │ ├── ruanyifeng.js
│ │ ├── weixin.js
│ │ └── zhangxinxu.js
│ ├── server.js
│ ├── stealth.js
│ ├── upload.js
│ └── utils.js
├── data/
│ ├── atom.xml
│ ├── deleted.json
│ ├── hotwords.json
│ ├── links.json
│ ├── processed.json
│ ├── rss.json
│ └── tags.json
├── details/
│ ├── JavaScript-Weekly.md
│ ├── Node-Weekly.md
│ ├── Nodejs技术栈.md
│ ├── iCSS前端趣闻.md
│ ├── tags/
│ │ ├── ai.md
│ │ ├── audio-video.md
│ │ ├── browser.md
│ │ ├── canvas-image.md
│ │ ├── css.md
│ │ ├── dev-desktop.md
│ │ ├── dev-game.md
│ │ ├── dev-mobile.md
│ │ ├── front-end-advanced.md
│ │ ├── git-svn.md
│ │ ├── html.md
│ │ ├── javascript.md
│ │ ├── job-interview.md
│ │ ├── miniprogram.md
│ │ ├── nodejs.md
│ │ ├── optimization.md
│ │ ├── other.md
│ │ ├── pack-build.md
│ │ ├── react.md
│ │ ├── server.md
│ │ ├── typescript.md
│ │ └── vue.md
│ ├── 凹凸实验室.md
│ ├── 前端之巅.md
│ ├── 前端从进阶到入院.md
│ ├── 前端侦探.md
│ ├── 前端大全.md
│ ├── 前端技术优选.md
│ ├── 前端早读课.md
│ ├── 前端精读评论.md
│ ├── 字节前端-ByteFE.md
│ ├── 张鑫旭-鑫空间-鑫生活.md
│ ├── 淘系前端团队.md
│ ├── 程序员成长指北.md
│ ├── 阮一峰的网络日志.md
│ └── 风痕·術&思.md
├── server/
│ ├── app.js
│ ├── dedupe-links.js
│ ├── feed.js
│ ├── fetch.js
│ ├── once.js
│ ├── package.json
│ ├── update.js
│ ├── utils.js
│ └── writemd.js
├── site/
│ ├── .babelrc
│ ├── .editorconfig
│ ├── .eslintignore
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .npmrc
│ ├── .postcssrc.js
│ ├── README.md
│ ├── build/
│ │ ├── build.js
│ │ ├── check-versions.js
│ │ ├── createFiles.js
│ │ ├── data.js
│ │ ├── template-parameters.js
│ │ ├── upload.js
│ │ ├── utils.js
│ │ ├── vue-loader.conf.js
│ │ ├── webpack.base.conf.js
│ │ ├── webpack.dev.conf.js
│ │ └── webpack.prod.conf.js
│ ├── config/
│ │ ├── dev.env.js
│ │ ├── index.js
│ │ └── prod.env.js
│ ├── index.html
│ ├── package.json
│ ├── src/
│ │ ├── App.vue
│ │ ├── components/
│ │ │ ├── Index.vue
│ │ │ └── MarkdownViewer.vue
│ │ ├── main.js
│ │ └── router/
│ │ └── index.js
│ └── static/
│ └── .gitkeep
└── templates/
├── DETAILS.md
├── README.md
└── TAGS.md
SYMBOL INDEX (149 symbols across 28 files)
FILE: article-to-md/src/check-cloud.js
constant DATA_DIR (line 18) | const DATA_DIR = path.join(__dirname, '..', '..', 'data')
constant DEFAULT_PROCESSED (line 19) | const DEFAULT_PROCESSED = path.join(DATA_DIR, 'processed.json')
constant DEFAULT_LINKS (line 20) | const DEFAULT_LINKS = path.join(DATA_DIR, 'links.json')
constant MISSING_PATH (line 21) | const MISSING_PATH = path.join(DATA_DIR, 'missing.json')
constant DEFAULT_HOST (line 22) | const DEFAULT_HOST = 'https://fed-data.chanceyu.com'
constant ARTICLE_DATA_HOST (line 24) | const ARTICLE_DATA_HOST = process.env.ARTICLE_DATA_HOST || DEFAULT_HOST
constant PROCESSED_JSON_PATH (line 25) | const PROCESSED_JSON_PATH = process.env.PROCESSED_JSON_PATH || DEFAULT_P...
constant LINKS_JSON_PATH (line 26) | const LINKS_JSON_PATH = process.env.LINKS_JSON_PATH || DEFAULT_LINKS
function loadProcessed (line 28) | function loadProcessed() {
function loadLinksMap (line 43) | function loadLinksMap() {
function headExists (line 68) | async function headExists(url) {
function main (line 77) | async function main() {
FILE: article-to-md/src/images.js
constant COMPRESSIBLE (line 8) | const COMPRESSIBLE = new Set(['jpeg', 'jpg', 'png', 'webp', 'avif'])
function imageFilename (line 18) | function imageFilename(url, detectedExt) {
constant MAX_WIDTH (line 25) | const MAX_WIDTH = 1000
function compressAndSave (line 33) | async function compressAndSave(raw, destPath) {
constant FETCH_RETRY_MAX (line 73) | const FETCH_RETRY_MAX = 3
constant FETCH_RETRY_DELAY_MS (line 74) | const FETCH_RETRY_DELAY_MS = 2000
constant FETCH_CONCURRENCY (line 75) | const FETCH_CONCURRENCY = 2
constant FETCH_DELAY_BETWEEN_MS (line 76) | const FETCH_DELAY_BETWEEN_MS = 400
function sleep (line 78) | function sleep(ms) {
function fetchImageBuffer (line 94) | async function fetchImageBuffer(url, pageOrigin, browserContext) {
function saveBase64Image (line 149) | async function saveBase64Image(dataUrl, destDir) {
function downloadImage (line 174) | async function downloadImage(url, destDir, pageOrigin, browserContext) {
constant MD_IMG_RE (line 191) | const MD_IMG_RE = /!\[([^\]]*)\]\(((?:https?:\/\/|data:image\/)[^)"\s]+)...
function runWithConcurrency (line 205) | async function runWithConcurrency(tasks, concurrency, delayBetweenMs = 0) {
function localizeImages (line 219) | async function localizeImages(markdown, imagesDir, browserContext = null...
FILE: article-to-md/src/index.js
constant LINKS_PATH (line 16) | const LINKS_PATH = join(__dirname, '..', '..', 'data', 'links.json')
function processAll (line 40) | async function processAll(options = {}) {
function parseArgs (line 85) | function parseArgs(argv) {
FILE: article-to-md/src/once.js
function once (line 11) | async function once(article) {
FILE: article-to-md/src/processor.js
constant ARTICLES_DIR (line 19) | const ARTICLES_DIR = join(__dirname, '..', '..', 'data', 'articles')
constant DELETED_PATH (line 20) | const DELETED_PATH = join(__dirname, '..', '..', 'data', 'deleted.json')
function withLinkLock (line 32) | async function withLinkLock(link, fn) {
constant LINKS_PATH (line 42) | const LINKS_PATH = join(__dirname, '..', '..', 'data', 'links.json')
function detectLang (line 50) | function detectLang(raw = '') {
function sanitizeSecrets (line 95) | function sanitizeSecrets(text) {
function processArticle (line 148) | async function processArticle(article, options = {}) {
FILE: article-to-md/src/rules/index.js
constant RULES (line 26) | const RULES = {
function getRuleForUrl (line 41) | function getRuleForUrl(url) {
FILE: article-to-md/src/rules/javascriptweekly.js
method preProcess (line 7) | async preProcess(page, options) {
method preProcess (line 158) | async preProcess(page) {
FILE: article-to-md/src/rules/ruanyifeng.js
method preProcess (line 6) | async preProcess(page) {
FILE: article-to-md/src/rules/weixin.js
method preProcess (line 20) | async preProcess(page) {
FILE: article-to-md/src/rules/zhangxinxu.js
method preProcess (line 12) | async preProcess(page) {
FILE: article-to-md/src/server.js
constant ARTICLES_DIR (line 14) | const ARTICLES_DIR = join(__dirname, '..', '..', 'data', 'articles')
constant LINKS_PATH (line 15) | const LINKS_PATH = join(__dirname, '..', '..', 'data', 'links.json')
constant RSS_PATH (line 16) | const RSS_PATH = join(__dirname, '..', '..', 'data', 'rss.json')
constant DELETED_PATH (line 17) | const DELETED_PATH = join(__dirname, '..', '..', 'data', 'deleted.json')
constant PROJECT_ROOT (line 18) | const PROJECT_ROOT = join(__dirname, '..', '..')
constant PORT (line 20) | const PORT = 8081
FILE: article-to-md/src/stealth.js
constant STEALTH_ARGS (line 5) | const STEALTH_ARGS = [
function applyStealthScripts (line 22) | async function applyStealthScripts(context) {
function simulateHuman (line 127) | async function simulateHuman(page) {
FILE: article-to-md/src/upload.js
constant QINIU_CDN_HOST (line 25) | const QINIU_CDN_HOST = 'https://fed-data.chanceyu.com'
function getFileMD5 (line 28) | function getFileMD5(filePath) {
constant DATA_DIR (line 34) | const DATA_DIR = join(__dirname, '..', '..', 'data')
constant ARTICLES_DIR (line 35) | const ARTICLES_DIR = join(DATA_DIR, 'articles')
constant PROCESSED_JSON_PATH (line 36) | const PROCESSED_JSON_PATH = join(DATA_DIR, 'processed.json')
constant PROCESSED_JSON_KEY (line 37) | const PROCESSED_JSON_KEY = 'processed.json'
constant LINKS_JSON_PATH (line 38) | const LINKS_JSON_PATH = join(DATA_DIR, 'links.json')
constant LINKS_JSON_KEY (line 39) | const LINKS_JSON_KEY = 'links.json'
constant SKIP_NAMES (line 41) | const SKIP_NAMES = new Set([
function isMdKey (line 47) | function isMdKey(key) {
function getMimeType (line 52) | function getMimeType(key) {
function getAllFiles (line 65) | function getAllFiles(dir, base = '') {
function getQiniuContext (line 83) | function getQiniuContext() {
function uploadFileList (line 115) | async function uploadFileList(files, ctx) {
function uploadProcessedJson (line 180) | async function uploadProcessedJson() {
function uploadLinksJson (line 192) | async function uploadLinksJson() {
function uploadArticleFolder (line 205) | async function uploadArticleFolder(md5) {
function runUpload (line 225) | async function runUpload() {
FILE: article-to-md/src/utils.js
constant PROCESSED_PATH (line 10) | const PROCESSED_PATH = join(__dirname, '..', '..', 'data', 'processed.js...
constant PROJECT_ROOT (line 11) | const PROJECT_ROOT = join(__dirname, '..', '..')
constant QINIU_ENABLED (line 13) | const QINIU_ENABLED = process.env.QINIU_ACCESS_KEY && process.env.QINIU_...
function urlToMd5 (line 20) | function urlToMd5(url) {
function loadProcessed (line 28) | function loadProcessed() {
function saveProcessed (line 41) | function saveProcessed(data) {
function withProcessedUpdate (line 53) | function withProcessedUpdate(update) {
function regenerateSiteFiles (line 67) | function regenerateSiteFiles() {
function regenerateWritemd (line 91) | function regenerateWritemd(newData = { length: 0, titles: [], rss: {}, l...
FILE: server/dedupe-links.js
function run (line 13) | function run() {
FILE: server/feed.js
function createFeed (line 8) | async function createFeed(linksJson){
FILE: server/fetch.js
function fetchFeed (line 6) | async function fetchFeed(rss) {
function initFetch (line 29) | async function initFetch(rssItem, onFinish) {
FILE: server/update.js
function handleUpdate (line 28) | async function handleUpdate() {
function handleCommit (line 39) | function handleCommit() {
function isDateInLast7Days (line 46) | function isDateInLast7Days(dateStr) {
function isDuplicateOfExisting (line 55) | function isDuplicateOfExisting(curr, allExistingItems, deletedUrls) {
function handleFeed (line 67) | function handleFeed() {
FILE: server/utils.js
constant RESP_PATH (line 12) | const RESP_PATH = path.join(__dirname, '../')
constant RSS_PATH (line 13) | const RSS_PATH = path.join(RESP_PATH + '/data/rss.json')
constant LINKS_PATH (line 14) | const LINKS_PATH = path.join(RESP_PATH + '/data/links.json')
constant TAGS_PATH (line 15) | const TAGS_PATH = path.join(RESP_PATH + '/data/tags.json')
constant DELETED_PATH (line 16) | const DELETED_PATH = path.join(RESP_PATH + '/data/deleted.json')
constant FEED_PATH (line 17) | const FEED_PATH = path.join(RESP_PATH + '/data/atom.xml')
constant README_PATH (line 18) | const README_PATH = path.join(RESP_PATH + '/README.md')
constant README_TEMPLATE_PATH (line 19) | const README_TEMPLATE_PATH = path.join(RESP_PATH + '/templates/README....
constant TAGS_MD_PATH (line 20) | const TAGS_MD_PATH = path.join(RESP_PATH + '/TAGS.md')
constant TAGS_TEMPLATE_PATH (line 21) | const TAGS_TEMPLATE_PATH = path.join(RESP_PATH + '/templates/TAGS.md')
constant DETAILS_TEMPLATE_PATH (line 22) | const DETAILS_TEMPLATE_PATH = path.join(RESP_PATH + '/templates/DETAILS...
constant ARTICLES_PROCESSED_PATH (line 23) | const ARTICLES_PROCESSED_PATH = path.join(RESP_PATH + '/data/processed.j...
constant WORKFLOW (line 30) | const WORKFLOW = !!process.env.WORKFLOW
method getLogPrefix (line 54) | getLogPrefix() {
method log (line 58) | log(msg) {
method logInfo (line 62) | logInfo(msg) {
method logWarn (line 66) | logWarn(msg) {
method logSuccess (line 70) | logSuccess(msg) {
method formatTitle (line 77) | formatTitle(title) {
method getNowDate (line 95) | getNowDate(format) {
method formatDate (line 102) | formatDate(time, format) {
method isSameLink (line 111) | isSameLink(link, compare) {
method getHomePage (line 135) | async getHomePage() {
method filterBySkill (line 152) | filterBySkill(items) {
FILE: server/writemd.js
function handleREADME (line 22) | async function handleREADME(newData, linksJson, processedMap) {
function handleTags (line 45) | function handleTags(newData, linksJson, processedMap) {
function handleDetails (line 100) | function handleDetails(newData, linksJson, processedMap, regenerateAll) {
FILE: site/build/check-versions.js
function exec (line 7) | function exec (cmd) {
FILE: site/build/createFiles.js
function loadJieba (line 9) | function loadJieba() {
constant PAGE_SIZE (line 24) | const PAGE_SIZE = 200
constant DIST_PATH (line 25) | const DIST_PATH = path.join(__dirname, '../dist')
constant DATA_DIR (line 26) | const DATA_DIR = path.join(DIST_PATH, 'data')
function prepareArticles (line 31) | function prepareArticles() {
function generateArticlesJson (line 54) | function generateArticlesJson(articles) {
function generateTextIndex (line 66) | function generateTextIndex(articles) {
function generateSourceIndex (line 126) | function generateSourceIndex(articles) {
function generateCategoryIndex (line 146) | function generateCategoryIndex(articles) {
function generateLegacyDataFile (line 179) | function generateLegacyDataFile() {
function compress (line 197) | function compress(filePath, ext) {
function createFiles (line 207) | function createFiles() {
FILE: site/build/data.js
constant RSS_DATA (line 1) | const RSS_DATA = require('../../data/rss.json')
constant TAGS_DATA (line 2) | const TAGS_DATA = require('../../data/tags.json')
constant LINKS_DATA (line 3) | const LINKS_DATA = require('../../data/links.json')
constant HOTWORDS_DATA (line 4) | const HOTWORDS_DATA = require('../../data/hotwords.json')
FILE: site/build/upload.js
constant SITE_DIR (line 21) | const SITE_DIR = path.join(__dirname, '..')
constant DIST_DIR (line 22) | const DIST_DIR = path.join(SITE_DIR, 'dist')
constant SKIP_NAMES (line 29) | const SKIP_NAMES = new Set([
function getFileMD5 (line 35) | function getFileMD5(filePath) {
function getMimeType (line 40) | function getMimeType(key) {
function getAllFiles (line 64) | function getAllFiles(dir, base = '') {
function getQiniuContext (line 81) | function getQiniuContext() {
function uploadFileList (line 109) | async function uploadFileList(files, ctx) {
function runUpload (line 147) | async function runUpload() {
FILE: site/build/utils.js
function generateLoaders (line 33) | function generateLoaders (loader, loaderOptions) {
FILE: site/build/webpack.base.conf.js
function resolve (line 7) | function resolve (dir) {
FILE: site/build/webpack.dev.conf.js
constant HOST (line 14) | const HOST = process.env.HOST
constant PORT (line 15) | const PORT = process.env.PORT && Number(process.env.PORT)
method before (line 49) | before(app) {
FILE: site/build/webpack.prod.conf.js
method minChunks (line 87) | minChunks (module) {
Copy disabled (too large)
Download .json
Condensed preview — 112 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (13,971K chars).
[
{
"path": ".github/workflows/server.yml",
"chars": 2471,
"preview": "name: RSS Runner\n\nrun-name: 'Runner #${{ github.run_number }} ${{ inputs.suffix }}'\n\non:\n workflow_dispatch:\n inputs"
},
{
"path": ".gitignore",
"chars": 212,
"preview": ".DS_Store\n.AppleDouble\n.LSOverride\n.svn\n\n._*\n\n.Spotlight-V100\n.Trashes\n\nThumbs.db\nehthumbs.db\n\nDesktop.ini\n\n$RECYCLE.BIN"
},
{
"path": "README.md",
"chars": 45284,
"preview": "<div align=\"center\"><img width=\"100\" src=\"/assets/rss.gif\" /><h1>Front-End RSS</h1>\n每天定时更新前端技术文章,并推送到 GitHub 方便查看\n</div>"
},
{
"path": "TAGS.md",
"chars": 63745,
"preview": "> 提示:只是根据文章标题简单匹配分类\n\n:alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)\n\n## 文章分类\n\n- [AI](#ai) \n- [React](#react)"
},
{
"path": "article-to-md/.gitignore",
"chars": 19,
"preview": "node_modules/\n.env\n"
},
{
"path": "article-to-md/README.md",
"chars": 1825,
"preview": "# article-to-md\n\n将 `data/links.json` 中的文章 URL 抓取并转换为 Markdown 文件。\n\n## 技术栈\n\n| 依赖 | 用途 |\n|------|------|\n| [playwright](ht"
},
{
"path": "article-to-md/package.json",
"chars": 782,
"preview": "{\n \"name\": \"article-to-md\",\n \"version\": \"1.0.0\",\n \"description\": \"Fetch articles by URL and convert to Markdown files"
},
{
"path": "article-to-md/src/check-cloud.js",
"chars": 3756,
"preview": "#!/usr/bin/env node\n/**\n * 读取 data/processed.json,判断每一个文章对应的 .md 文件在云存储上是否存在,输出日志。\n * 缺失时输出文章标题和链接(标题来源于 data/links.json"
},
{
"path": "article-to-md/src/images.js",
"chars": 8371,
"preview": "import sharp from 'sharp'\nimport fs from 'fs-extra'\nconst { ensureDirSync, writeFileSync } = fs\nimport { join, extname }"
},
{
"path": "article-to-md/src/index.js",
"chars": 3088,
"preview": "import { fileURLToPath } from 'node:url'\nimport { join } from 'node:path'\nimport fs from 'fs-extra'\nconst { readJsonSync"
},
{
"path": "article-to-md/src/once.js",
"chars": 1884,
"preview": "import { join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport fs from 'fs-extra'\nconst { existsSync, "
},
{
"path": "article-to-md/src/processor.js",
"chars": 27964,
"preview": "import { chromium } from 'playwright'\nimport TurndownService from 'turndown'\nimport dayjs from 'dayjs'\nimport fs from 'f"
},
{
"path": "article-to-md/src/rules/fenghen.js",
"chars": 149,
"preview": "/** @type {import('./index.js').SiteRule} */\nexport default {\n contentSelector: '.content__default',\n excludeSelectors"
},
{
"path": "article-to-md/src/rules/index.js",
"chars": 2237,
"preview": "import weixinRule from './weixin.js'\nimport fenghenRule from './fenghen.js'\nimport nodeweeklyRule from './nodeweekly.js'"
},
{
"path": "article-to-md/src/rules/javascriptweekly.js",
"chars": 6210,
"preview": "export const weeklyRule = {\n contentSelector: '#content',\n excludeSelectors: [\n '#together',\n ],\n waitUntil: 'loa"
},
{
"path": "article-to-md/src/rules/nodeweekly.js",
"chars": 297,
"preview": "import { weeklyRule } from './javascriptweekly.js'\n\n/** @type {import('./index.js').SiteRule} */\nexport default {\n ...w"
},
{
"path": "article-to-md/src/rules/ruanyifeng.js",
"chars": 764,
"preview": "/** @type {import('./index.js').SiteRule} */\nexport default {\n contentSelector: '#main-content',\n excludeSelectors: []"
},
{
"path": "article-to-md/src/rules/weixin.js",
"chars": 3197,
"preview": "/** @type {import('./index.js').SiteRule} */\nexport default {\n contentSelector: '#js_content',\n excludeSelectors: [\n "
},
{
"path": "article-to-md/src/rules/zhangxinxu.js",
"chars": 998,
"preview": "/** @type {import('./index.js').SiteRule} */\nexport default {\n contentSelector: '#content article',\n excludeSelectors:"
},
{
"path": "article-to-md/src/server.js",
"chars": 7658,
"preview": "import { serve } from '@hono/node-server'\nimport { Hono } from 'hono'\nimport { cors } from 'hono/cors'\nimport fs from 'f"
},
{
"path": "article-to-md/src/stealth.js",
"chars": 5056,
"preview": "/**\n * Chromium launch args that suppress automation signals.\n * @type {string[]}\n */\nexport const STEALTH_ARGS = [\n '-"
},
{
"path": "article-to-md/src/upload.js",
"chars": 8346,
"preview": "/**\n * 将项目根目录 data/ 下所有文件上传到七牛云存储。\n * - 独立运行:在 article-to-md 目录执行 pnpm run upload\n * - 被调用:可在 once.js 处理完成后执行 runUpload("
},
{
"path": "article-to-md/src/utils.js",
"chars": 3699,
"preview": "import 'dotenv/config'\nimport { createHash } from 'node:crypto'\nimport { fileURLToPath } from 'node:url'\nimport { join }"
},
{
"path": "data/atom.xml",
"chars": 27864,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<feed xmlns=\"http://www.w3.org/2005/Atom\">\n <id>https://fed.chanceyu.com/atom."
},
{
"path": "data/deleted.json",
"chars": 4825,
"preview": "[\"http://mp.weixin.qq.com/s?__biz=MzAxODE2MjM1MA==&mid=2651624085&idx=1&sn=0f1bbccd402eab8124a871c111231efb&chksm=802245"
},
{
"path": "data/hotwords.json",
"chars": 185,
"preview": "[\n \"AI\",\n \"React\",\n \"Vue\",\n \"TypeScript\",\n \"JavaScript\",\n \"Webpack\",\n \"Vite\",\n \"Node\",\n \"CSS\",\n \"Canvas\",\n \"E"
},
{
"path": "data/links.json",
"chars": 3056627,
"preview": "[{\"title\":\"Node-Weekly\",\"items\":[{\"title\":\"Node.js 25.9 brings --max-heap-size and better, iterable streams\",\"link\":\"htt"
},
{
"path": "data/processed.json",
"chars": 87112,
"preview": "{\"http://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651278629&idx=1&sn=b51bc320fefe9c3d3d6f715b1d6626bf&chksm=bcbd70"
},
{
"path": "data/rss.json",
"chars": 2084,
"preview": "[\n {\n \"title\": \"Node-Weekly\",\n \"rss\": \"https://cprss.s3.amazonaws.com/nodeweekly.com.xml\"\n },\n {\n \"title\": \""
},
{
"path": "data/tags.json",
"chars": 5767,
"preview": "[\n {\n \"tag\": \"AI\",\n \"filename\": \"ai\",\n \"keywords\": \"\\\\b(AI|LLM|GPT|GLM|大模型|人工智能|机器学习|深度学习|神经网络|ChatGPT|Cursor|"
},
{
"path": "details/JavaScript-Weekly.md",
"chars": 21156,
"preview": ":alarm_clock: 更新时间: 2026-04-01 00:46:31。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## JavaScript-Weekly\n\n\n\n\n- [2026-03-31-A-ne"
},
{
"path": "details/Node-Weekly.md",
"chars": 22656,
"preview": ":alarm_clock: 更新时间: 2026-04-02 20:43:59。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## Node-Weekly\n\n\n\n\n- [2026-04-02-Node.js-25"
},
{
"path": "details/Nodejs技术栈.md",
"chars": 6012,
"preview": ":alarm_clock: 更新时间: 2026-04-03 22:36:14。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## Nodejs技术栈\n\n\n\n\n- [2026-04-03-刚刚,Cursor-3."
},
{
"path": "details/iCSS前端趣闻.md",
"chars": 15681,
"preview": ":alarm_clock: 更新时间: 2026-03-28 21:58:35。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## iCSS前端趣闻\n\n\n\n\n- [2025-11-05-当-AI-开始「偷懒」:一"
},
{
"path": "details/tags/ai.md",
"chars": 115455,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## AI\n\n\n> 关键字:`AI`、`LLM`、`GPT`、`GLM`、`大模型`、"
},
{
"path": "details/tags/audio-video.md",
"chars": 52636,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 音视频\n\n\n> 关键字:`WebVR`、`WebXR`、`WebRTC`、`Vi"
},
{
"path": "details/tags/browser.md",
"chars": 124925,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 浏览器\n\n\n> 关键字:`Chrome`、`Chromium`、`IE`、`Fi"
},
{
"path": "details/tags/canvas-image.md",
"chars": 93633,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 图形图像\n\n\n> 关键字:`Canvas`、`SVG`、`WebGL`、`Web"
},
{
"path": "details/tags/css.md",
"chars": 171931,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## CSS\n\n\n> 关键字:`CSS`、`Sass`、`SCSS`、`Less`、`"
},
{
"path": "details/tags/dev-desktop.md",
"chars": 28099,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 桌面应用\n\n\n> 关键字:`Electron`、`NW.js`、`Tauri`、"
},
{
"path": "details/tags/dev-game.md",
"chars": 24961,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 游戏开发\n\n\n> 关键字:`游戏`、`Three.js`、`Create.js`"
},
{
"path": "details/tags/dev-mobile.md",
"chars": 67706,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 手机应用\n\n\n> 关键字:`Flutter`、`Dart`、`ReactNati"
},
{
"path": "details/tags/front-end-advanced.md",
"chars": 738682,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 前端进阶\n\n\n> 关键字:`AST`、`抽象语法树`、`GPU`、`WebAss"
},
{
"path": "details/tags/git-svn.md",
"chars": 32978,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 版本控制\n\n\n> 关键字:`Git`、`Bitbucket`、`SVN`、`版本"
},
{
"path": "details/tags/html.md",
"chars": 37054,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## HTML\n\n\n> 关键字:`HTML`、`HTML5`、`标签`、`meta`、"
},
{
"path": "details/tags/javascript.md",
"chars": 505003,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## JavaScript\n\n\n> 关键字:`JavaScript`、`ECMAScr"
},
{
"path": "details/tags/job-interview.md",
"chars": 186349,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 招聘面试\n\n\n> 关键字:`招聘`、`招人`、`岗位`、`面试`、`笔试`、`内"
},
{
"path": "details/tags/miniprogram.md",
"chars": 40425,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 小程序\n\n\n> 关键字:`小程序`、`Taro`、`Uni-app`、`MPVu"
},
{
"path": "details/tags/nodejs.md",
"chars": 127310,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## NodeJS\n\n\n> 关键字:`Node`、`Node.js`、`Deno`、`"
},
{
"path": "details/tags/optimization.md",
"chars": 185403,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 性能优化\n\n\n> 关键字:`性能`、`优化`、`加载`、`速度`、`体验`、`S"
},
{
"path": "details/tags/other.md",
"chars": 2576692,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 其它\n\n\n\n\n- [2026-04-03-刚刚,Cursor-3.0-发布!编辑"
},
{
"path": "details/tags/pack-build.md",
"chars": 138489,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 构建打包\n\n\n> 关键字:`Webpack`、`Lerna`、`Rollup`、"
},
{
"path": "details/tags/react.md",
"chars": 174815,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## React\n\n\n> 关键字:`React`、`Rax`、`Nerv`、`Redu"
},
{
"path": "details/tags/server.md",
"chars": 330751,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 服务端\n\n\n> 关键字:`Nginx`、`Apache`、`Caddy`、`Tr"
},
{
"path": "details/tags/typescript.md",
"chars": 90732,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## TypeScript\n\n\n> 关键字:`TypeScript`、`TS`、`in"
},
{
"path": "details/tags/vue.md",
"chars": 163936,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## Vue\n\n\n> 关键字:`Vue`、`Vuex`、`Pinia`、`Nuxt`、"
},
{
"path": "details/凹凸实验室.md",
"chars": 28737,
"preview": ":alarm_clock: 更新时间: 2026-03-28 21:58:35。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 凹凸实验室\n\n\n\n\n- [2023-09-14-给-Web-前端工程师看的用-R"
},
{
"path": "details/前端之巅.md",
"chars": 429330,
"preview": ":alarm_clock: 更新时间: 2026-03-28 21:58:35。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 前端之巅\n\n\n\n\n- [2026-03-15-18-岁创业者用-OpenClaw"
},
{
"path": "details/前端从进阶到入院.md",
"chars": 106139,
"preview": ":alarm_clock: 更新时间: 2026-04-01 20:51:41。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 前端从进阶到入院\n\n\n\n\n- [2026-04-01-紧急!Axios-被投毒,"
},
{
"path": "details/前端侦探.md",
"chars": 15020,
"preview": ":alarm_clock: 更新时间: 2026-03-28 21:58:35。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 前端侦探\n\n\n\n\n- [2025-09-22-Safari又出bug了?临时修复"
},
{
"path": "details/前端大全.md",
"chars": 634537,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 前端大全\n\n\n\n\n- [2026-04-01-紧急预警:Axios-供应链被投毒"
},
{
"path": "details/前端技术优选.md",
"chars": 69268,
"preview": ":alarm_clock: 更新时间: 2026-03-28 21:58:35。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 前端技术优选\n\n\n\n\n- [2022-11-15-Git-是如何工作的](htt"
},
{
"path": "details/前端早读课.md",
"chars": 723875,
"preview": ":alarm_clock: 更新时间: 2026-04-03 14:55:43。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 前端早读课\n\n\n\n\n- [2026-04-03-【早说】Ghostty创始人的A"
},
{
"path": "details/前端精读评论.md",
"chars": 6199,
"preview": ":alarm_clock: 更新时间: 2026-03-28 21:58:35。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 前端精读评论\n\n\n\n\n- [2024-09-09-手动算根号](http://m"
},
{
"path": "details/字节前端-ByteFE.md",
"chars": 15154,
"preview": ":alarm_clock: 更新时间: 2026-03-28 21:58:35。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 字节前端-ByteFE\n\n\n\n\n- [2025-12-16-豆包-应用生成「一点"
},
{
"path": "details/张鑫旭-鑫空间-鑫生活.md",
"chars": 47221,
"preview": ":alarm_clock: 更新时间: 2026-03-30 15:14:03。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 张鑫旭-鑫空间-鑫生活\n\n\n\n\n- [2026-03-30-CSS-corner"
},
{
"path": "details/淘系前端团队.md",
"chars": 63563,
"preview": ":alarm_clock: 更新时间: 2026-04-03 22:36:14。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 淘系前端团队\n\n\n\n\n- [2026-04-03-淘宝跨端体验优化-AI-演进之"
},
{
"path": "details/程序员成长指北.md",
"chars": 311967,
"preview": ":alarm_clock: 更新时间: 2026-04-04 00:27:56。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 程序员成长指北\n\n\n\n\n- [2026-04-02-Skills-乱麻了!这款开"
},
{
"path": "details/阮一峰的网络日志.md",
"chars": 46313,
"preview": ":alarm_clock: 更新时间: 2026-04-03 12:18:36。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 阮一峰的网络日志\n\n\n\n\n- [2026-04-03-科技爱好者周刊(第-391"
},
{
"path": "details/风痕·術&思.md",
"chars": 15443,
"preview": ":alarm_clock: 更新时间: 2026-03-28 21:58:35。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## 风痕·術&思\n\n\n\n\n- [2026-02-03-2025-年终总结](http"
},
{
"path": "server/app.js",
"chars": 373,
"preview": "const later = require('later')\n\nconst handleUpdate = require('./update')\n\n// node app.js 设置自动更新\nlater.date.localTime()\nl"
},
{
"path": "server/dedupe-links.js",
"chars": 2248,
"preview": "/**\n * 对 data/links.json 去重,满足其一即去重(保留旧的、去除新的):\n * 1. 标题相同且时间相邻(七天内)\n * 2. 标题相同且标题字符长度 > 30\n * 用法:node server/dedupe-lin"
},
{
"path": "server/feed.js",
"chars": 1349,
"preview": "const { Feed } = require('feed')\nconst path = require('path')\nconst moment = require('moment')\nconst fs = require('fs-ex"
},
{
"path": "server/fetch.js",
"chars": 1597,
"preview": "const Parser = require('rss-parser')\nconst Async = require('async')\n\nconst utils = require('./utils')\n\nasync function fe"
},
{
"path": "server/once.js",
"chars": 82,
"preview": "const handleUpdate = require('./update')\n\n// node once.js 执行一次,手动更新\nhandleUpdate()"
},
{
"path": "server/package.json",
"chars": 924,
"preview": "{\n \"name\": \"front-end-rss\",\n \"version\": \"1.0.0\",\n \"main\": \"index.js\",\n \"description\": \"每天定时抓取最新前端技术文章,并推送到 GitHub 方便"
},
{
"path": "server/update.js",
"chars": 4123,
"preview": "const fs = require('fs-extra')\nconst path = require('path')\nconst Async = require('async')\nconst moment = require('momen"
},
{
"path": "server/utils.js",
"chars": 4043,
"preview": "const fs = require('fs-extra')\nconst path = require('path')\nconst url = require('url')\nconst moment = require('moment-ti"
},
{
"path": "server/writemd.js",
"chars": 3766,
"preview": "const fs = require('fs-extra')\nconst path = require('path')\nconst _ = require('underscore')\n\nconst utils = require('./ut"
},
{
"path": "site/.babelrc",
"chars": 348,
"preview": "{\n \"presets\": [\n [\"env\", {\n \"modules\": false,\n \"targets\": {\n \"browsers\": [\"> 1%\", \"last 2 versions\""
},
{
"path": "site/.editorconfig",
"chars": 147,
"preview": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_"
},
{
"path": "site/.eslintignore",
"chars": 30,
"preview": "/build/\n/config/\n/dist/\n/*.js\n"
},
{
"path": "site/.eslintrc.js",
"chars": 819,
"preview": "// https://eslint.org/docs/user-guide/configuring\n\nmodule.exports = {\n root: true,\n parserOptions: {\n parser: 'babe"
},
{
"path": "site/.gitignore",
"chars": 154,
"preview": ".DS_Store\nnode_modules/\n/dist/\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Editor directories and files\n.idea\n.vsc"
},
{
"path": "site/.npmrc",
"chars": 71,
"preview": "# for pnpm\npublic-hoist-pattern[]=*babel*\npublic-hoist-pattern[]=vue-*\n"
},
{
"path": "site/.postcssrc.js",
"chars": 373,
"preview": "// https://github.com/michael-ciniawsky/postcss-load-config\n\nmodule.exports = {\n \"plugins\": {\n \"postcss-import\": {},"
},
{
"path": "site/README.md",
"chars": 461,
"preview": "# site\n\n> A Vue.js project\n\n## Build Setup\n\n``` bash\n# install dependencies\nnpm install\n\n# serve with hot reload at loca"
},
{
"path": "site/build/build.js",
"chars": 1420,
"preview": "'use strict'\nrequire('./check-versions')()\n\nprocess.env.NODE_ENV = 'production'\n\nconst ora = require('ora')\nconst rm = r"
},
{
"path": "site/build/check-versions.js",
"chars": 1290,
"preview": "'use strict'\nconst chalk = require('chalk')\nconst semver = require('semver')\nconst packageConfig = require('../package.j"
},
{
"path": "site/build/createFiles.js",
"chars": 6129,
"preview": "const fs = require('fs-extra')\nconst path = require('path')\nconst zlib = require('zlib')\nconst dayjs = require('dayjs')\n"
},
{
"path": "site/build/data.js",
"chars": 285,
"preview": "const RSS_DATA = require('../../data/rss.json')\nconst TAGS_DATA = require('../../data/tags.json')\nconst LINKS_DATA = req"
},
{
"path": "site/build/template-parameters.js",
"chars": 416,
"preview": "const data = require('./data')\nconst createFiles = require('./createFiles')\n\n// 生成所有静态文件并获取配置信息\nconst config = createFil"
},
{
"path": "site/build/upload.js",
"chars": 5210,
"preview": "/**\n * 将 site/dist 目录上传到七牛云存储。\n * 使用:在 site 目录执行 node build/upload.js 或 pnpm run upload(需在 package.json 添加脚本)\n *\n * 环境变量"
},
{
"path": "site/build/utils.js",
"chars": 2593,
"preview": "'use strict'\nconst path = require('path')\nconst config = require('../config')\nconst ExtractTextPlugin = require('extract"
},
{
"path": "site/build/vue-loader.conf.js",
"chars": 553,
"preview": "'use strict'\nconst utils = require('./utils')\nconst config = require('../config')\nconst isProduction = process.env.NODE_"
},
{
"path": "site/build/webpack.base.conf.js",
"chars": 2385,
"preview": "'use strict'\nconst path = require('path')\nconst utils = require('./utils')\nconst config = require('../config')\nconst vue"
},
{
"path": "site/build/webpack.dev.conf.js",
"chars": 4260,
"preview": "'use strict'\nconst utils = require('./utils')\nconst webpack = require('webpack')\nconst config = require('../config')\ncon"
},
{
"path": "site/build/webpack.prod.conf.js",
"chars": 5528,
"preview": "'use strict'\nconst path = require('path')\nconst utils = require('./utils')\nconst webpack = require('webpack')\nconst conf"
},
{
"path": "site/config/dev.env.js",
"chars": 241,
"preview": "'use strict'\nconst merge = require('webpack-merge')\nconst prodEnv = require('./prod.env')\n\nmodule.exports = merge(prodEn"
},
{
"path": "site/config/index.js",
"chars": 2299,
"preview": "'use strict'\n// Template version: 1.3.1\n// see http://vuejs-templates.github.io/webpack for documentation.\n\nconst path ="
},
{
"path": "site/config/prod.env.js",
"chars": 117,
"preview": "'use strict'\nmodule.exports = {\n NODE_ENV: '\"production\"',\n ARTICLE_DATA_HOST: '\"https://fed-data.chanceyu.com\"'\n}\n"
},
{
"path": "site/index.html",
"chars": 1485,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrom"
},
{
"path": "site/package.json",
"chars": 2817,
"preview": "{\n \"name\": \"site\",\n \"version\": \"1.0.0\",\n \"packageManager\": \"pnpm@10.26.2\",\n \"description\": \"A Vue.js project\",\n \"au"
},
{
"path": "site/src/App.vue",
"chars": 570,
"preview": "<template>\n <div id=\"app\">\n <router-view/>\n </div>\n</template>\n\n<script>\nexport default {\n name: 'App'\n}\n</script>"
},
{
"path": "site/src/components/Index.vue",
"chars": 41586,
"preview": "<template>\n <div class=\"page-wrap\">\n <!-- PC:左右两列居中贴在一起,左侧 sticky -->\n <div class=\"layout-center\">\n <!-- 左侧筛选:"
},
{
"path": "site/src/components/MarkdownViewer.vue",
"chars": 18972,
"preview": "<template>\n <van-popup\n :value=\"visible\"\n :position=\"isMobile ? 'bottom' : 'right'\"\n :duration=\"0.15\"\n :sty"
},
{
"path": "site/src/main.js",
"chars": 1002,
"preview": "// The Vue build version to load with the `import` command\n// (runtime-only or standalone) has been set in webpack.base."
},
{
"path": "site/src/router/index.js",
"chars": 250,
"preview": "import Vue from 'vue'\nimport Router from 'vue-router'\nimport Index from '@/components/Index'\n\nVue.use(Router)\n\nexport de"
},
{
"path": "site/static/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "templates/DETAILS.md",
"chars": 519,
"preview": ":alarm_clock: 更新时间: <%= obj.currentDate %>。[文章来源](/README.md)、[文章分类](/TAGS.md)\n\n## <%= obj.title %>\n\n<% if(obj.keywords)"
},
{
"path": "templates/README.md",
"chars": 1498,
"preview": "<div align=\"center\"><img width=\"100\" src=\"/assets/rss.gif\" /><h1>Front-End RSS</h1>\n每天定时更新前端技术文章,并推送到 GitHub 方便查看\n</div>"
},
{
"path": "templates/TAGS.md",
"chars": 872,
"preview": "> 提示:只是根据文章标题简单匹配分类\n\n:alarm_clock: 更新时间: <%= obj.currentDate %>。[文章来源](/README.md)\n\n## 文章分类\n<% _.each(obj.tags, function"
}
]
About this extraction
This page contains the full source code of the ChanceYu/front-end-rss GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 112 files (11.5 MB), approximately 3.0M tokens, and a symbol index with 149 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.