<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>RYANUO</title>
        <link>https://ryanuo.cc</link>
        <description>RYANUO' Blog</description>
        <lastBuildDate>Thu, 18 Dec 2025 06:50:24 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>RYANUO</title>
            <url>https://ryanuo.cc/avatar.png</url>
            <link>https://ryanuo.cc</link>
        </image>
        <copyright>CC BY-NC-SA 4.0 2021 © RYANUO</copyright>
        <atom:link href="https://ryanuo.cc/sitemap.xml" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[工具 | RYANUO]]></title>
            <link>https://ryanuo.cc/zh/navs/tools</link>
            <guid>https://ryanuo.cc/zh/navs/tools</guid>
            <pubDate>Thu, 18 Dec 2025 06:50:24 GMT</pubDate>
            <description><![CDATA[前端在线工具正在整理中，并持续更新。]]></description>
            <content:encoded><![CDATA[<h4>常用小工具</h4><div class="project-item"><div class="project-item-title">带壳截屏</div><div class="project-item-desc">一种在真实设备模型中展示应用截图的功能，使得开发者或营销人员能够获得更专业、更具吸引力的应用展示效果。</div><div class="project-item-link"><a href="https://deviceshots.com/app" target="_blank">https://deviceshots.com/app</a></div></div><div class="project-item"><div class="project-item-title">在线PS</div><div class="project-item-desc">一个功能强大的在线替代品，提供与Adobe Photoshop相似的图片处理体验。用户无需下载安装即可在浏览器中打开并使用它来编辑照片、应用特效、滤镜、添加文本、裁剪或调整图片大小等。支持多种格式如PSD、XD、Sketch、XCF和RAW等，并具备实时链接状态检查，确保用户可以无缝访问此在线PS服务。</div><div class="project-item-link"><a href="https://www.photopea.com/" target="_blank">https://www.photopea.com/</a></div></div><div class="project-item"><div class="project-item-title">images-tools</div><div class="project-item-desc">在线图片处理,批量转换图片,批量压缩图片,webp转jpg,视频转gif,gif转webp</div><div class="project-item-link"><a href="https://github.com/renzhezhilu/webp2jpg-online" target="_blank">https://github.com/renzhezhilu/webp2jpg-online</a></div></div><div class="project-item"><div class="project-item-title">封面图片生成工具</div><div class="project-item-desc">This is a simple web app that allows you to upload an image and get a cover of your choice.</div><div class="project-item-link"><a href="https://github.com/ryanuo/own-cover" target="_blank">https://github.com/ryanuo/own-cover</a></div></div><h4>快速查阅</h4><div class="project-item"><div class="project-item-title">Quick Reference</div><div class="project-item-desc">为开发人员分享快速参考备忘单</div><div class="project-item-link"><a href="https://wangchujiang.com/reference/index.html" target="_blank">https://wangchujiang.com/reference/index.html</a></div></div><div class="project-item"><div class="project-item-title">reference</div><div class="project-item-desc">为开发人员分享快速参考备忘单</div><div class="project-item-link"><a href="https://github.com/Fechin/reference" target="_blank">https://github.com/Fechin/reference</a></div></div><div class="project-item"><div class="project-item-title">Excel 参考</div><div class="project-item-desc">Excel 参考是一款工具，旨在帮助您快速找到所需使用的Excel函数。</div><div class="project-item-link"><a href="https://www.lanrenexcel.com/" target="_blank">https://www.lanrenexcel.com/</a></div></div><h4>谷歌插件</h4><div class="project-item"><div class="project-item-title">react1s</div><div class="project-item-desc">点击页面元素跳转到编辑器,支持 React 项目本地开发时 Option(Alt)+Click 页面上对应元素即可跳转到编辑器对应组件行列。</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/react1s/gpcoahaomdfmekggblkckofkgjggnjlp" target="_blank">https://chromewebstore.google.com/detail/react1s/gpcoahaomdfmekggblkckofkgjggnjlp</a></div></div><div class="project-item"><div class="project-item-title">react-developer-tools</div><div class="project-item-desc">React 开发者工具，用于调试 React 应用程序。</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi" target="_blank">https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi</a></div></div><div class="project-item"><div class="project-item-title">扩展管理器</div><div class="project-item-desc">Chrome 扩展程序管理器，用于管理 Chrome 扩展程序。</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/%E6%89%A9%E5%B1%95%E7%AE%A1%E7%90%86%E5%99%A8%EF%BC%88extension-manager%EF%BC%89/gjldcdngmdknpinoemndlidpcabkggco" target="_blank">https://chromewebstore.google.com/detail/%E6%89%A9%E5%B1%95%E7%AE%A1%E7%90%86%E5%99%A8%EF%BC%88extension-manager%EF%BC%89/gjldcdngmdknpinoemndlidpcabkggco</a></div></div><div class="project-item"><div class="project-item-title">FeHelper</div><div class="project-item-desc">JSON自动格式化、手动格式化，支持排序、解码、下载等，更多功能可在配置页按需安装！</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/fehelper%E5%89%8D%E7%AB%AF%E5%8A%A9%E6%89%8B/pkgccpejnmalmdinmhkkfafefagiiiad" target="_blank">https://chromewebstore.google.com/detail/fehelper%E5%89%8D%E7%AB%AF%E5%8A%A9%E6%89%8B/pkgccpejnmalmdinmhkkfafefagiiiad</a></div></div><div class="project-item"><div class="project-item-title">uBlock Origin</div><div class="project-item-desc">广告拦截器，支持自定义规则，可屏蔽网站的广告和其他干扰性内容。</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm" target="_blank">https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm</a></div></div><div class="project-item"><div class="project-item-title">沉浸式翻译</div><div class="project-item-desc">沉浸式翻译，支持PDF文件翻译，可将PDF文件中的文字转换成语音播报，并可选择是否朗读。</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/%E6%B2%89%E6%B5%B8%E5%BC%8F%E7%BF%BB%E8%AF%91-%E5%8F%8C%E8%AF%AD%E5%AF%B9%E7%85%A7%E7%BD%91%E9%A1%B5%E7%BF%BB%E8%AF%91-pdf%E6%96%87%E6%A1%A3%E7%BF%BB%E8%AF%91/bpoadfkcbjbfhfodiogcnhhhpibjhbnh" target="_blank">https://chromewebstore.google.com/detail/%E6%B2%89%E6%B5%B8%E5%BC%8F%E7%BF%BB%E8%AF%91-%E5%8F%8C%E8%AF%AD%E5%AF%B9%E7%85%A7%E7%BD%91%E9%A1%B5%E7%BF%BB%E8%AF%91-pdf%E6%96%87%E6%A1%A3%E7%BF%BB%E8%AF%91/bpoadfkcbjbfhfodiogcnhhhpibjhbnh</a></div></div><h4>其它</h4><div class="project-item"><div class="project-item-title">虫部落</div><div class="project-item-desc">快搜！虫部落全网独家首发出品！为你聚合Google,百度,必应等国内外综合搜索和学术,资源,专业领域知识等垂直搜索。精准搜索,便捷交互！是你的网络搜索第一站！</div><div class="project-item-link"><a href="https://search.chongbuluo.com/" target="_blank">https://search.chongbuluo.com/</a></div></div><div class="project-item"><div class="project-item-title">导航汇总</div><div class="project-item-desc">前端导航外链汇总</div><div class="project-item-link"><a href="https://github.com/ryanuo/navs" target="_blank">https://github.com/ryanuo/navs</a></div></div><div class="project-item"><div class="project-item-title">Independent Developer</div><div class="project-item-desc">优秀独立开发者</div><div class="project-item-link"><a href="https://github.com/1c7/chinese-independent-developer" target="_blank">https://github.com/1c7/chinese-independent-developer</a></div></div>]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[导航 | RYANUO]]></title>
            <link>https://ryanuo.cc/zh/navs</link>
            <guid>https://ryanuo.cc/zh/navs</guid>
            <pubDate>Thu, 18 Dec 2025 06:50:24 GMT</pubDate>
            <description><![CDATA[前端导航包括常用的前端第三方网站、前端知识以及与前端相关的内容。]]></description>
            <content:encoded><![CDATA[<h4>常用工具</h4><div class="project-item"><div class="project-item-title">Web Tools</div><div class="project-item-desc">许多有用的Web在线工具，适用于Web开发人员和程序员</div><div class="project-item-link"><a href="https://wangchujiang.com/tools/#/" target="_blank">https://wangchujiang.com/tools/#/</a></div></div><div class="project-item"><div class="project-item-title">Can I Use</div><div class="project-item-desc">提供最新的浏览器支持表格，用于支持桌面和移动网络浏览器上的前端网络技术。</div><div class="project-item-link"><a href="https://caniuse.com/" target="_blank">https://caniuse.com/</a></div></div><div class="project-item"><div class="project-item-title">Carbon</div><div class="project-item-desc">代码生成图像。</div><div class="project-item-link"><a href="https://carbon.now.sh/" target="_blank">https://carbon.now.sh/</a></div></div><div class="project-item"><div class="project-item-title">Tool.lu</div><div class="project-item-desc">在线工具库。</div><div class="project-item-link"><a href="https://tool.lu/" target="_blank">https://tool.lu/</a></div></div><div class="project-item"><div class="project-item-title">Npm Devtool</div><div class="project-item-desc">npm包引用到cdn在线。</div><div class="project-item-link"><a href="https://npm.devtool.tech/" target="_blank">https://npm.devtool.tech/</a></div></div><div class="project-item"><div class="project-item-title">JSON Tool</div><div class="project-item-desc">提供JSON解析、验证、格式化、压缩、编辑器以及JSON与XML之间转换的服务。</div><div class="project-item-link"><a href="https://www.json.cn/" target="_blank">https://www.json.cn/</a></div></div><h4>英文学习</h4><div class="project-item"><div class="project-item-title">1000-hours</div><div class="project-item-desc">1000个小时的英语学习计划。</div><div class="project-item-link"><a href="https://github.com/xiaolai/everyone-can-use-english" target="_blank">https://github.com/xiaolai/everyone-can-use-english</a></div></div><div class="project-item"><div class="project-item-title">cpwp</div><div class="project-item-desc">中国程序员发音错误</div><div class="project-item-link"><a href="https://github.com/antfu/cpwp" target="_blank">https://github.com/antfu/cpwp</a></div></div><h4>AI导航</h4><div class="project-item"><div class="project-item-title">Chatgpt of mirror</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://github.com/xx025/carrot" target="_blank">https://github.com/xx025/carrot</a></div></div><div class="project-item"><div class="project-item-title">Chatgpt</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://chat.openai.com/chat" target="_blank">https://chat.openai.com/chat</a></div></div><div class="project-item"><div class="project-item-title">kimi</div><div class="project-item-desc">AI 聊天机器人</div><div class="project-item-link"><a href="https://kimi.moonshot.cn/" target="_blank">https://kimi.moonshot.cn/</a></div></div><div class="project-item"><div class="project-item-title">通义千问</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://tongyi.aliyun.com/qianwen/" target="_blank">https://tongyi.aliyun.com/qianwen/</a></div></div><div class="project-item"><div class="project-item-title">AI工具集</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://ai-bot.cn/" target="_blank">https://ai-bot.cn/</a></div></div><h4>Rust</h4><div class="project-item"><div class="project-item-title">Rust包</div><div class="project-item-desc">Rust community’s crate registry.</div><div class="project-item-link"><a href="https://crates.io/" target="_blank">https://crates.io/</a></div></div><h4>React生态</h4><div class="project-item"><div class="project-item-title">React</div><div class="project-item-desc">React 中文翻译文档。</div><div class="project-item-link"><a href="https://zh-hans.react.dev/" target="_blank">https://zh-hans.react.dev/</a></div></div><div class="project-item"><div class="project-item-title">React Router</div><div class="project-item-desc">React 应用的导航路由解决方案。</div><div class="project-item-link"><a href="https://reactrouter.com" target="_blank">https://reactrouter.com</a></div></div><div class="project-item"><div class="project-item-title">Next.js</div><div class="project-item-desc">Vercel 出品的全栈 React 框架，用于 Web 开发。</div><div class="project-item-link"><a href="https://nextjs.org" target="_blank">https://nextjs.org</a></div></div><div class="project-item"><div class="project-item-title">UmiJS</div><div class="project-item-desc">可扩展、基于路由的前端框架，拥有丰富的插件。</div><div class="project-item-link"><a href="https://umijs.org" target="_blank">https://umijs.org</a></div></div><div class="project-item"><div class="project-item-title">Ant Design</div><div class="project-item-desc">全球第二受欢迎的 React UI 框架。</div><div class="project-item-link"><a href="https://ant-design.antgroup.com" target="_blank">https://ant-design.antgroup.com</a></div></div><div class="project-item"><div class="project-item-title">Ant Design Mobile</div><div class="project-item-desc">构建移动网络应用的基础 UI 组件。</div><div class="project-item-link"><a href="http://ant-design-mobile.antgroup.com" target="_blank">http://ant-design-mobile.antgroup.com</a></div></div><div class="project-item"><div class="project-item-title">Mantine</div><div class="project-item-desc">现代 React 组件库和钩子集合。</div><div class="project-item-link"><a href="https://mantine.dev" target="_blank">https://mantine.dev</a></div></div><div class="project-item"><div class="project-item-title">zustand</div><div class="project-item-desc">最小且灵活的状态管理库。</div><div class="project-item-link"><a href="https://docs.pmnd.rs/zustand/getting-started/introduction" target="_blank">https://docs.pmnd.rs/zustand/getting-started/introduction</a></div></div><div class="project-item"><div class="project-item-title">Valtio</div><div class="project-item-desc">使代理状态对 React 和 Vanilla 简单化。</div><div class="project-item-link"><a href="https://valtio.pmnd.rs" target="_blank">https://valtio.pmnd.rs</a></div></div><div class="project-item"><div class="project-item-title">Jotai</div><div class="project-item-desc">React 的原始且灵活的状态管理。</div><div class="project-item-link"><a href="https://jotai.jscn.org" target="_blank">https://jotai.jscn.org</a></div></div><div class="project-item"><div class="project-item-title">Redux</div><div class="project-item-desc">JavaScript 应用的可预测状态容器。</div><div class="project-item-link"><a href="https://cn.redux.js.org" target="_blank">https://cn.redux.js.org</a></div></div><div class="project-item"><div class="project-item-title">MobX</div><div class="project-item-desc">响应式状态管理库。</div><div class="project-item-link"><a href="https://zh.mobx.js.org" target="_blank">https://zh.mobx.js.org</a></div></div><div class="project-item"><div class="project-item-title">ahooks</div><div class="project-item-desc">高效的 React Hooks 集合。</div><div class="project-item-link"><a href="https://ahooks.js.org" target="_blank">https://ahooks.js.org</a></div></div><h4>Vue生态</h4><div class="project-item"><div class="project-item-title">Vue3</div><div class="project-item-desc">Vue.js 是一个渐进式 JavaScript 框架。</div><div class="project-item-link"><a href="https://cn.vuejs.org" target="_blank">https://cn.vuejs.org</a></div></div><div class="project-item"><div class="project-item-title">Vue2</div><div class="project-item-desc">Vue.js 是一个渐进式 JavaScript 框架。</div><div class="project-item-link"><a href="https://v2.cn.vuejs.org/" target="_blank">https://v2.cn.vuejs.org/</a></div></div><div class="project-item"><div class="project-item-title">Vue Router</div><div class="project-item-desc">Vue.js 的官方路由管理器</div><div class="project-item-link"><a href="https://router.vuejs.org" target="_blank">https://router.vuejs.org</a></div></div><div class="project-item"><div class="project-item-title">Pinia</div><div class="project-item-desc">直观、类型安全、轻量且灵活的 Vue 存储管理库</div><div class="project-item-link"><a href="https://pinia.vuejs.org" target="_blank">https://pinia.vuejs.org</a></div></div><div class="project-item"><div class="project-item-title">Nuxt.js</div><div class="project-item-desc">Nuxt 是一个开源框架，让web开发变得直观且强大</div><div class="project-item-link"><a href="https://nuxt.com" target="_blank">https://nuxt.com</a></div></div><div class="project-item"><div class="project-item-title">VueUse</div><div class="project-item-desc">Vue 组合式API实用工具集合</div><div class="project-item-link"><a href="https://vueuse.org" target="_blank">https://vueuse.org</a></div></div><div class="project-item"><div class="project-item-title">Element Plus</div><div class="project-item-desc">基于 Vue 3 构建的设计师和开发者适用的组件库</div><div class="project-item-link"><a href="https://element-plus.org" target="_blank">https://element-plus.org</a></div></div><div class="project-item"><div class="project-item-title">Ant Design Vue</div><div class="project-item-desc">基于 Ant Design 和 Vue 的企业级 UI 组件库</div><div class="project-item-link"><a href="https://antdv.com" target="_blank">https://antdv.com</a></div></div><div class="project-item"><div class="project-item-title">Vant</div><div class="project-item-desc">基于 Vue 构建的移动端 UI 组件库</div><div class="project-item-link"><a href="https://vant-ui.github.io" target="_blank">https://vant-ui.github.io</a></div></div><h4>CSS 相关</h4><div class="project-item"><div class="project-item-title">UnoCSS</div><div class="project-item-desc">即时按需的原子化 CSS 引擎</div><div class="project-item-link"><a href="https://unocss.dev" target="_blank">https://unocss.dev</a></div></div><div class="project-item"><div class="project-item-title">Tailwind CSS</div><div class="project-item-desc">Tailwind CSS 是一款以实用工具为主的 CSS 框架，无需离开 HTML 即可快速构建现代网站</div><div class="project-item-link"><a href="https://www.tailwindcss.cn" target="_blank">https://www.tailwindcss.cn</a></div></div><div class="project-item"><div class="project-item-title">iCSS</div><div class="project-item-desc">各种 CSS 花式技巧</div><div class="project-item-link"><a href="https://github.com/chokcoco/iCSS" target="_blank">https://github.com/chokcoco/iCSS</a></div></div><div class="project-item"><div class="project-item-title">You-need-to-know-css</div><div class="project-item-desc">你需要了解的 CSS 知识汇总</div><div class="project-item-link"><a href="https://lhammer.cn" target="_blank">https://lhammer.cn</a></div></div><div class="project-item"><div class="project-item-title">CSS tricks</div><div class="project-item-desc">常见的css样式技巧汇总</div><div class="project-item-link"><a href="https://qishaoxuan.github.io/css_tricks/" target="_blank">https://qishaoxuan.github.io/css_tricks/</a></div></div><div class="project-item"><div class="project-item-title">纸屑动画</div><div class="project-item-desc">基于canvas的纸屑动画</div><div class="project-item-link"><a href="https://github.com/catdad/canvas-confetti" target="_blank">https://github.com/catdad/canvas-confetti</a></div></div><div class="project-item"><div class="project-item-title">Hover Animation</div><div class="project-item-desc">CSS3 驱动的悬停效果集合，可应用于链接、按钮、徽标、SVG、特色图像等。轻松应用于您自己的元素，修改或只是用于灵感。在 CSS、Sass 和 LESS 中可用。</div><div class="project-item-link"><a href="https://github.com/IanLunn/Hover" target="_blank">https://github.com/IanLunn/Hover</a></div></div><div class="project-item"><div class="project-item-title">手绘动画</div><div class="project-item-desc">在网页上创建手绘批注并为其添加动画效果</div><div class="project-item-link"><a href="https://github.com/rough-stuff/rough-notation" target="_blank">https://github.com/rough-stuff/rough-notation</a></div></div><div class="project-item"><div class="project-item-title">颜色选择</div><div class="project-item-desc">来自 Sketch、Photoshop、Chrome、Github、Twitter 等的颜色选择器</div><div class="project-item-link"><a href="https://github.com/casesandberg/react-color" target="_blank">https://github.com/casesandberg/react-color</a></div></div><div class="project-item"><div class="project-item-title">React Components For Creative Developers</div><div class="project-item-desc">Highly customizable animated components that make your React projects truly stand out</div><div class="project-item-link"><a href="https://www.reactbits.dev/text-animations/split-text" target="_blank">https://www.reactbits.dev/text-animations/split-text</a></div></div><h4>图标库</h4><div class="project-item"><div class="project-item-title">Icônes</div><div class="project-item-desc">提供图标设计服务</div><div class="project-item-link"><a href="https://icones.js.org/" target="_blank">https://icones.js.org/</a></div></div><div class="project-item"><div class="project-item-title">Iconify Design</div><div class="project-item-desc">整合了所有流行图标的集合，只需一个框架</div><div class="project-item-link"><a href="https://iconify.design/" target="_blank">https://iconify.design/</a></div></div><div class="project-item"><div class="project-item-title">WebFX</div><div class="project-item-desc">WebFX是顶级数字营销机构，已为客户创造了超过60亿美元收入，可获取免费提案并探索我们多样化的数字营销服务</div><div class="project-item-link"><a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a></div></div><div class="project-item"><div class="project-item-title">Yesicon</div><div class="project-item-desc">拥有168个图标集和超过21万个图标，支持多种语言跨图标集搜索</div><div class="project-item-link"><a href="https://yesicon.app/" target="_blank">https://yesicon.app/</a></div></div><h4>网站部署</h4><div class="project-item"><div class="project-item-title">NuxtHub</div><div class="project-item-desc">NuxtHub是一个用于部署Nuxt应用程序的云平台。它允许您使用Nuxt应用程序和第三方服务，如Netlify、Vercel和GitHub Pages。</div><div class="project-item-link"><a href="https://admin.hub.nuxt.com/" target="_blank">https://admin.hub.nuxt.com/</a></div></div><div class="project-item"><div class="project-item-title">Netlify App</div><div class="project-item-desc">快速构建优质网络体验的起点</div><div class="project-item-link"><a href="https://app.netlify.com/" target="_blank">https://app.netlify.com/</a></div></div><div class="project-item"><div class="project-item-title">GitHub Pages</div><div class="project-item-desc">GitHub Pages 是一个静态站点托管服务</div><div class="project-item-link"><a href="https://docs.github.com/zh/pages/getting-started-with-github-pages" target="_blank">https://docs.github.com/zh/pages/getting-started-with-github-pages</a></div></div><div class="project-item"><div class="project-item-title">Vercel</div><div class="project-item-desc">Vercel 前端云为开发者提供框架、工作流程及基础设施，助力构建更快、更个性化的 Web 应用</div><div class="project-item-link"><a href="https://vercel.com/dashboard" target="_blank">https://vercel.com/dashboard</a></div></div><div class="project-item"><div class="project-item-title">Heroku</div><div class="project-item-desc">Heroku 是一个平台即服务（PaaS），允许开发人员在云端构建、运行和管理应用程序</div><div class="project-item-link"><a href="https://www.heroku.com/" target="_blank">https://www.heroku.com/</a></div></div><div class="project-item"><div class="project-item-title">Serverless</div><div class="project-item-desc">基于无服务器架构</div><div class="project-item-link"><a href="https://cloud.tencent.com/product/sls" target="_blank">https://cloud.tencent.com/product/sls</a></div></div><h4>"真"学习</h4><div class="project-item"><div class="project-item-title">V2ex</div><div class="project-item-desc">V2EX是程序员和设计师分享知识和经验的社区</div><div class="project-item-link"><a href="https://www.v2ex.com/" target="_blank">https://www.v2ex.com/</a></div></div><div class="project-item"><div class="project-item-title">bilibili</div><div class="project-item-desc">Bilibili 是一个视频弹幕网站，提供高清视频、音频、直播、漫画以及娱乐资讯等内容</div><div class="project-item-link"><a href="https://www.bilibili.com/" target="_blank">https://www.bilibili.com/</a></div></div><div class="project-item"><div class="project-item-title">YouTube</div><div class="project-item-desc">YouTube 是一个全球性的视频分享网站，最初由 Gawker 公司创办，现在属于 Google</div><div class="project-item-link"><a href="https://www.youtube.com/" target="_blank">https://www.youtube.com/</a></div></div>]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[组件库 | RYANUO]]></title>
            <link>https://ryanuo.cc/zh/navs/libraries</link>
            <guid>https://ryanuo.cc/zh/navs/libraries</guid>
            <pubDate>Thu, 18 Dec 2025 06:50:24 GMT</pubDate>
            <description><![CDATA[该前端工具库收录了个人使用或遇到过的各类库，按类别分类便于搜索。]]></description>
            <content:encoded><![CDATA[<h4>工具集</h4><div class="project-item"><div class="project-item-title">Lodash</div><div class="project-item-desc">Lodash 是一个一致性、模块化且高性能的 JavaScript 工具库</div><div class="project-item-link"><a href="https://www.lodashjs.com" target="_blank">https://www.lodashjs.com</a></div></div><div class="project-item"><div class="project-item-title">Ramda</div><div class="project-item-desc">函数式编程实用工具库</div><div class="project-item-link"><a href="https://ramdajs.com" target="_blank">https://ramdajs.com</a></div></div><div class="project-item"><div class="project-item-title">qs</div><div class="project-item-desc">支持嵌套和数组的查询字符串解析器，其 API 对 node.js url 模块的用户来说很熟悉</div><div class="project-item-link"><a href="https://github.com/ljharb/qs" target="_blank">https://github.com/ljharb/qs</a></div></div><div class="project-item"><div class="project-item-title">ahooks</div><div class="project-item-desc">用于 URL 状态管理的 React Hooks</div><div class="project-item-link"><a href="https://ahooks.js.org" target="_blank">https://ahooks.js.org</a></div></div><div class="project-item"><div class="project-item-title">VueUse</div><div class="project-item-desc">收集了基本的Vue组合工具</div><div class="project-item-link"><a href="https://vueuse.org/" target="_blank">https://vueuse.org/</a></div></div><h4>时间处理</h4><div class="project-item"><div class="project-item-title">Moment.js</div><div class="project-item-desc">在JavaScript中解析、验证、操作和展示日期</div><div class="project-item-link"><a href="https://momentjs.com" target="_blank">https://momentjs.com</a></div></div><div class="project-item"><div class="project-item-title">Day.js</div><div class="project-item-desc">作为 Moment.js 的快速替代方案，仅2kB大小，拥有相同的现代API</div><div class="project-item-link"><a href="https://day.js.org" target="_blank">https://day.js.org</a></div></div><h4>精度处理</h4><div class="project-item"><div class="project-item-title">bignumber.js</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://mikemcl.github.io/bignumber.js/" target="_blank">https://mikemcl.github.io/bignumber.js/</a></div></div><div class="project-item"><div class="project-item-title">Big.js</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://mikemcl.github.io/big.js/" target="_blank">https://mikemcl.github.io/big.js/</a></div></div><h4>滚动处理</h4><div class="project-item"><div class="project-item-title">better-scroll</div><div class="project-item-desc">基于原生滚动事件的滚动处理库</div><div class="project-item-link"><a href="https://better-scroll.github.io/docs/zh-CN/" target="_blank">https://better-scroll.github.io/docs/zh-CN/</a></div></div><div class="project-item"><div class="project-item-title">Smooth Scrollbar</div><div class="project-item-desc">在现代浏览器中自定义滚动条，提供流畅的滚动体验。</div><div class="project-item-link"><a href="https://github.com/Grsmto/smoothscroll" target="_blank">https://github.com/Grsmto/smoothscroll</a></div></div><div class="project-item"><div class="project-item-title">react-scroll</div><div class="project-item-desc">轻量级的库，用于增强React应用程序中的滚动功能</div><div class="project-item-link"><a href="https://github.com/fisshy/react-scroll" target="_blank">https://github.com/fisshy/react-scroll</a></div></div><div class="project-item"><div class="project-item-title">React Scroll Parallax</div><div class="project-item-desc">React滚动视差效果库</div><div class="project-item-link"><a href="https://react-scroll-parallax.damnthat.tv/docs/intro" target="_blank">https://react-scroll-parallax.damnthat.tv/docs/intro</a></div></div><div class="project-item"><div class="project-item-title">Lenis</div><div class="project-item-desc">平滑滚动库</div><div class="project-item-link"><a href="https://github.com/darkroomengineering/lenis" target="_blank">https://github.com/darkroomengineering/lenis</a></div></div><h4>事件处理</h4><div class="project-item"><div class="project-item-title">hotkeys-js</div><div class="project-item-desc">一个用于捕获键盘输入的健壮的 Javascript 库</div><div class="project-item-link"><a href="https://github.com/jaywcjlove/hotkeys-js/" target="_blank">https://github.com/jaywcjlove/hotkeys-js/</a></div></div><h4>单一组件</h4><div class="project-item"><div class="project-item-title">Vue Final Modal</div><div class="project-item-desc">Vue Final Modal 是一款适用于 Vue 3 的功能强大且体积小巧的模态对话框组件库</div><div class="project-item-link"><a href="https://github.com/vue-final/vue-final-modal" target="_blank">https://github.com/vue-final/vue-final-modal</a></div></div><div class="project-item"><div class="project-item-title">Floating Vue</div><div class="project-item-desc">轻松实现工具提示与下拉菜单</div><div class="project-item-link"><a href="https://github.com/Akryum/floating-vue" target="_blank">https://github.com/Akryum/floating-vue</a></div></div><h4>图片处理</h4><div class="project-item"><div class="project-item-title">Medium Zoom</div><div class="project-item-desc">一个类似 Medium 风格的 JavaScript 图片缩放库</div><div class="project-item-link"><a href="https://github.com/francoischalifour/medium-zoom" target="_blank">https://github.com/francoischalifour/medium-zoom</a></div></div><div class="project-item"><div class="project-item-title">react-image-crop</div><div class="project-item-desc">一个强大的 JavaScript 图片编辑器，可与各种技术栈集成</div><div class="project-item-link"><a href="https://github.com/pqina/filepond" target="_blank">https://github.com/pqina/filepond</a></div></div><div class="project-item"><div class="project-item-title">html2canvas</div><div class="project-item-desc">使用 JavaScript 进行网页截图</div><div class="project-item-link"><a href="https://github.com/niklasvh/html2canvas" target="_blank">https://github.com/niklasvh/html2canvas</a></div></div><div class="project-item"><div class="project-item-title">html-to-image</div><div class="project-item-desc">通过 HTML5 canvas 和 SVG 将 DOM 节点生成为图像</div><div class="project-item-link"><a href="https://github.com/bubkoo/html-to-image?tab=readme-ov-file#toPng" target="_blank">https://github.com/bubkoo/html-to-image?tab=readme-ov-file#toPng</a></div></div><div class="project-item"><div class="project-item-title">satori</div><div class="project-item-desc">用于将 HTML 和 CSS 转换为 SVG 的智能库</div><div class="project-item-link"><a href="https://github.com/vercel/satori" target="_blank">https://github.com/vercel/satori</a></div></div><div class="project-item"><div class="project-item-title">node-qrcode</div><div class="project-item-desc">适用于 Node.js 的二维码生成器</div><div class="project-item-link"><a href="https://github.com/soldair/node-qrcode" target="_blank">https://github.com/soldair/node-qrcode</a></div></div><div class="project-item"><div class="project-item-title">qrcode.react</div><div class="project-item-desc">适用于 React 的 QRCode 组件</div><div class="project-item-link"><a href="https://github.com/zpao/qrcode.react" target="_blank">https://github.com/zpao/qrcode.react</a></div></div><div class="project-item"><div class="project-item-title">qrcode-terminal</div><div class="project-item-desc">在终端中生成二维码</div><div class="project-item-link"><a href="https://github.com/gtanner/qrcode-terminal" target="_blank">https://github.com/gtanner/qrcode-terminal</a></div></div><div class="project-item"><div class="project-item-title">compressorjs</div><div class="project-item-desc">用于压缩图片的 JavaScript 库</div><div class="project-item-link"><a href="https://github.com/fengyuanchen/compressorjs" target="_blank">https://github.com/fengyuanchen/compressorjs</a></div></div><div class="project-item"><div class="project-item-title">imagemin</div><div class="project-item-desc">无缝地压缩图片</div><div class="project-item-link"><a href="https://github.com/imagemin/imagemin" target="_blank">https://github.com/imagemin/imagemin</a></div></div><h4>文件处理</h4><div class="project-item"><div class="project-item-title">FileSaver</div><div class="project-item-desc">一个用于在浏览器中保存文件的 JavaScript 库</div><div class="project-item-link"><a href="https://github.com/eligrey/FileSaver.js" target="_blank">https://github.com/eligrey/FileSaver.js</a></div></div><div class="project-item"><div class="project-item-title">StreamSaver.js</div><div class="project-item-desc">StreamSaver能够直接将流异步写入文件系统</div><div class="project-item-link"><a href="https://github.com/jimmywarting/StreamSaver.js" target="_blank">https://github.com/jimmywarting/StreamSaver.js</a></div></div><div class="project-item"><div class="project-item-title">sheetjs</div><div class="project-item-desc">SheetJS 电子表格数据工具包</div><div class="project-item-link"><a href="https://github.com/SheetJS/sheetjs" target="_blank">https://github.com/SheetJS/sheetjs</a></div></div><div class="project-item"><div class="project-item-title">node-fs-extra</div><div class="project-item-desc">一个 Node.js 的扩展，它为 fs 模块添加了额外的功能</div><div class="project-item-link"><a href="https://github.com/jprichardson/node-fs-extra" target="_blank">https://github.com/jprichardson/node-fs-extra</a></div></div><div class="project-item"><div class="project-item-title">rimraf</div><div class="project-item-desc">一个用于删除文件的 Node.js 库</div><div class="project-item-link"><a href="https://github.com/isaacs/rimraf" target="_blank">https://github.com/isaacs/rimraf</a></div></div><div class="project-item"><div class="project-item-title">node-glob</div><div class="project-item-desc">一个用于匹配文件路径的 Node.js 库</div><div class="project-item-link"><a href="https://github.com/isaacs/node-glob" target="_blank">https://github.com/isaacs/node-glob</a></div></div><div class="project-item"><div class="project-item-title">node-globby</div><div class="project-item-desc">Promise API\多种模式\否定模式\扩展目录</div><div class="project-item-link"><a href="https://github.com/sindresorhus/globby" target="_blank">https://github.com/sindresorhus/globby</a></div></div><div class="project-item"><div class="project-item-title">copy</div><div class="project-item-desc">一个用于复制文件的 Node.js 库</div><div class="project-item-link"><a href="https://github.com/sindresorhus/cpy" target="_blank">https://github.com/sindresorhus/cpy</a></div></div><h4>Markdown 解析器</h4><div class="project-item"><div class="project-item-title">markdown-it</div><div class="project-item-desc">一个快速、可扩展且功能强大的 Markdown 解析器</div><div class="project-item-link"><a href="https://github.com/markdown-it/markdown-it" target="_blank">https://github.com/markdown-it/markdown-it</a></div></div><div class="project-item"><div class="project-item-title">mermaid</div><div class="project-item-desc">通过类似于markdown的文本生成流程图或序列图等图表</div><div class="project-item-link"><a href="https://github.com/mermaid-js/mermaid" target="_blank">https://github.com/mermaid-js/mermaid</a></div></div><div class="project-item"><div class="project-item-title">slidev</div><div class="project-item-desc">一个基于 Vue 的基于 Markdown 的演示文稿生成器</div><div class="project-item-link"><a href="https://github.com/slidevjs/slidev" target="_blank">https://github.com/slidevjs/slidev</a></div></div><div class="project-item"><div class="project-item-title">mdx-deck</div><div class="project-item-desc">一个基于 React 的基于 Markdown 的演示文稿生成器</div><div class="project-item-link"><a href="https://github.com/jxnblk/mdx-deck" target="_blank">https://github.com/jxnblk/mdx-deck</a></div></div><div class="project-item"><div class="project-item-title">react-markdown</div><div class="project-item-desc">适用于React的Markdown组件</div><div class="project-item-link"><a href="https://github.com/remarkjs/react-markdown" target="_blank">https://github.com/remarkjs/react-markdown</a></div></div><h4>代码高亮</h4><div class="project-item"><div class="project-item-title">shiki</div><div class="project-item-desc">一个轻量、可扩展的语法高亮库</div><div class="project-item-link"><a href="https://github.com/shikijs/shiki" target="_blank">https://github.com/shikijs/shiki</a></div></div><div class="project-item"><div class="project-item-title">prismjs</div><div class="project-item-desc">一个轻量、可扩展的语法高亮库</div><div class="project-item-link"><a href="https://github.com/PrismJS/prism" target="_blank">https://github.com/PrismJS/prism</a></div></div><div class="project-item"><div class="project-item-title">highlight.js</div><div class="project-item-desc">JavaScript 语法高亮器，具备自动识别语言功能且无任何依赖项。</div><div class="project-item-link"><a href="https://github.com/highlightjs/highlight.js" target="_blank">https://github.com/highlightjs/highlight.js</a></div></div><div class="project-item"><div class="project-item-title">react-highlighter</div><div class="project-item-desc">通过内联样式使用 Prismjs 或 Highlightjs AST 为 react 提供语法高亮组件。</div><div class="project-item-link"><a href="https://github.com/react-syntax-highlighter/react-syntax-highlighter" target="_blank">https://github.com/react-syntax-highlighter/react-syntax-highlighter</a></div></div><h4>轮播</h4><div class="project-item"><div class="project-item-title">swiper</div><div class="project-item-desc">一个强大的基于 JavaScript 的滑动/滚动插件</div><div class="project-item-link"><a href="https://github.com/nolimits4web/swiper" target="_blank">https://github.com/nolimits4web/swiper</a></div></div><h4>表单处理</h4><div class="project-item"><div class="project-item-title">formik</div><div class="project-item-desc">一个用于处理表单的 React 库</div><div class="project-item-link"><a href="https://github.com/jaredpalmer/formik" target="_blank">https://github.com/jaredpalmer/formik</a></div></div><div class="project-item"><div class="project-item-title">react-hook-form</div><div class="project-item-desc">用于表单状态管理和验证的 React Hooks （Web + React Native）</div><div class="project-item-link"><a href="https://github.com/react-hook-form/react-hook-form" target="_blank">https://github.com/react-hook-form/react-hook-form</a></div></div><div class="project-item"><div class="project-item-title">formily</div><div class="project-item-desc">一个强大的表单处理库</div><div class="project-item-link"><a href="https://github.com/alibaba/formily" target="_blank">https://github.com/alibaba/formily</a></div></div><h4>表格</h4><div class="project-item"><div class="project-item-title">ag-grid</div><div class="project-item-desc">用于构建企业应用程序的最佳 JavaScript 数据表。支持 React / Angular / Vue / Plain JavaScript。</div><div class="project-item-link"><a href="https://github.com/ag-grid/ag-grid" target="_blank">https://github.com/ag-grid/ag-grid</a></div></div><h4>拖拽</h4><div class="project-item"><div class="project-item-title">Sortable</div><div class="project-item-desc">适用于现代浏览器和触摸设备的可重新排序的拖放列表。不需要 jQuery 或框架。</div><div class="project-item-link"><a href="https://github.com/SortableJS/Sortable" target="_blank">https://github.com/SortableJS/Sortable</a></div></div><div class="project-item"><div class="project-item-title">dnd-kit</div><div class="project-item-desc">现代、轻量级、高性能、可访问和可扩展的 React 拖放工具包。</div><div class="project-item-link"><a href="https://github.com/clauderic/dnd-kit" target="_blank">https://github.com/clauderic/dnd-kit</a></div></div><div class="project-item"><div class="project-item-title">vue-draggable-plus</div><div class="project-item-desc">支持 Vue 3 和 Vue 2 的通用拖放组件</div><div class="project-item-link"><a href="https://github.com/Alfred-Skyblue/vue-draggable-plus" target="_blank">https://github.com/Alfred-Skyblue/vue-draggable-plus</a></div></div><h4>用户体验</h4><div class="project-item"><div class="project-item-title">react-joyride</div><div class="project-item-desc">在应用中创建导览</div><div class="project-item-link"><a href="https://github.com/gilbarbara/react-joyride" target="_blank">https://github.com/gilbarbara/react-joyride</a></div></div><div class="project-item"><div class="project-item-title">vue-tour</div><div class="project-item-desc">为 Vue 应用程序创建教程</div><div class="project-item-link"><a href="https://github.com/pulsardev/vue-tour" target="_blank">https://github.com/pulsardev/vue-tour</a></div></div><div class="project-item"><div class="project-item-title">shepherd</div><div class="project-item-desc">一个简单易用的用户引导库</div><div class="project-item-link"><a href="https://github.com/shepherd-pro/shepherd" target="_blank">https://github.com/shepherd-pro/shepherd</a></div></div><div class="project-item"><div class="project-item-title">intro.js</div><div class="project-item-desc">轻量级、用户友好的入职旅游库</div><div class="project-item-link"><a href="https://github.com/usablica/intro.js" target="_blank">https://github.com/usablica/intro.js</a></div></div><div class="project-item"><div class="project-item-title">driver.js</div><div class="project-item-desc">一个轻量级、无依赖性的普通 JavaScript 引擎，可将用户的注意力集中在整个页面上</div><div class="project-item-link"><a href="https://github.com/kamranahmedse/driver.js" target="_blank">https://github.com/kamranahmedse/driver.js</a></div></div><div class="project-item"><div class="project-item-title">clipboard.js</div><div class="project-item-desc">一个简单、可扩展的 JavaScript 复制到剪贴板库</div><div class="project-item-link"><a href="https://github.com/zenorocha/clipboard.js" target="_blank">https://github.com/zenorocha/clipboard.js</a></div></div><div class="project-item"><div class="project-item-title">copy-to-clipboard</div><div class="project-item-desc">一个简单、可扩展的 JavaScript 复制到剪贴板库</div><div class="project-item-link"><a href="https://github.com/sudodoki/copy-to-clipboard" target="_blank">https://github.com/sudodoki/copy-to-clipboard</a></div></div><div class="project-item"><div class="project-item-title">screenfull</div><div class="project-item-desc">用于跨浏览器使用 JavaScript 全屏 API 的简单包装器</div><div class="project-item-link"><a href="https://github.com/sindresorhus/screenfull" target="_blank">https://github.com/sindresorhus/screenfull</a></div></div><div class="project-item"><div class="project-item-title">nprogress</div><div class="project-item-desc">一个简单的进度条，用于在页面顶部显示进度。</div><div class="project-item-link"><a href="https://github.com/rstacruz/nprogress" target="_blank">https://github.com/rstacruz/nprogress</a></div></div><h4>评论系统</h4><div class="project-item"><div class="project-item-title">valine</div><div class="project-item-desc">一个快速、简单和强大的评论系统。</div><div class="project-item-link"><a href="https://github.com/xCss/Valine" target="_blank">https://github.com/xCss/Valine</a></div></div><div class="project-item"><div class="project-item-title">giscus</div><div class="project-item-desc">由 GitHub Discussions 提供支持的评论系统。</div><div class="project-item-link"><a href="https://github.com/giscus/giscus" target="_blank">https://github.com/giscus/giscus</a></div></div><div class="project-item"><div class="project-item-title">gitalk</div><div class="project-item-desc">Gitalk 是一个基于 Github Issue 和 Preact 的现代评论组件。</div><div class="project-item-link"><a href="https://github.com/gitalk/gitalk" target="_blank">https://github.com/gitalk/gitalk</a></div></div><div class="project-item"><div class="project-item-title">waline</div><div class="project-item-desc">简单、安全的评论系统</div><div class="project-item-link"><a href="https://github.com/walinejs/waline" target="_blank">https://github.com/walinejs/waline</a></div></div><div class="project-item"><div class="project-item-title">twikoo</div><div class="project-item-desc">一个简洁、安全、免费的静态网站评论系统</div><div class="project-item-link"><a href="https://github.com/twikoojs/twikoo" target="_blank">https://github.com/twikoojs/twikoo</a></div></div><h4>编译构建打包</h4><div class="project-item"><div class="project-item-title">esno</div><div class="project-item-desc">Node.js运行时增强了 esbuild，用于加载 TypeScript 和 ESM</div><div class="project-item-link"><a href="https://github.com/antfu/esno" target="_blank">https://github.com/antfu/esno</a></div></div><div class="project-item"><div class="project-item-title">vite</div><div class="project-item-desc">一个现代的前端构建工具，专注于开发速度和开发体验。</div><div class="project-item-link"><a href="https://github.com/vitejs/vite" target="_blank">https://github.com/vitejs/vite</a></div></div><div class="project-item"><div class="project-item-title">rollup</div><div class="project-item-desc">下一代 ES 模块捆绑器</div><div class="project-item-link"><a href="https://github.com/rollup/rollup" target="_blank">https://github.com/rollup/rollup</a></div></div><div class="project-item"><div class="project-item-title">turbo</div><div class="project-item-desc">增量打包器和构建系统，针对 JavaScript 和 TypeScript 进行了优化，用 Rust 编写，包括 Turbopack 和 Turborepo。</div><div class="project-item-link"><a href="https://github.com/vercel/turbo" target="_blank">https://github.com/vercel/turbo</a></div></div><div class="project-item"><div class="project-item-title">webpackjs</div><div class="project-item-desc">用于构建 JavaScript 应用程序的模块化打包器和构建工具。</div><div class="project-item-link"><a href="https://www.webpackjs.com/" target="_blank">https://www.webpackjs.com/</a></div></div><div class="project-item"><div class="project-item-title">babeljs</div><div class="project-item-desc">用于将 ES6 代码转换为浏览器可理解的代码。</div><div class="project-item-link"><a href="https://www.babeljs.cn/" target="_blank">https://www.babeljs.cn/</a></div></div><div class="project-item"><div class="project-item-title">esbuild</div><div class="project-item-desc">一个 JavaScript 代码构建工具和模块打包器。</div><div class="project-item-link"><a href="https://esbuild.github.io/" target="_blank">https://esbuild.github.io/</a></div></div><div class="project-item"><div class="project-item-title">swc</div><div class="project-item-desc">SWC 是一个可扩展的基于 Rust 的平台，适用于下一代快速开发人员工具。</div><div class="project-item-link"><a href="https://swc.rs/" target="_blank">https://swc.rs/</a></div></div><div class="project-item"><div class="project-item-title">tsup</div><div class="project-item-desc">捆绑 TypeScript 库的最简单、最快捷的方法。</div><div class="project-item-link"><a href="https://github.com/egoist/tsup" target="_blank">https://github.com/egoist/tsup</a></div></div><div class="project-item"><div class="project-item-title">unbuild</div><div class="project-item-desc">统一的 javascript 构建系统</div><div class="project-item-link"><a href="https://github.com/unjs/unbuild" target="_blank">https://github.com/unjs/unbuild</a></div></div><div class="project-item"><div class="project-item-title">unplugin</div><div class="project-item-desc">适用于 Vite、Rollup、Webpack、esbuild、rolldown 等的统一插件系统</div><div class="project-item-link"><a href="https://github.com/unjs/unplugin" target="_blank">https://github.com/unjs/unplugin</a></div></div>]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Tools | RYANUO]]></title>
            <link>https://ryanuo.cc/navs/tools</link>
            <guid>https://ryanuo.cc/navs/tools</guid>
            <pubDate>Thu, 18 Dec 2025 06:50:24 GMT</pubDate>
            <description><![CDATA[Front-end online tools are being organized and are continuously updated.]]></description>
            <content:encoded><![CDATA[<h4>Commonly Used Tools</h4><div class="project-item"><div class="project-item-title">Device Framed Screenshots</div><div class="project-item-desc">A feature that presents app screenshots within realistic device models, enabling developers or marketers to achieve more professional and visually appealing app presentations.</div><div class="project-item-link"><a href="https://deviceshots.com/app" target="_blank">https://deviceshots.com/app</a></div></div><div class="project-item"><div class="project-item-title">Online Photo</div><div class="project-item-desc">A powerful online alternative that offers an editing experience comparable to Adobe Photoshop. Users can open and use it directly in their web browser without downloading or installing any software, allowing them to edit photos, apply effects, filters, add text, crop, resize images, and more. It supports a variety of formats such as PSD, XD, Sketch, XCF, and RAW, and includes real-time link status checking to ensure seamless access to this online photo editing service.</div><div class="project-item-link"><a href="https://www.photopea.com/" target="_blank">https://www.photopea.com/</a></div></div><div class="project-item"><div class="project-item-title">images-tools</div><div class="project-item-desc">This is a simple web app that allows you to upload an image and get a cover of your choice.</div><div class="project-item-link"><a href="https://github.com/ryanuo/own-cover" target="_blank">https://github.com/ryanuo/own-cover</a></div></div><div class="project-item"><div class="project-item-title">Cover Image Generator</div><div class="project-item-desc">pic prose is a better cover image generator tool for medium you tube bili bili blog and more</div><div class="project-item-link"><a href="https://github.com/gezhaoyou/picprose" target="_blank">https://github.com/gezhaoyou/picprose</a></div></div><h4>Quick Look</h4><div class="project-item"><div class="project-item-title">Quick Reference</div><div class="project-item-desc">Share quick reference cheat sheet for developers.</div><div class="project-item-link"><a href="https://wangchujiang.com/reference/index.html" target="_blank">https://wangchujiang.com/reference/index.html</a></div></div><div class="project-item"><div class="project-item-title">reference</div><div class="project-item-desc">Share quick reference cheat sheet for developers.</div><div class="project-item-link"><a href="https://github.com/Fechin/reference" target="_blank">https://github.com/Fechin/reference</a></div></div><div class="project-item"><div class="project-item-title">Excel Reference</div><div class="project-item-desc">Excel Reference, a tool that helps you quickly find the Excel function you need.</div><div class="project-item-link"><a href="https://www.lanrenexcel.com/" target="_blank">https://www.lanrenexcel.com/</a></div></div><h4>Google plugins</h4><div class="project-item"><div class="project-item-title">react1s</div><div class="project-item-desc">Click on page elements to jump to the editor, supports React project local development, Option(Alt)+Click on corresponding element on the page to jump to the editor's corresponding component line and column.</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/react1s/gpcoahaomdfmekggblkckofkgjggnjlp" target="_blank">https://chromewebstore.google.com/detail/react1s/gpcoahaomdfmekggblkckofkgjggnjlp</a></div></div><div class="project-item"><div class="project-item-title">react-developer-tools</div><div class="project-item-desc">React Developer Tools, used for debugging React applications.</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi" target="_blank">https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi</a></div></div><div class="project-item"><div class="project-item-title">Extension Manager</div><div class="project-item-desc">Chrome Extension Manager Which Is Used To Manage Chrome Extensions。</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/%E6%89%A9%E5%B1%95%E7%AE%A1%E7%90%86%E5%99%A8%EF%BC%88extension-manager%EF%BC%89/gjldcdngmdknpinoemndlidpcabkggco" target="_blank">https://chromewebstore.google.com/detail/%E6%89%A9%E5%B1%95%E7%AE%A1%E7%90%86%E5%99%A8%EF%BC%88extension-manager%EF%BC%89/gjldcdngmdknpinoemndlidpcabkggco</a></div></div><div class="project-item"><div class="project-item-title">FeHelper</div><div class="project-item-desc">JSON automatic formatting, manual formatting, support for sorting, decoding, downloading, and more features can be installed on demand in the configuration page!</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/fehelper前端助手/pkgccpejnmalmdinmhkkfafefagiiiad" target="_blank">https://chromewebstore.google.com/detail/fehelper前端助手/pkgccpejnmalmdinmhkkfafefagiiiad</a></div></div><div class="project-item"><div class="project-item-title">uBlock Origin</div><div class="project-item-desc">Ad blocker, supports custom rules, can block ads and other disruptive content on websites.</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm" target="_blank">https://chromewebstore.google.com/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm</a></div></div><div class="project-item"><div class="project-item-title">Immersive Translation</div><div class="project-item-desc">Immersive translation, supports translating PDF files, can convert text in PDF files into speech for playback, and allows you to choose whether to read aloud.</div><div class="project-item-link"><a href="https://chromewebstore.google.com/detail/沉浸式翻译-双语对照网页翻译-pdf文档翻译/bpoadfkcbjbfhfodiogcnhhhpibjhbnh" target="_blank">https://chromewebstore.google.com/detail/沉浸式翻译-双语对照网页翻译-pdf文档翻译/bpoadfkcbjbfhfodiogcnhhhpibjhbnh</a></div></div><h4>Other</h4><div class="project-item"><div class="project-item-title">Worm Tribe</div><div class="project-item-desc">Quick search worm tribe exclusive debut products on the whole network for you to aggregate google baidu bing and other domestic and foreign comprehensive search and academic resource professional field knowledge and other vertical search accurate search convenient interaction it is your first stop for internet search</div><div class="project-item-link"><a href="https://search.chongbuluo.com/" target="_blank">https://search.chongbuluo.com/</a></div></div><div class="project-item"><div class="project-item-title">Navs Summary</div><div class="project-item-desc">Front End Navigation Backlink Summary</div><div class="project-item-link"><a href="https://github.com/ryanuo/navs" target="_blank">https://github.com/ryanuo/navs</a></div></div><div class="project-item"><div class="project-item-title">Independent Developer</div><div class="project-item-desc">A list of independent developers in China, including the number of followers, number of stars, number of repositories, etc.</div><div class="project-item-link"><a href="https://github.com/1c7/chinese-independent-developer" target="_blank">https://github.com/1c7/chinese-independent-developer</a></div></div>]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Navs | RYANUO]]></title>
            <link>https://ryanuo.cc/navs</link>
            <guid>https://ryanuo.cc/navs</guid>
            <pubDate>Thu, 18 Dec 2025 06:50:24 GMT</pubDate>
            <description><![CDATA[Front-end navigation includes commonly used front-end third-party websites, front-end knowledge, and front-end related content.]]></description>
            <content:encoded><![CDATA[<h4>Common Tools</h4><div class="project-item"><div class="project-item-title">Web Tools</div><div class="project-item-desc">Many many useful Web Online Tools For Web Developers & Programmers</div><div class="project-item-link"><a href="https://wangchujiang.com/tools/#/" target="_blank">https://wangchujiang.com/tools/#/</a></div></div><div class="project-item"><div class="project-item-title">Can I Use</div><div class="project-item-desc">provides up-to-date browser support tables for support of front-end web technologies on desktop and mobile web browsers.</div><div class="project-item-link"><a href="https://caniuse.com/" target="_blank">https://caniuse.com/</a></div></div><div class="project-item"><div class="project-item-title">Carbon</div><div class="project-item-desc">Code generation image.</div><div class="project-item-link"><a href="https://carbon.now.sh/" target="_blank">https://carbon.now.sh/</a></div></div><div class="project-item"><div class="project-item-title">Tool.lu</div><div class="project-item-desc">Online Tool Library.</div><div class="project-item-link"><a href="https://tool.lu/" target="_blank">https://tool.lu/</a></div></div><div class="project-item"><div class="project-item-title">Npm Devtool</div><div class="project-item-desc">the npm package refers to the cdn online</div><div class="project-item-link"><a href="https://npm.devtool.tech/" target="_blank">https://npm.devtool.tech/</a></div></div><div class="project-item"><div class="project-item-title">JSON Tool</div><div class="project-item-desc">Provides services such as JSON parsing, validation, formatting, compression, editors, and the conversion between JSON and XML.</div><div class="project-item-link"><a href="https://www.json.cn/" target="_blank">https://www.json.cn/</a></div></div><h4>English Study</h4><div class="project-item"><div class="project-item-title">1000-hours</div><div class="project-item-desc">1000 hours of learning English</div><div class="project-item-link"><a href="https://github.com/xiaolai/everyone-can-use-english" target="_blank">https://github.com/xiaolai/everyone-can-use-english</a></div></div><div class="project-item"><div class="project-item-title">cpwp</div><div class="project-item-desc">A collection of 1000+ free English learning resources</div><div class="project-item-link"><a href="https://github.com/antfu/cpwp" target="_blank">https://github.com/antfu/cpwp</a></div></div><h4>AI Navigation</h4><div class="project-item"><div class="project-item-title">Chatgpt of mirror</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://github.com/xx025/carrot" target="_blank">https://github.com/xx025/carrot</a></div></div><div class="project-item"><div class="project-item-title">Chatgpt</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://chat.openai.com/chat" target="_blank">https://chat.openai.com/chat</a></div></div><div class="project-item"><div class="project-item-title">kimi</div><div class="project-item-desc">AI Assistant</div><div class="project-item-link"><a href="https://kimi.moonshot.cn/" target="_blank">https://kimi.moonshot.cn/</a></div></div><div class="project-item"><div class="project-item-title">Tongyi Qianwen</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://tongyi.aliyun.com/qianwen/" target="_blank">https://tongyi.aliyun.com/qianwen/</a></div></div><div class="project-item"><div class="project-item-title">AI Toolset</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://ai-bot.cn/" target="_blank">https://ai-bot.cn/</a></div></div><h4>Rust</h4><div class="project-item"><div class="project-item-title">Rust Crates</div><div class="project-item-desc">Rust community’s crate registry.</div><div class="project-item-link"><a href="https://crates.io/" target="_blank">https://crates.io/</a></div></div><h4>React Ecosystem</h4><div class="project-item"><div class="project-item-title">React</div><div class="project-item-desc">React Chinese Translation Document.</div><div class="project-item-link"><a href="https://zh-hans.react.dev/" target="_blank">https://zh-hans.react.dev/</a></div></div><div class="project-item"><div class="project-item-title">React Router</div><div class="project-item-desc">Navigation routing solution for React apps.</div><div class="project-item-link"><a href="https://reactrouter.com" target="_blank">https://reactrouter.com</a></div></div><div class="project-item"><div class="project-item-title">Next.js</div><div class="project-item-desc">Next.js by Vercel is the full-stack React framework for the web.</div><div class="project-item-link"><a href="https://nextjs.org" target="_blank">https://nextjs.org</a></div></div><div class="project-item"><div class="project-item-title">UmiJS</div><div class="project-item-desc">Extensible, router-driven front-end framework with rich plugins.</div><div class="project-item-link"><a href="https://umijs.org" target="_blank">https://umijs.org</a></div></div><div class="project-item"><div class="project-item-title">Ant Design</div><div class="project-item-desc">The world's second most popular React UI framework.</div><div class="project-item-link"><a href="https://ant-design.antgroup.com" target="_blank">https://ant-design.antgroup.com</a></div></div><div class="project-item"><div class="project-item-title">Ant Design Mobile</div><div class="project-item-desc">Essential UI blocks for building mobile web apps.</div><div class="project-item-link"><a href="http://ant-design-mobile.antgroup.com" target="_blank">http://ant-design-mobile.antgroup.com</a></div></div><div class="project-item"><div class="project-item-title">Mantine</div><div class="project-item-desc">Modern React component library and hooks collection.</div><div class="project-item-link"><a href="https://mantine.dev" target="_blank">https://mantine.dev</a></div></div><div class="project-item"><div class="project-item-title">zustand</div><div class="project-item-desc">Minimal and flexible state management library.</div><div class="project-item-link"><a href="https://docs.pmnd.rs/zustand/getting-started/introduction" target="_blank">https://docs.pmnd.rs/zustand/getting-started/introduction</a></div></div><div class="project-item"><div class="project-item-title">Valtio</div><div class="project-item-desc">makes proxy-state simple for React and Vanilla</div><div class="project-item-link"><a href="https://valtio.pmnd.rs" target="_blank">https://valtio.pmnd.rs</a></div></div><div class="project-item"><div class="project-item-title">Jotai</div><div class="project-item-desc">Primitive And Flexible State Management For React</div><div class="project-item-link"><a href="https://jotai.jscn.org" target="_blank">https://jotai.jscn.org</a></div></div><div class="project-item"><div class="project-item-title">Redux</div><div class="project-item-desc">Predictable state container for JavaScript apps.</div><div class="project-item-link"><a href="https://cn.redux.js.org" target="_blank">https://cn.redux.js.org</a></div></div><div class="project-item"><div class="project-item-title">MobX</div><div class="project-item-desc">Reactive state management library.</div><div class="project-item-link"><a href="https://zh.mobx.js.org" target="_blank">https://zh.mobx.js.org</a></div></div><div class="project-item"><div class="project-item-title">ahooks</div><div class="project-item-desc">Collection of efficient React Hooks.</div><div class="project-item-link"><a href="https://ahooks.js.org" target="_blank">https://ahooks.js.org</a></div></div><h4>Vue Ecosystem</h4><div class="project-item"><div class="project-item-title">Vue3</div><div class="project-item-desc">vue js a progressive java script framework</div><div class="project-item-link"><a href="https://cn.vuejs.org" target="_blank">https://cn.vuejs.org</a></div></div><div class="project-item"><div class="project-item-title">Vue2</div><div class="project-item-desc">vue js a progressive java script framework</div><div class="project-item-link"><a href="https://v2.cn.vuejs.org/" target="_blank">https://v2.cn.vuejs.org/</a></div></div><div class="project-item"><div class="project-item-title">Vue Router</div><div class="project-item-desc">The official Router for Vue.js</div><div class="project-item-link"><a href="https://router.vuejs.org" target="_blank">https://router.vuejs.org</a></div></div><div class="project-item"><div class="project-item-title">Pinia</div><div class="project-item-desc">Intuitive, type safe, light and flexible Store for Vue</div><div class="project-item-link"><a href="https://pinia.vuejs.org" target="_blank">https://pinia.vuejs.org</a></div></div><div class="project-item"><div class="project-item-title">Nuxt.js</div><div class="project-item-desc">Nuxt is an open source framework that makes web development intuitive and powerful. </div><div class="project-item-link"><a href="https://nuxt.com" target="_blank">https://nuxt.com</a></div></div><div class="project-item"><div class="project-item-title">VueUse</div><div class="project-item-desc">Collection of essential Vue Composition Utilities</div><div class="project-item-link"><a href="https://vueuse.org" target="_blank">https://vueuse.org</a></div></div><div class="project-item"><div class="project-item-title">Element Plus</div><div class="project-item-desc">A Vue 3 based component library for designers and developers</div><div class="project-item-link"><a href="https://element-plus.org" target="_blank">https://element-plus.org</a></div></div><div class="project-item"><div class="project-item-title">Ant Design Vue</div><div class="project-item-desc">An enterprise-class UI components based on Ant Design and Vue</div><div class="project-item-link"><a href="https://antdv.com" target="_blank">https://antdv.com</a></div></div><div class="project-item"><div class="project-item-title">Vant</div><div class="project-item-desc">Mobile UI Components built on Vue</div><div class="project-item-link"><a href="https://vant-ui.github.io" target="_blank">https://vant-ui.github.io</a></div></div><h4>CSS Related</h4><div class="project-item"><div class="project-item-title">UnoCSS</div><div class="project-item-desc">The instant on-demand Atomic CSS engine</div><div class="project-item-link"><a href="https://unocss.dev" target="_blank">https://unocss.dev</a></div></div><div class="project-item"><div class="project-item-title">Tailwind CSS</div><div class="project-item-desc">Tailwind CSS is a utility-first CSS framework for rapidly building modern websites without ever leaving your HTML.</div><div class="project-item-link"><a href="https://www.tailwindcss.cn" target="_blank">https://www.tailwindcss.cn</a></div></div><div class="project-item"><div class="project-item-title">iCSS</div><div class="project-item-desc">CSS tricks here they ve got it all</div><div class="project-item-link"><a href="https://github.com/chokcoco/iCSS" target="_blank">https://github.com/chokcoco/iCSS</a></div></div><div class="project-item"><div class="project-item-title">You-need-to-know-css</div><div class="project-item-desc">You-need-to-know-css</div><div class="project-item-link"><a href="https://lhammer.cn" target="_blank">https://lhammer.cn</a></div></div><div class="project-item"><div class="project-item-title">css_tricks</div><div class="project-item-desc">A Summary Of Common Css Styling Tips</div><div class="project-item-link"><a href="https://qishaoxuan.github.io/css_tricks/" target="_blank">https://qishaoxuan.github.io/css_tricks/</a></div></div><div class="project-item"><div class="project-item-title">Confetti Animation</div><div class="project-item-desc">Confetti animation based on canvas.</div><div class="project-item-link"><a href="https://github.com/catdad/canvas-confetti" target="_blank">https://github.com/catdad/canvas-confetti</a></div></div><div class="project-item"><div class="project-item-title">Hover Animation</div><div class="project-item-desc">Collection of CSS3-driven hover effects that can be applied to links, buttons, logos, SVG, featured images, and more. Easily apply to your own elements, modify, or just use for inspiration. Available in CSS, Sass, and LESS.</div><div class="project-item-link"><a href="https://github.com/IanLunn/Hover" target="_blank">https://github.com/IanLunn/Hover</a></div></div><div class="project-item"><div class="project-item-title">Hand-drawn Animation</div><div class="project-item-desc">Create hand-drawn annotations on the web and animate them.</div><div class="project-item-link"><a href="https://github.com/rough-stuff/rough-notation" target="_blank">https://github.com/rough-stuff/rough-notation</a></div></div><div class="project-item"><div class="project-item-title">color picker</div><div class="project-item-desc">color pickers from sketch photoshop chrome github twitter and more</div><div class="project-item-link"><a href="https://github.com/casesandberg/react-color" target="_blank">https://github.com/casesandberg/react-color</a></div></div><div class="project-item"><div class="project-item-title">React Components For Creative Developers</div><div class="project-item-desc">Highly customizable animated components that make your React projects truly stand out</div><div class="project-item-link"><a href="https://www.reactbits.dev/text-animations/split-text" target="_blank">https://www.reactbits.dev/text-animations/split-text</a></div></div><h4>Icon library</h4><div class="project-item"><div class="project-item-title">Icônes</div><div class="project-item-desc">Icon design tool</div><div class="project-item-link"><a href="https://icones.js.org/" target="_blank">https://icones.js.org/</a></div></div><div class="project-item"><div class="project-item-title">Iconify Design</div><div class="project-item-desc">All popular icon sets, one framework</div><div class="project-item-link"><a href="https://iconify.design/" target="_blank">https://iconify.design/</a></div></div><div class="project-item"><div class="project-item-title">WebFX</div><div class="project-item-desc">WebFX is the #1 digital marketing agency delivering over $6B in revenue for our clients. Get a free proposal and explore our digital marketing services.</div><div class="project-item-link"><a href="https://www.webfx.com/tools/emoji-cheat-sheet/" target="_blank">https://www.webfx.com/tools/emoji-cheat-sheet/</a></div></div><div class="project-item"><div class="project-item-title">Yesicon</div><div class="project-item-desc">168 icon sets with 210k+ icons, search across icon sets in multiple languages</div><div class="project-item-link"><a href="https://yesicon.app/" target="_blank">https://yesicon.app/</a></div></div><h4>Web Build</h4><div class="project-item"><div class="project-item-title">NuxtHub</div><div class="project-item-desc">The best place to find and deploy static sites</div><div class="project-item-link"><a href="https://admin.hub.nuxt.com/" target="_blank">https://admin.hub.nuxt.com/</a></div></div><div class="project-item"><div class="project-item-title">Netlify App</div><div class="project-item-desc">Start building the best web experiences in record time</div><div class="project-item-link"><a href="https://app.netlify.com/" target="_blank">https://app.netlify.com/</a></div></div><div class="project-item"><div class="project-item-title">GitHub Pages</div><div class="project-item-desc">GitHub Pages is a static site hosting service</div><div class="project-item-link"><a href="https://docs.github.com/zh/pages/getting-started-with-github-pages" target="_blank">https://docs.github.com/zh/pages/getting-started-with-github-pages</a></div></div><div class="project-item"><div class="project-item-title">Vercel</div><div class="project-item-desc">Vercel Frontend Cloud gives developers the frameworks, workflows, and infrastructure to build a faster, more personalized Web.</div><div class="project-item-link"><a href="https://vercel.com/dashboard" target="_blank">https://vercel.com/dashboard</a></div></div><div class="project-item"><div class="project-item-title">Heroku</div><div class="project-item-desc">Heroku is a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud.</div><div class="project-item-link"><a href="https://www.heroku.com/" target="_blank">https://www.heroku.com/</a></div></div><div class="project-item"><div class="project-item-title">Serverless</div><div class="project-item-desc">Based On The Serverless Framework</div><div class="project-item-link"><a href="https://cloud.tencent.com/product/sls" target="_blank">https://cloud.tencent.com/product/sls</a></div></div><h4>"True" Learning</h4><div class="project-item"><div class="project-item-title">V2ex</div><div class="project-item-desc">V2EX is a community for programmers and designers to share their knowledge and experience.</div><div class="project-item-link"><a href="https://www.v2ex.com/" target="_blank">https://www.v2ex.com/</a></div></div><div class="project-item"><div class="project-item-title">Bilibili</div><div class="project-item-desc">Bilibili is a video barrage website that provides high-definition videos, audios, live broadcasts, comics, and entertainment news.</div><div class="project-item-link"><a href="https://www.bilibili.com/" target="_blank">https://www.bilibili.com/</a></div></div><div class="project-item"><div class="project-item-title">YouTube</div><div class="project-item-desc">YouTube is a global video sharing website, initially founded by Gawker, now owned by Google.</div><div class="project-item-link"><a href="https://www.youtube.com/" target="_blank">https://www.youtube.com/</a></div></div>]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Libraries | RYANUO]]></title>
            <link>https://ryanuo.cc/navs/libraries</link>
            <guid>https://ryanuo.cc/navs/libraries</guid>
            <pubDate>Thu, 18 Dec 2025 06:50:24 GMT</pubDate>
            <description><![CDATA[The front-end tool library includes libraries that have been used or encountered by individuals, categorized by category for easy searching.]]></description>
            <content:encoded><![CDATA[<h4>Toolkit</h4><div class="project-item"><div class="project-item-title">Lodash</div><div class="project-item-desc">Lodash is a consistent, modular, high performance JavaScript utility library.</div><div class="project-item-link"><a href="https://www.lodashjs.com" target="_blank">https://www.lodashjs.com</a></div></div><div class="project-item"><div class="project-item-title">Ramda</div><div class="project-item-desc">Functional programming utility library</div><div class="project-item-link"><a href="https://ramdajs.com" target="_blank">https://ramdajs.com</a></div></div><div class="project-item"><div class="project-item-title">qs</div><div class="project-item-desc">A querystring parser that supports nesting and arrays, with an API familiar to users of the node.js url module.</div><div class="project-item-link"><a href="https://github.com/ljharb/qs" target="_blank">https://github.com/ljharb/qs</a></div></div><div class="project-item"><div class="project-item-title">ahooks</div><div class="project-item-desc">React Hooks for URL State Management.</div><div class="project-item-link"><a href="https://ahooks.js.org" target="_blank">https://ahooks.js.org</a></div></div><div class="project-item"><div class="project-item-title">VueUse</div><div class="project-item-desc">Collection of essential Vue Composition Utilities</div><div class="project-item-link"><a href="https://vueuse.org/" target="_blank">https://vueuse.org/</a></div></div><h4>Time Processing</h4><div class="project-item"><div class="project-item-title">Moment.js</div><div class="project-item-desc">Parse, validate, manipulate, and display dates in javascript.</div><div class="project-item-link"><a href="https://momentjs.com" target="_blank">https://momentjs.com</a></div></div><div class="project-item"><div class="project-item-title">Day.js</div><div class="project-item-desc">Fast 2kB alternative to Moment.js with the same modern API</div><div class="project-item-link"><a href="https://day.js.org" target="_blank">https://day.js.org</a></div></div><h4>Accuracy Processing</h4><div class="project-item"><div class="project-item-title">bignumber.js</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://mikemcl.github.io/bignumber.js/" target="_blank">https://mikemcl.github.io/bignumber.js/</a></div></div><div class="project-item"><div class="project-item-title">Big.js</div><div class="project-item-desc">undefined</div><div class="project-item-link"><a href="https://mikemcl.github.io/big.js/" target="_blank">https://mikemcl.github.io/big.js/</a></div></div><h4>Scroll Handling</h4><div class="project-item"><div class="project-item-title">better-scroll</div><div class="project-item-desc">A scroll handling library based on native scroll events</div><div class="project-item-link"><a href="https://better-scroll.github.io/docs/zh-CN/" target="_blank">https://better-scroll.github.io/docs/zh-CN/</a></div></div><div class="project-item"><div class="project-item-title">Smooth Scrollbar</div><div class="project-item-desc">Customize scrollbars in modern browsers to provide a smooth scrolling experience.</div><div class="project-item-link"><a href="https://github.com/Grsmto/smoothscroll" target="_blank">https://github.com/Grsmto/smoothscroll</a></div></div><div class="project-item"><div class="project-item-title">react-scroll</div><div class="project-item-desc">A lightweight library for enhancing scrolling functionality in React applications</div><div class="project-item-link"><a href="https://github.com/fisshy/react-scroll" target="_blank">https://github.com/fisshy/react-scroll</a></div></div><div class="project-item"><div class="project-item-title">React Scroll Parallax</div><div class="project-item-desc">React scroll parallax effect library</div><div class="project-item-link"><a href="https://react-scroll-parallax.damnthat.tv/docs/intro" target="_blank">https://react-scroll-parallax.damnthat.tv/docs/intro</a></div></div><div class="project-item"><div class="project-item-title">Lenis</div><div class="project-item-desc">Smooth scroll library</div><div class="project-item-link"><a href="https://github.com/darkroomengineering/lenis" target="_blank">https://github.com/darkroomengineering/lenis</a></div></div><h4>Event Handling</h4><div class="project-item"><div class="project-item-title">hotkeys-js</div><div class="project-item-desc">A robust JavaScript library for capturing keyboard inputs</div><div class="project-item-link"><a href="https://github.com/jaywcjlove/hotkeys-js/" target="_blank">https://github.com/jaywcjlove/hotkeys-js/</a></div></div><h4>Individual Components</h4><div class="project-item"><div class="project-item-title">Vue Final Modal</div><div class="project-item-desc">Vue Final Modal is the most powerful yet most light-weight modal library for Vue 3</div><div class="project-item-link"><a href="https://github.com/vue-final/vue-final-modal" target="_blank">https://github.com/vue-final/vue-final-modal</a></div></div><div class="project-item"><div class="project-item-title">Floating Vue</div><div class="project-item-desc">Tooltips & dropdowns made easy</div><div class="project-item-link"><a href="https://github.com/Akryum/floating-vue" target="_blank">https://github.com/Akryum/floating-vue</a></div></div><h4>Image Processing</h4><div class="project-item"><div class="project-item-title">Medium Zoom</div><div class="project-item-desc">A JavaScript library for zooming images like Medium</div><div class="project-item-link"><a href="https://github.com/francoischalifour/medium-zoom" target="_blank">https://github.com/francoischalifour/medium-zoom</a></div></div><div class="project-item"><div class="project-item-title">react-image-crop</div><div class="project-item-desc">A powerful JavaScript Image Editor that integrates with every stack</div><div class="project-item-link"><a href="https://github.com/pqina/filepond" target="_blank">https://github.com/pqina/filepond</a></div></div><div class="project-item"><div class="project-item-title">html2canvas</div><div class="project-item-desc">Screenshots with JavaScript</div><div class="project-item-link"><a href="https://github.com/niklasvh/html2canvas" target="_blank">https://github.com/niklasvh/html2canvas</a></div></div><div class="project-item"><div class="project-item-title">html-to-image</div><div class="project-item-desc">Generates an image from a DOM node using HTML5 canvas and SVG.</div><div class="project-item-link"><a href="https://github.com/bubkoo/html-to-image?tab=readme-ov-file#toPng" target="_blank">https://github.com/bubkoo/html-to-image?tab=readme-ov-file#toPng</a></div></div><div class="project-item"><div class="project-item-title">satori</div><div class="project-item-desc">Enlightened library to convert HTML and CSS to SVG</div><div class="project-item-link"><a href="https://github.com/vercel/satori" target="_blank">https://github.com/vercel/satori</a></div></div><div class="project-item"><div class="project-item-title">node-qrcode</div><div class="project-item-desc">A QR Code generator for Node.js</div><div class="project-item-link"><a href="https://github.com/soldair/node-qrcode" target="_blank">https://github.com/soldair/node-qrcode</a></div></div><div class="project-item"><div class="project-item-title">qrcode.react</div><div class="project-item-desc">A QRCode component for use with React.</div><div class="project-item-link"><a href="https://github.com/zpao/qrcode.react" target="_blank">https://github.com/zpao/qrcode.react</a></div></div><div class="project-item"><div class="project-item-title">qrcode-terminal</div><div class="project-item-desc">Generate QRCode on terminal</div><div class="project-item-link"><a href="https://github.com/gtanner/qrcode-terminal" target="_blank">https://github.com/gtanner/qrcode-terminal</a></div></div><div class="project-item"><div class="project-item-title">compressorjs</div><div class="project-item-desc">A JavaScript library for compressing images</div><div class="project-item-link"><a href="https://github.com/fengyuanchen/compressorjs" target="_blank">https://github.com/fengyuanchen/compressorjs</a></div></div><div class="project-item"><div class="project-item-title">imagemin</div><div class="project-item-desc">Minify images seamlessly</div><div class="project-item-link"><a href="https://github.com/imagemin/imagemin" target="_blank">https://github.com/imagemin/imagemin</a></div></div><h4>File Processing</h4><div class="project-item"><div class="project-item-title">FileSaver</div><div class="project-item-desc">A JavaScript library for saving files in the browser</div><div class="project-item-link"><a href="https://github.com/eligrey/FileSaver.js" target="_blank">https://github.com/eligrey/FileSaver.js</a></div></div><div class="project-item"><div class="project-item-title">StreamSaver.js</div><div class="project-item-desc">StreamSaver asynchronously writes streams directly to the filesystem</div><div class="project-item-link"><a href="https://github.com/jimmywarting/StreamSaver.js" target="_blank">https://github.com/jimmywarting/StreamSaver.js</a></div></div><div class="project-item"><div class="project-item-title">sheetjs</div><div class="project-item-desc">SheetJS Spreadsheet Data Toolkit</div><div class="project-item-link"><a href="https://github.com/SheetJS/sheetjs" target="_blank">https://github.com/SheetJS/sheetjs</a></div></div><div class="project-item"><div class="project-item-title">node-fs-extra</div><div class="project-item-desc">An extension for Node.js that adds extra functionality to the fs module</div><div class="project-item-link"><a href="https://github.com/jprichardson/node-fs-extra" target="_blank">https://github.com/jprichardson/node-fs-extra</a></div></div><div class="project-item"><div class="project-item-title">rimraf</div><div class="project-item-desc">A Node.js library for deleting files</div><div class="project-item-link"><a href="https://github.com/isaacs/rimraf" target="_blank">https://github.com/isaacs/rimraf</a></div></div><div class="project-item"><div class="project-item-title">node-glob</div><div class="project-item-desc">A Node.js library for matching file paths</div><div class="project-item-link"><a href="https://github.com/isaacs/node-glob" target="_blank">https://github.com/isaacs/node-glob</a></div></div><div class="project-item"><div class="project-item-title">node-globby</div><div class="project-item-desc">Promise API\Multiple patterns\Negated patterns\Expands directories</div><div class="project-item-link"><a href="https://github.com/sindresorhus/globby" target="_blank">https://github.com/sindresorhus/globby</a></div></div><div class="project-item"><div class="project-item-title">copy</div><div class="project-item-desc">A Node.js library for copying files</div><div class="project-item-link"><a href="https://github.com/sindresorhus/cpy" target="_blank">https://github.com/sindresorhus/cpy</a></div></div><h4>Markdown</h4><div class="project-item"><div class="project-item-title">markdown-it</div><div class="project-item-desc">A fast, extensible, and powerful Markdown parser</div><div class="project-item-link"><a href="https://github.com/markdown-it/markdown-it" target="_blank">https://github.com/markdown-it/markdown-it</a></div></div><div class="project-item"><div class="project-item-title">mermaid</div><div class="project-item-desc">Generates flowcharts or sequence diagrams (among other chart types) using a syntax similar to Markdown</div><div class="project-item-link"><a href="https://github.com/mermaid-js/mermaid" target="_blank">https://github.com/mermaid-js/mermaid</a></div></div><div class="project-item"><div class="project-item-title">slidev</div><div class="project-item-desc">A presentation generator based on Vue and Markdown</div><div class="project-item-link"><a href="https://github.com/slidevjs/slidev" target="_blank">https://github.com/slidevjs/slidev</a></div></div><div class="project-item"><div class="project-item-title">mdx-deck</div><div class="project-item-desc">A presentation generator based on React and Markdown</div><div class="project-item-link"><a href="https://github.com/jxnblk/mdx-deck" target="_blank">https://github.com/jxnblk/mdx-deck</a></div></div><div class="project-item"><div class="project-item-title">react-markdown</div><div class="project-item-desc">A Markdown component for React</div><div class="project-item-link"><a href="https://github.com/remarkjs/react-markdown" target="_blank">https://github.com/remarkjs/react-markdown</a></div></div><h4>Code Highlighting</h4><div class="project-item"><div class="project-item-title">shiki</div><div class="project-item-desc">A fast and extensible syntax highlighting library for code with dark mode support and customizable themes with source maps</div><div class="project-item-link"><a href="https://github.com/shikijs/shiki" target="_blank">https://github.com/shikijs/shiki</a></div></div><div class="project-item"><div class="project-item-title">prismjs</div><div class="project-item-desc">A lightweight, extensible syntax highlighting library</div><div class="project-item-link"><a href="https://github.com/PrismJS/prism" target="_blank">https://github.com/PrismJS/prism</a></div></div><div class="project-item"><div class="project-item-title">highlight.js</div><div class="project-item-desc">A JavaScript syntax highlighter with language auto-detection and no dependencies.</div><div class="project-item-link"><a href="https://github.com/highlightjs/highlight.js" target="_blank">https://github.com/highlightjs/highlight.js</a></div></div><div class="project-item"><div class="project-item-title">react-highlighter</div><div class="project-item-desc">Provides a syntax highlighting component for React using inline styles with Prismjs or Highlightjs AST.</div><div class="project-item-link"><a href="https://github.com/react-syntax-highlighter/react-syntax-highlighter" target="_blank">https://github.com/react-syntax-highlighter/react-syntax-highlighter</a></div></div><h4>Swiper</h4><div class="project-item"><div class="project-item-title">swiper</div><div class="project-item-desc">A powerful JavaScript-based sliding/scrolling plugin</div><div class="project-item-link"><a href="https://github.com/nolimits4web/swiper" target="_blank">https://github.com/nolimits4web/swiper</a></div></div><h4>Form Processing</h4><div class="project-item"><div class="project-item-title">formik</div><div class="project-item-desc">A React library for form handling</div><div class="project-item-link"><a href="https://github.com/jaredpalmer/formik" target="_blank">https://github.com/jaredpalmer/formik</a></div></div><div class="project-item"><div class="project-item-title">react-hook-form</div><div class="project-item-desc">A React Hooks library for form state management and validation (Web + React Native)</div><div class="project-item-link"><a href="https://github.com/react-hook-form/react-hook-form" target="_blank">https://github.com/react-hook-form/react-hook-form</a></div></div><div class="project-item"><div class="project-item-title">formily</div><div class="project-item-desc">A powerful form handling library</div><div class="project-item-link"><a href="https://github.com/alibaba/formily" target="_blank">https://github.com/alibaba/formily</a></div></div><h4>Table</h4><div class="project-item"><div class="project-item-title">ag-grid</div><div class="project-item-desc">A top-tier JavaScript data table component for building enterprise-level applications, supporting React, Angular, Vue, and pure JavaScript.</div><div class="project-item-link"><a href="https://github.com/ag-grid/ag-grid" target="_blank">https://github.com/ag-grid/ag-grid</a></div></div><h4>Drag</h4><div class="project-item"><div class="project-item-title">Sortable</div><div class="project-item-desc">Draggable and reordable list for modern browsers and touch devices. No jQuery or framework required.</div><div class="project-item-link"><a href="https://github.com/SortableJS/Sortable" target="_blank">https://github.com/SortableJS/Sortable</a></div></div><div class="project-item"><div class="project-item-title">dnd-kit</div><div class="project-item-desc">Modern, lightweight, high-performance, accessible, and extensible drag and drop toolkit for React.</div><div class="project-item-link"><a href="https://github.com/clauderic/dnd-kit" target="_blank">https://github.com/clauderic/dnd-kit</a></div></div><div class="project-item"><div class="project-item-title">vue-draggable-plus</div><div class="project-item-desc">Universal draggable component supporting Vue 3 and Vue 2.</div><div class="project-item-link"><a href="https://github.com/Alfred-Skyblue/vue-draggable-plus" target="_blank">https://github.com/Alfred-Skyblue/vue-draggable-plus</a></div></div><h4>User Experience</h4><div class="project-item"><div class="project-item-title">react-joyride</div><div class="project-item-desc">Create guided tours in your application</div><div class="project-item-link"><a href="https://github.com/gilbarbara/react-joyride" target="_blank">https://github.com/gilbarbara/react-joyride</a></div></div><div class="project-item"><div class="project-item-title">vue-tour</div><div class="project-item-desc">Create tutorials for Vue applications</div><div class="project-item-link"><a href="https://github.com/pulsardev/vue-tour" target="_blank">https://github.com/pulsardev/vue-tour</a></div></div><div class="project-item"><div class="project-item-title">shepherd</div><div class="project-item-desc">A simple and easy-to-use library for user guidance</div><div class="project-item-link"><a href="https://github.com/shepherd-pro/shepherd" target="_blank">https://github.com/shepherd-pro/shepherd</a></div></div><div class="project-item"><div class="project-item-title">intro.js</div><div class="project-item-desc">A lightweight and user-friendly library for onboarding tours</div><div class="project-item-link"><a href="https://github.com/usablica/intro.js" target="_blank">https://github.com/usablica/intro.js</a></div></div><div class="project-item"><div class="project-item-title">driver.js</div><div class="project-item-desc">A lightweight and dependency-free JavaScript engine to focus user's attention on the whole page</div><div class="project-item-link"><a href="https://github.com/kamranahmedse/driver.js" target="_blank">https://github.com/kamranahmedse/driver.js</a></div></div><div class="project-item"><div class="project-item-title">clipboard.js</div><div class="project-item-desc">A simple and extensible JavaScript library for copying to the clipboard</div><div class="project-item-link"><a href="https://github.com/zenorocha/clipboard.js" target="_blank">https://github.com/zenorocha/clipboard.js</a></div></div><div class="project-item"><div class="project-item-title">copy-to-clipboard</div><div class="project-item-desc">A simple and extensible JavaScript library for copying to the clipboard</div><div class="project-item-link"><a href="https://github.com/sudodoki/copy-to-clipboard" target="_blank">https://github.com/sudodoki/copy-to-clipboard</a></div></div><div class="project-item"><div class="project-item-title">screenfull</div><div class="project-item-desc">A simple wrapper for the cross-browser JavaScript full-screen API</div><div class="project-item-link"><a href="https://github.com/sindresorhus/screenfull" target="_blank">https://github.com/sindresorhus/screenfull</a></div></div><div class="project-item"><div class="project-item-title">nprogress</div><div class="project-item-desc">A simple progress bar for displaying progress at the top of the page.</div><div class="project-item-link"><a href="https://github.com/rstacruz/nprogress" target="_blank">https://github.com/rstacruz/nprogress</a></div></div><h4>Comment Systems</h4><div class="project-item"><div class="project-item-title">valine</div><div class="project-item-desc">A fast, simple, and powerful comment system.</div><div class="project-item-link"><a href="https://github.com/xCss/Valine" target="_blank">https://github.com/xCss/Valine</a></div></div><div class="project-item"><div class="project-item-title">giscus</div><div class="project-item-desc">A comment system powered by GitHub Discussions.</div><div class="project-item-link"><a href="https://github.com/giscus/giscus" target="_blank">https://github.com/giscus/giscus</a></div></div><div class="project-item"><div class="project-item-title">gitalk</div><div class="project-item-desc">Gitalk is a modern comment component based on Github Issue and Preact.</div><div class="project-item-link"><a href="https://github.com/gitalk/gitalk" target="_blank">https://github.com/gitalk/gitalk</a></div></div><div class="project-item"><div class="project-item-title">waline</div><div class="project-item-desc">Simple and secure comment system.</div><div class="project-item-link"><a href="https://github.com/walinejs/waline" target="_blank">https://github.com/walinejs/waline</a></div></div><div class="project-item"><div class="project-item-title">twikoo</div><div class="project-item-desc">A concise, secure, and free static website comment system.</div><div class="project-item-link"><a href="https://github.com/twikoojs/twikoo" target="_blank">https://github.com/twikoojs/twikoo</a></div></div><h4>Compiling & Building & Packaging</h4><div class="project-item"><div class="project-item-title">esno</div><div class="project-item-desc">Node.js runtime enhanced with esbuild for loading TypeScript and ESM.</div><div class="project-item-link"><a href="https://github.com/antfu/esno" target="_blank">https://github.com/antfu/esno</a></div></div><div class="project-item"><div class="project-item-title">vite</div><div class="project-item-desc">A modern frontend build tool focused on development speed and experience.</div><div class="project-item-link"><a href="https://github.com/vitejs/vite" target="_blank">https://github.com/vitejs/vite</a></div></div><div class="project-item"><div class="project-item-title">rollup</div><div class="project-item-desc">The next-generation ES module bundler.</div><div class="project-item-link"><a href="https://github.com/rollup/rollup" target="_blank">https://github.com/rollup/rollup</a></div></div><div class="project-item"><div class="project-item-title">turbo</div><div class="project-item-desc">Incremental packager and build system optimized for JavaScript and TypeScript, written in Rust, including Turbopack and Turborepo.</div><div class="project-item-link"><a href="https://github.com/vercel/turbo" target="_blank">https://github.com/vercel/turbo</a></div></div><div class="project-item"><div class="project-item-title">webpackjs</div><div class="project-item-desc">Modular bundler and build tool for JavaScript applications.</div><div class="project-item-link"><a href="https://www.webpackjs.com/" target="_blank">https://www.webpackjs.com/</a></div></div><div class="project-item"><div class="project-item-title">babeljs</div><div class="project-item-desc">Transpiles ES6 code into browser-understandable code.</div><div class="project-item-link"><a href="https://www.babeljs.cn/" target="_blank">https://www.babeljs.cn/</a></div></div><div class="project-item"><div class="project-item-title">esbuild</div><div class="project-item-desc">A JavaScript build tool and module bundler.</div><div class="project-item-link"><a href="https://esbuild.github.io/" target="_blank">https://esbuild.github.io/</a></div></div><div class="project-item"><div class="project-item-title">swc</div><div class="project-item-desc">SWC is an extensible Rust-based platform for fast developer tools of the next generation.</div><div class="project-item-link"><a href="https://swc.rs/" target="_blank">https://swc.rs/</a></div></div><div class="project-item"><div class="project-item-title">tsup</div><div class="project-item-desc">The simplest and fastest way to bundle TypeScript libraries.</div><div class="project-item-link"><a href="https://github.com/egoist/tsup" target="_blank">https://github.com/egoist/tsup</a></div></div><div class="project-item"><div class="project-item-title">unbuild</div><div class="project-item-desc">Unified javascript build system.</div><div class="project-item-link"><a href="https://github.com/unjs/unbuild" target="_blank">https://github.com/unjs/unbuild</a></div></div><div class="project-item"><div class="project-item-title">unplugin</div><div class="project-item-desc">Unified plugin system for Vite, Rollup, Webpack, esbuild, rolldown, and more.</div><div class="project-item-link"><a href="https://github.com/unjs/unplugin" target="_blank">https://github.com/unjs/unplugin</a></div></div>]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Build Your First MCP Server in TypeScript in 10 Minutes]]></title>
            <link>https://ryanuo.cc/posts/mcp</link>
            <guid>https://ryanuo.cc/posts/mcp</guid>
            <pubDate>Sat, 12 Jul 2025 10:19:00 GMT</pubDate>
            <description><![CDATA[Quickly get started with the Model Context Protocol (MCP) by building your first MCP server in TypeScript, allowing AI hosts like Claude or Cursor Desktop to directly call your tools.]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>What is MCP?</h2>
<p><em>MCP (Model Context Protocol) is a protocol that allows AI agents to easily connect with various tools.</em><br>
It consists of three core components:</p>
<h3>1. MCP Servers</h3>
<p>They act as a bridge to connect APIs, databases, or code, exposing them as tools. MCP servers can be built using Python or TypeScript SDKs.</p>
<h3>2. MCP Clients</h3>
<p>Clients use the MCP protocol to communicate with MCP servers. They can also be built using Python or TypeScript SDKs.</p>
<h3>3. MCP Hosts</h3>
<p>These hosts manage data exchange between servers and clients. Popular MCP hosts include Claude Desktop, Cursor Desktop, Zed, and Sourcegraph Cody.</p>
<h3>Role of MCP Servers</h3>
<p>MCP servers provide tools that any MCP host can access. This allows developers to quickly connect AI agents to new tools without writing integration code for each one.</p>
<h2>How to Build an MCP Server</h2>
<p>Below is a complete example of building an MCP server using the TypeScript SDK, with Claude or Cursor Desktop as the test host.</p>
<h3>Step 1: Install Dependencies</h3>
<p>Create a new project and initialize npm. Then install the dependencies for the MCP server and configure <code>package.json</code> and <code>tsconfig.json</code>.</p>
<p><strong>Create project directory:</strong></p>
<pre><code class="language-bash">mkdir mcp-server
cd mcp-server
</code></pre>
<p><strong>Create <code>package.json</code>:</strong></p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;mcp-server&quot;,
  &quot;version&quot;: &quot;0.1.0&quot;,
  &quot;description&quot;: &quot;Model Context Protocol server example&quot;,
  &quot;private&quot;: true,
  &quot;type&quot;: &quot;module&quot;,
  &quot;bin&quot;: {
    &quot;mcp-server&quot;: &quot;./build/index.js&quot;
  },
  &quot;files&quot;: [
    &quot;build&quot;
  ],
  &quot;scripts&quot;: {
    &quot;build&quot;: &quot;tsc &amp;&amp; node -e \&quot;require('fs').chmodSync('build/index.js', '755')\&quot;&quot;,
    &quot;prepare&quot;: &quot;npm run build&quot;,
    &quot;watch&quot;: &quot;tsc --watch&quot;,
    &quot;inspector&quot;: &quot;npx @modelcontextprotocol/inspector build/index.js&quot;
  },
  &quot;dependencies&quot;: {
    &quot;@modelcontextprotocol/sdk&quot;: &quot;0.6.0&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@types/node&quot;: &quot;^20.11.24&quot;,
    &quot;typescript&quot;: &quot;^5.3.3&quot;
  }
}
</code></pre>
<p><strong>Create <code>tsconfig.json</code>:</strong></p>
<pre><code class="language-json">{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;ES2022&quot;,
    &quot;module&quot;: &quot;Node16&quot;,
    &quot;moduleResolution&quot;: &quot;Node16&quot;,
    &quot;outDir&quot;: &quot;./build&quot;,
    &quot;rootDir&quot;: &quot;./src&quot;,
    &quot;strict&quot;: true,
    &quot;esModuleInterop&quot;: true,
    &quot;skipLibCheck&quot;: true,
    &quot;forceConsistentCasingInFileNames&quot;: true
  },
  &quot;include&quot;: [&quot;src/**/*&quot;],
  &quot;exclude&quot;: [&quot;node_modules&quot;]
}
</code></pre>
<p>Then run:</p>
<pre><code class="language-bash">npm install
</code></pre>
<h3>Step 2: Write Basic Code</h3>
<p>Create <code>src/index.ts</code> and add the following:</p>
<pre><code class="language-ts">import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import {
  CallToolRequestSchema,
  ErrorCode,
  ListToolsRequestSchema,
  McpError,
} from '@modelcontextprotocol/sdk/types.js'

const server = new Server(
  {
    name: 'mcp-server',
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {}
    }
  }
)

server.setRequestHandler(ListToolsRequestSchema, async () =&gt; {
  return { tools: [] }
})

server.setRequestHandler(CallToolRequestSchema, async (request) =&gt; {
  if (request.params.name === 'name_of_tool') {
    return {}
  }
  throw new McpError(ErrorCode.ToolNotFound, 'Tool not found')
})

const transport = new StdioServerTransport()
await server.connect(transport)
</code></pre>
<h3>Step 3: Define and Add MCP Tools</h3>
<p>Here we define a simple tool, e.g., <strong>calculating the sum of two numbers</strong>:</p>
<pre><code class="language-ts">server.setRequestHandler(ListToolsRequestSchema, async () =&gt; {
  return {
    tools: [
      {
        name: 'calculate_sum',
        description: 'Calculates the sum of two numbers',
        inputSchema: {
          type: 'object',
          properties: {
            a: { type: 'number' },
            b: { type: 'number' }
          },
          required: ['a', 'b']
        }
      }
    ]
  }
})

server.setRequestHandler(CallToolRequestSchema, async (request) =&gt; {
  if (request.params.name === 'calculate_sum') {
    const { a, b } = request.params.arguments
    return { toolResult: a + b }
  }
  throw new McpError(ErrorCode.ToolNotFound, 'Tool not found')
})
</code></pre>
<h3>Step 4: Integrate MCP Server into Claude or Cursor Desktop</h3>
<p>Register the MCP server in <code>claude_desktop_config.json</code> or <code>cursor_desktop_config.json</code>:</p>
<pre><code class="language-json">{
  &quot;mcpServers&quot;: {
    &quot;mcp-server&quot;: {
      &quot;command&quot;: &quot;node&quot;,
      &quot;args&quot;: [
        &quot;/Users/YOUR_USER/mcp-server/build/index.js&quot;
      ]
    }
  }
}
</code></pre>
<p>Restart Claude or Cursor Desktop, and you should see <code>calculate_sum</code> in the tool list!</p>
<h3>Step 5: Call External REST API as an MCP Tool</h3>
<p>You can also create tools that fetch external APIs:</p>
<pre><code class="language-ts">server.setRequestHandler(CallToolRequestSchema, async (request) =&gt; {
  if (request.params.name === 'httpbin_json') {
    try {
      const response = await fetch('https://httpbin.org/json', {
        method: 'GET',
        headers: { accept: 'application/json' }
      })
      if (!response.ok) {
        throw new Error(`HTTP error! Status: ${response.status}`)
      }
      const data = await response.json()
      return { toolResult: data }
    }
    catch (e) {
      throw new Error('Request failed')
    }
  }
})
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[用 TypeScript 在 10 分钟内构建你的第一个 MCP 服务器]]></title>
            <link>https://ryanuo.cc/zh/posts/mcp</link>
            <guid>https://ryanuo.cc/zh/posts/mcp</guid>
            <pubDate>Sat, 12 Jul 2025 10:19:00 GMT</pubDate>
            <description><![CDATA[快速上手 Model Context Protocol (MCP)，使用 TypeScript 构建你的第一个 MCP 服务器，让 Claude 等 AI 主机直接调用你的工具。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>什么是 MCP？</h2>
<p><em>MCP（模型上下文协议，Model Context Protocol） 是一个让智能体轻松连接各种工具的协议</em>。<br>
它主要由三个核心组件组成：</p>
<h3>1. MCP 服务器（MCP Servers）</h3>
<p>充当桥梁，用于连接 API、数据库或代码，并将这些数据源公开为工具。可使用 Python 或 TypeScript SDK 构建。</p>
<h3>2. MCP 客户端（MCP Clients）</h3>
<p>这些客户端使用 MCP 协议与 MCP 服务器通信，也可以用 Python 或 TypeScript SDK 进行开发。</p>
<h3>3. MCP 主机（MCP Hosts）</h3>
<p>在服务器和客户端之间进行数据交换，确保通信顺畅。目前流行的 MCP 主机包括 Claude Desktop、Zed 和 Sourcegraph Cody。</p>
<h3>MCP 服务器的作用</h3>
<p>MCP 服务器能够提供各种工具，任何 MCP 主机都可以访问它们。这意味着开发者可以快速将智能体连接到新的工具，而无需为每种工具都写一遍集成代码。</p>
<h2>如何搭建 MCP 服务器？</h2>
<p>下面是一个使用 TypeScript SDK 来构建 MCP 服务器，并用 Claude Desktop 作为测试主机的完整示例。</p>
<h3>步骤 1：安装依赖</h3>
<p>首先，创建一个新项目，并初始化 <code>npm</code> 包。然后安装 MCP 服务器所需的依赖项，并配置 <code>package.json</code> 和 <code>tsconfig.json</code>。</p>
<p><strong>创建项目目录：</strong></p>
<pre><code class="language-bash">mkdir mcp-server
cd mcp-server
</code></pre>
<p><strong>创建 <code>package.json</code>：</strong></p>
<pre><code class="language-json">{
  &quot;name&quot;: &quot;mcp-server&quot;,
  &quot;version&quot;: &quot;0.1.0&quot;,
  &quot;description&quot;: &quot;Model Context Protocol 服务器示例&quot;,
  &quot;private&quot;: true,
  &quot;type&quot;: &quot;module&quot;,
  &quot;bin&quot;: {
    &quot;mcp-server&quot;: &quot;./build/index.js&quot;
  },
  &quot;files&quot;: [
    &quot;build&quot;
  ],
  &quot;scripts&quot;: {
    &quot;build&quot;: &quot;tsc &amp;&amp; node -e \&quot;require('fs').chmodSync('build/index.js', '755')\&quot;&quot;,
    &quot;prepare&quot;: &quot;npm run build&quot;,
    &quot;watch&quot;: &quot;tsc --watch&quot;,
    &quot;inspector&quot;: &quot;npx @modelcontextprotocol/inspector build/index.js&quot;
  },
  &quot;dependencies&quot;: {
    &quot;@modelcontextprotocol/sdk&quot;: &quot;0.6.0&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@types/node&quot;: &quot;^20.11.24&quot;,
    &quot;typescript&quot;: &quot;^5.3.3&quot;
  }
}
</code></pre>
<p><strong>创建 <code>tsconfig.json</code>：</strong></p>
<pre><code class="language-json">{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;ES2022&quot;,
    &quot;module&quot;: &quot;Node16&quot;,
    &quot;moduleResolution&quot;: &quot;Node16&quot;,
    &quot;outDir&quot;: &quot;./build&quot;,
    &quot;rootDir&quot;: &quot;./src&quot;,
    &quot;strict&quot;: true,
    &quot;esModuleInterop&quot;: true,
    &quot;skipLibCheck&quot;: true,
    &quot;forceConsistentCasingInFileNames&quot;: true
  },
  &quot;include&quot;: [&quot;src/**/*&quot;],
  &quot;exclude&quot;: [&quot;node_modules&quot;]
}
</code></pre>
<p>然后执行：</p>
<pre><code class="language-bash">npm install
</code></pre>
<h3>步骤 2：编写基础代码</h3>
<p>在 <code>src</code> 目录下创建 <code>index.ts</code>，并添加以下内容：</p>
<pre><code class="language-ts">import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import {
  CallToolRequestSchema,
  ErrorCode,
  ListToolsRequestSchema,
  McpError,
} from '@modelcontextprotocol/sdk/types.js'

const server = new Server(
  {
    name: 'mcp-server',
    version: '1.0.0',
  },
  {
    capabilities: {
      tools: {}
    }
  }
)

server.setRequestHandler(ListToolsRequestSchema, async () =&gt; {
  return { tools: [] }
})

server.setRequestHandler(CallToolRequestSchema, async (request) =&gt; {
  if (request.params.name === 'name_of_tool') {
    return {}
  }
  throw new McpError(ErrorCode.ToolNotFound, '工具未找到')
})

const transport = new StdioServerTransport()
await server.connect(transport)
</code></pre>
<h3>步骤 3：定义并添加 MCP 工具</h3>
<p>接下来我们定义一个简单工具，比如 “计算两个数的和”：</p>
<pre><code class="language-ts">server.setRequestHandler(ListToolsRequestSchema, async () =&gt; {
  return {
    tools: [
      {
        name: 'calculate_sum',
        description: '计算两个数的总和',
        inputSchema: {
          type: 'object',
          properties: {
            a: { type: 'number' },
            b: { type: 'number' }
          },
          required: ['a', 'b']
        }
      }
    ]
  }
})

server.setRequestHandler(CallToolRequestSchema, async (request) =&gt; {
  if (request.params.name === 'calculate_sum') {
    const { a, b } = request.params.arguments
    return { toolResult: a + b }
  }
  throw new McpError(ErrorCode.ToolNotFound, '工具未找到')
})
</code></pre>
<h3>步骤 4：将 MCP 服务器集成到 Claude/Cursor Desktop</h3>
<p>在 <code>claude_desktop_config.json</code> 中注册 MCP 服务器：</p>
<pre><code class="language-json">{
  &quot;mcpServers&quot;: {
    &quot;mcp-server&quot;: {
      &quot;command&quot;: &quot;node&quot;,
      &quot;args&quot;: [
        &quot;/Users/YOUR_USER/mcp-server/build/index.js&quot;
      ]
    }
  }
}
</code></pre>
<p>重启 Claude/Cursor，你应该能在工具列表里看到 <code>calculate_sum</code>！</p>
<h3>步骤 5：调用 REST API 作为 MCP 工具</h3>
<p>你也可以让 MCP 工具帮你调用外部 REST API，例如：</p>
<pre><code class="language-ts">server.setRequestHandler(CallToolRequestSchema, async (request) =&gt; {
  if (request.params.name === 'httpbin_json') {
    try {
      const response = await fetch('https://httpbin.org/json', {
        method: 'GET',
        headers: {
          accept: 'application/json'
        }
      })
      if (!response.ok) {
        throw new Error(`HTTP 错误！状态码：${response.status}`)
      }
      const data = await response.json()
      return { toolResult: data }
    }
    catch (e) {
      throw new Error('请求失败')
    }
  }
})
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Comprehensive Guide to GPT Interaction and Application Platform]]></title>
            <link>https://ryanuo.cc/posts/gpt</link>
            <guid>https://ryanuo.cc/posts/gpt</guid>
            <pubDate>Wed, 02 Apr 2025 15:00:00 GMT</pubDate>
            <description><![CDATA[A Flask-based web application providing APIs and a simple frontend for interacting with GPT models, including features like chat, image generation, and content summarization.]]></description>
            <content:encoded><![CDATA[<ul>
<li><a href="https://github.com/ryanuo/gpt">repo</a></li>
</ul>
<h2>Project Overview</h2>
<p>This project is a Flask-based web application that provides API interfaces and a simple frontend for interacting with GPT models. Users can interact with AI through a WeChat official account or a web interface to chat, generate images, or obtain content summaries.</p>
<p>Key features include:</p>
<ul>
<li><strong>Text-based interaction with GPT models</strong>: Supports multi-turn conversations, simulating human-like chats.</li>
<li><strong>Image generation based on user input</strong>: Generates high-quality images using AI models.</li>
<li><strong>WeChat official account message handling</strong>: Supports auto-replies, image generation, and other features.</li>
<li><strong>Content summarization</strong>: Extracts key information from long texts or web pages to generate concise summaries.</li>
</ul>
<p>This project is suitable for individual developers, enterprises, or researchers to quickly build applications based on GPT models.</p>
<h2>Usage Instructions</h2>
<h3>Environment Setup</h3>
<ol>
<li>
<p>Clone the repository:</p>
<pre><code class="language-bash">git clone &lt;repository-url&gt;
cd gpt
</code></pre>
</li>
<li>
<p>Install dependencies:</p>
<pre><code class="language-bash">pip install -r requirements.txt
</code></pre>
</li>
<li>
<p>Configure environment variables:</p>
<ul>
<li>Create a <code>.env</code> file and set the following variables:
<ul>
<li><code>WX_TOKEN</code>: Token for the WeChat official account, used for message verification.</li>
<li><code>OPENAI_API_KEY</code>: API key for the GPT model (if required).</li>
</ul>
</li>
<li>Ensure other necessary environment variables are correctly configured, such as database connection information (if applicable).</li>
</ul>
</li>
<li>
<p>Start the service:</p>
<pre><code class="language-bash">python -m api.index
</code></pre>
</li>
<li>
<p>Deploy to Vercel:</p>
<ul>
<li>Ensure the <code>vercel.json</code> configuration is correct, including routes and environment variables.</li>
<li>Deploy using the Vercel CLI:<pre><code class="language-bash">vercel
</code></pre>
</li>
</ul>
</li>
</ol>
<h3>API Usage</h3>
<ul>
<li>
<p><strong>Chat Interface</strong>:</p>
<ul>
<li>Path: <code>/g4f/&lt;model&gt;</code></li>
<li>Method: POST</li>
<li>Parameters:
<ul>
<li><code>message</code>: User input text.</li>
<li><code>context</code> (optional): Conversation context.</li>
</ul>
</li>
<li>Response: Reply generated by the GPT model.</li>
</ul>
</li>
<li>
<p><strong>Image Generation</strong>:</p>
<ul>
<li>Path: <code>/generate-image</code></li>
<li>Method: POST</li>
<li>Parameters:
<ul>
<li><code>prompt</code>: Text describing the content of the image.</li>
</ul>
</li>
<li>Response: URL of the generated image.</li>
</ul>
</li>
<li>
<p><strong>WeChat Official Account</strong>:</p>
<ul>
<li>Path: <code>/wechat</code></li>
<li>Method: POST</li>
<li>Functionality: Handles messages from the WeChat official account, including text replies and image generation.</li>
</ul>
</li>
<li>
<p><strong>Content Summarization</strong>:</p>
<ul>
<li>Path: <code>/ai-post</code></li>
<li>Method: POST</li>
<li>Parameters:
<ul>
<li><code>url</code>: URL of the webpage to summarize.</li>
</ul>
</li>
<li>Response: A concise summary of the webpage content.</li>
</ul>
</li>
</ul>
<h3>Project Structure</h3>
<ul>
<li><code>api/</code>: Backend API code.</li>
<li><code>static/</code>: Frontend static files (HTML, CSS, JS).</li>
<li><code>templates/</code>: Frontend template files.</li>
<li><code>requirements.txt</code>: Python dependency list.</li>
<li><code>vercel.json</code>: Vercel deployment configuration.</li>
</ul>
<h2>Core Principles</h2>
<ol>
<li>
<p><strong>GPT Model Interaction</strong>:</p>
<ul>
<li>Uses the <code>g4f</code> client to interact with GPT models, supporting various models (e.g., <code>gpt-4o-mini</code>).</li>
<li>Provides flexible interfaces, supporting custom contexts and model selection.</li>
</ul>
</li>
<li>
<p><strong>WeChat Official Account Integration</strong>:</p>
<ul>
<li>Uses the <code>wechatpy</code> library to handle message signature verification, message parsing, and replies.</li>
<li>Supports text messages, image generation requests, and other custom features.</li>
</ul>
</li>
<li>
<p><strong>Image Generation</strong>:</p>
<ul>
<li>Calls the image generation interface of the <code>g4f</code> client to generate images based on user input prompts.</li>
<li>Returns the URL of the image for user download or preview.</li>
</ul>
</li>
<li>
<p><strong>Content Summarization</strong>:</p>
<ul>
<li>Retrieves webpage content from the specified URL and generates a concise summary using the GPT model.</li>
<li>Supports multilingual content processing.</li>
</ul>
</li>
</ol>
<h2>Frequently Asked Questions</h2>
<ol>
<li>
<p><strong>How to switch GPT models?</strong></p>
<ul>
<li>Call the <code>/models</code> interface to get a list of supported models.</li>
<li>Specify the desired model name in the request.</li>
</ul>
</li>
<li>
<p><strong>How to debug WeChat official account features?</strong></p>
<ul>
<li>Use the WeChat official platform's developer tools for testing.</li>
<li>Check if the <code>WX_TOKEN</code> is correctly configured.</li>
</ul>
</li>
<li>
<p><strong>What to do if image generation fails?</strong></p>
<ul>
<li>Ensure the <code>g4f</code> client is working correctly.</li>
<li>Check if the input prompt meets the requirements.</li>
</ul>
</li>
</ol>
<h2>References</h2>
<ul>
<li><a href="https://flask.palletsprojects.com/">Flask Official Documentation</a></li>
<li><a href="https://wechatpy.readthedocs.io/">wechatpy Documentation</a></li>
<li><a href="https://vercel.com/docs">Vercel Official Documentation</a></li>
<li><a href="https://github.com/xtekky/gpt4free">OpenAI GPT Models</a></li>
<li><a href="https://docs.python.org/">Python Official Documentation</a></li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[GPT交互和应用平台综合指南]]></title>
            <link>https://ryanuo.cc/zh/posts/gpt</link>
            <guid>https://ryanuo.cc/zh/posts/gpt</guid>
            <pubDate>Wed, 02 Apr 2025 15:00:00 GMT</pubDate>
            <description><![CDATA[基于Flask的web应用程序，提供api和用于与GPT模型交互的简单前端，包括聊天、图像生成和内容汇总等功能]]></description>
            <content:encoded><![CDATA[<ul>
<li><a href="https://github.com/ryanuo/gpt">repo</a></li>
</ul>
<h2>项目简介</h2>
<p>本项目是一个基于 Flask 的 Web 应用，提供了与 GPT 模型交互的 API 接口和简单的前端页面。用户可以通过微信公众号或 Web 界面与 AI 进行对话、生成图片或获取内容摘要。</p>
<p>主要功能包括：</p>
<ul>
<li><strong>与 GPT 模型进行文本对话</strong>：支持多轮对话，模拟人类聊天。</li>
<li><strong>根据用户输入生成图片</strong>：通过 AI 模型生成高质量图片。</li>
<li><strong>微信公众号的消息处理功能</strong>：支持自动回复、图片生成等功能。</li>
<li><strong>内容摘要功能</strong>：从长文本或网页中提取关键信息，生成简洁摘要。</li>
</ul>
<p>本项目适用于个人开发者、企业或研究人员，帮助快速构建基于 GPT 模型的应用。</p>
<h2>使用说明</h2>
<h3>环境准备</h3>
<ol>
<li>
<p>克隆仓库：</p>
<pre><code class="language-bash">git clone &lt;仓库地址&gt;
cd gpt
</code></pre>
</li>
<li>
<p>安装依赖：</p>
<pre><code class="language-bash">pip install -r requirements.txt
</code></pre>
</li>
<li>
<p>配置环境变量：</p>
<ul>
<li>创建 <code>.env</code> 文件，设置以下变量：
<ul>
<li><code>WX_TOKEN</code>：微信公众号的 token，用于消息验证。</li>
<li><code>OPENAI_API_KEY</code>：GPT 模型的 API 密钥（如果需要）。</li>
</ul>
</li>
<li>确保其他必要的环境变量已正确配置，例如数据库连接信息（如适用）。</li>
</ul>
</li>
<li>
<p>启动服务：</p>
<pre><code class="language-bash">python -m api.index
</code></pre>
</li>
<li>
<p>部署到 Vercel：</p>
<ul>
<li>确保 <code>vercel.json</code> 配置正确，包含路由和环境变量。</li>
<li>使用 Vercel CLI 部署：<pre><code class="language-bash">vercel
</code></pre>
</li>
</ul>
</li>
</ol>
<h3>API 使用</h3>
<ul>
<li>
<p><strong>对话接口</strong>：</p>
<ul>
<li>路径：<code>/g4f/&lt;model&gt;</code></li>
<li>方法：POST</li>
<li>参数：
<ul>
<li><code>message</code>：用户输入的文本。</li>
<li><code>context</code>（可选）：对话上下文。</li>
</ul>
</li>
<li>返回：GPT 模型生成的回复。</li>
</ul>
</li>
<li>
<p><strong>图片生成</strong>：</p>
<ul>
<li>路径：<code>/generate-image</code></li>
<li>方法：POST</li>
<li>参数：
<ul>
<li><code>prompt</code>：描述图片内容的文本。</li>
</ul>
</li>
<li>返回：生成图片的 URL。</li>
</ul>
</li>
<li>
<p><strong>微信公众号</strong>：</p>
<ul>
<li>路径：<code>/wechat</code></li>
<li>方法：POST</li>
<li>功能：处理微信公众号的消息，包括文本回复和图片生成。</li>
</ul>
</li>
<li>
<p><strong>内容摘要</strong>：</p>
<ul>
<li>路径：<code>/ai-post</code></li>
<li>方法：POST</li>
<li>参数：
<ul>
<li><code>url</code>：需要摘要的网页 URL。</li>
</ul>
</li>
<li>返回：网页内容的简短摘要。</li>
</ul>
</li>
</ul>
<h3>项目结构</h3>
<ul>
<li><code>api/</code>：后端 API 代码。</li>
<li><code>static/</code>：前端静态文件（HTML、CSS、JS）。</li>
<li><code>templates/</code>：前端模板文件。</li>
<li><code>requirements.txt</code>：Python 依赖列表。</li>
<li><code>vercel.json</code>：Vercel 部署配置。</li>
</ul>
<h2>核心原理</h2>
<ol>
<li>
<p><strong>GPT 模型交互</strong>：</p>
<ul>
<li>使用 <code>g4f</code> 客户端与 GPT 模型交互，支持多种模型（如 <code>gpt-4o-mini</code>）。</li>
<li>提供灵活的接口，支持自定义上下文和模型选择。</li>
</ul>
</li>
<li>
<p><strong>微信公众号集成</strong>：</p>
<ul>
<li>使用 <code>wechatpy</code> 库处理消息签名验证、消息解析和回复。</li>
<li>支持文本消息、图片生成请求和其他自定义功能。</li>
</ul>
</li>
<li>
<p><strong>图片生成</strong>：</p>
<ul>
<li>调用 <code>g4f</code> 客户端的图片生成接口，基于用户输入的 prompt 生成图片。</li>
<li>返回图片的 URL，供用户下载或预览。</li>
</ul>
</li>
<li>
<p><strong>内容摘要</strong>：</p>
<ul>
<li>从指定 URL 获取网页内容，使用 GPT 模型生成简短摘要。</li>
<li>支持多语言内容处理。</li>
</ul>
</li>
</ol>
<h2>常见问题</h2>
<ol>
<li>
<p><strong>如何更换 GPT 模型？</strong></p>
<ul>
<li>调用 <code>/models</code> 接口获取支持的模型列表。</li>
<li>在请求中指定所需的模型名称。</li>
</ul>
</li>
<li>
<p><strong>如何调试微信公众号功能？</strong></p>
<ul>
<li>使用微信公众平台的开发者工具进行测试。</li>
<li>检查 <code>WX_TOKEN</code> 是否正确配置。</li>
</ul>
</li>
<li>
<p><strong>图片生成失败怎么办？</strong></p>
<ul>
<li>确保 <code>g4f</code> 客户端正常工作。</li>
<li>检查输入的 prompt 是否符合要求。</li>
</ul>
</li>
</ol>
<h2>参考</h2>
<ul>
<li><a href="https://flask.palletsprojects.com/">Flask 官方文档</a></li>
<li><a href="https://wechatpy.readthedocs.io/">wechatpy 文档</a></li>
<li><a href="https://vercel.com/docs">Vercel 官方文档</a></li>
<li><a href="https://github.com/xtekky/gpt4free">OpenAI GPT 模型</a></li>
<li><a href="https://docs.python.org/">Python 官方文档</a></li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[A Complete Guide to Creating a Custom Email with ForwardEmail]]></title>
            <link>https://ryanuo.cc/posts/forwardemail</link>
            <guid>https://ryanuo.cc/posts/forwardemail</guid>
            <pubDate>Tue, 01 Apr 2025 09:09:09 GMT</pubDate>
            <description><![CDATA[Learn how to quickly set up and use your own custom email address with the ForwardEmail service.]]></description>
            <content:encoded><![CDATA[<p>If you want to create your own email address and use the ForwardEmail service for forwarding, here are the detailed steps:</p>
<h2>What is ForwardEmail?</h2>
<p><a href="https://forwardemail.net/">ForwardEmail</a> is an open-source email forwarding service that allows you to forward emails from a custom domain to an existing email address without hosting your own mail server.</p>
<h2>Prerequisites</h2>
<ol>
<li>A custom domain (e.g., <code>example.com</code>).</li>
<li>An existing email address (e.g., <code>yourname@gmail.com</code>).</li>
<li>Access to a ForwardEmail account.</li>
</ol>
<h2>Configuration Steps</h2>
<h3>1. Sign Up and Log In to ForwardEmail</h3>
<p>Visit <a href="https://forwardemail.net/">ForwardEmail</a>, sign up for an account, and log in.</p>
<h3>2. Add Your Domain</h3>
<p>In the ForwardEmail dashboard, click the <strong>Add Domain</strong> button and enter your domain (e.g., <code>example.com</code>).</p>
<h3>3. Configure DNS Records</h3>
<p>Follow the instructions provided by ForwardEmail to add the following DNS records in your domain registrar's control panel:</p>
<ul>
<li><strong>MX Records</strong>: Set to <code>mx1.forwardemail.net</code> and <code>mx2.forwardemail.net</code> with priorities <code>10</code> and <code>20</code>, respectively.</li>
<li><strong>TXT Record</strong>: Used to verify domain ownership, with the value provided by ForwardEmail.</li>
</ul>
<p>Save the changes and wait for the DNS records to propagate (this usually takes a few minutes to a few hours).</p>
<h3>4. Set Up Forwarding Rules</h3>
<p>In the ForwardEmail dashboard, set up forwarding rules for your domain. For example:</p>
<ul>
<li>Forward <code>hello@example.com</code> to <code>yourname@gmail.com</code>.</li>
<li>Forward <code>info@example.com</code> to <code>anotheremail@gmail.com</code>.</li>
</ul>
<h3>5. Test Your Email</h3>
<p>Send a test email to your custom email address (e.g., <code>hello@example.com</code>) and confirm that the email is successfully forwarded to the target address.</p>
<h2>Advantages</h2>
<ul>
<li><strong>Free</strong>: ForwardEmail offers a free plan.</li>
<li><strong>Privacy Protection</strong>: No need to host your own mail server.</li>
<li><strong>Easy to Use</strong>: Simply configure DNS records.</li>
</ul>
<p>By following these steps, you can easily create and use your own email address!</p>
<h2>Reference</h2>
<ul>
<li><a href="https://antfu.me/posts/domain-email">domain-email</a></li>
<li><a href="https://www.v2ex.com/t/889932">v2ex question</a></li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[如何使自己的电子邮件使用forwardemail]]></title>
            <link>https://ryanuo.cc/zh/posts/forwardemail</link>
            <guid>https://ryanuo.cc/zh/posts/forwardemail</guid>
            <pubDate>Tue, 01 Apr 2025 09:09:09 GMT</pubDate>
            <description><![CDATA[如何使自己的电子邮件使用forwardemail]]></description>
            <content:encoded><![CDATA[<p>如果你想创建自己的电子邮件地址并使用 ForwardEmail 服务进行转发，以下是详细步骤：</p>
<h2>什么是 ForwardEmail？</h2>
<p><a href="https://forwardemail.net/">ForwardEmail</a> 是一个开源的电子邮件转发服务，允许你将自定义域名的电子邮件转发到现有的电子邮件地址，而无需托管自己的邮件服务器。</p>
<h2>准备工作</h2>
<ol>
<li>一个自定义域名（例如 <code>example.com</code>）。</li>
<li>一个现有的电子邮件地址（例如 <code>yourname@gmail.com</code>）。</li>
<li>访问 ForwardEmail 的账户。</li>
</ol>
<h2>配置步骤</h2>
<h3>1. 注册并登录 ForwardEmail</h3>
<p>访问 <a href="https://forwardemail.net/">ForwardEmail</a> 网站，注册一个账户并登录。</p>
<h3>2. 添加你的域名</h3>
<p>在 ForwardEmail 的仪表板中，点击 <strong>Add Domain</strong> 按钮，输入你的域名（例如 <code>example.com</code>）。</p>
<h3>3. 配置 DNS 记录</h3>
<p>根据 ForwardEmail 提供的说明，在你的域名注册商的控制面板中添加以下 DNS 记录：</p>
<ul>
<li><strong>MX 记录</strong>：设置为 <code>mx1.forwardemail.net</code> 和 <code>mx2.forwardemail.net</code>，优先级分别为 <code>10</code> 和 <code>20</code>。</li>
<li><strong>TXT 记录</strong>：用于验证域名所有权，内容为 ForwardEmail 提供的值。</li>
</ul>
<p>保存更改后，等待 DNS 记录生效（通常需要几分钟到几小时）。</p>
<h3>4. 设置转发规则</h3>
<p>在 ForwardEmail 的仪表板中，为你的域名设置转发规则。例如：</p>
<ul>
<li>将 <code>hello@example.com</code> 转发到 <code>yourname@gmail.com</code>。</li>
<li>将 <code>info@example.com</code> 转发到 <code>anotheremail@gmail.com</code>。</li>
</ul>
<h3>5. 测试电子邮件</h3>
<p>发送一封测试邮件到你的自定义邮箱地址（例如 <code>hello@example.com</code>），确认邮件是否成功转发到目标邮箱。</p>
<h2>优势</h2>
<ul>
<li><strong>免费</strong>：ForwardEmail 提供免费计划。</li>
<li><strong>隐私保护</strong>：无需托管自己的邮件服务器。</li>
<li><strong>简单易用</strong>：只需配置 DNS 记录即可。</li>
</ul>
<p>通过以上步骤，你就可以轻松创建并使用自己的电子邮件地址了！</p>
<h2>参考文章</h2>
<ul>
<li><a href="https://antfu.me/posts/domain-email">domain-email</a></li>
<li><a href="https://www.v2ex.com/t/889932">v2ex question</a></li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Template CLI: A Powerful Tool for Managing Project Templates]]></title>
            <link>https://ryanuo.cc/posts/tmpl-cli</link>
            <guid>https://ryanuo.cc/posts/tmpl-cli</guid>
            <pubDate>Mon, 10 Mar 2025 20:00:00 GMT</pubDate>
            <description><![CDATA[A powerful tool for efficiently managing project templates, supporting quick cloning and flexible configuration from Git repositories.]]></description>
            <content:encoded><![CDATA[<h1>Template CLI 🚀</h1>
<p>A convenient project template management tool for quickly cloning templates from Git repositories.</p>
<h2>Features</h2>
<ul>
<li>✅ Interactive template selection</li>
<li>✅ Flexible configuration via command-line arguments</li>
<li>✅ Intelligent cache configuration management</li>
<li>✅ Support for branch specification and directory renaming</li>
<li>⚡ Native JSON project selection mode</li>
</ul>
<h2>Installation</h2>
<h3>Install via Cargo</h3>
<pre><code class="language-bash">cargo install --path .
</code></pre>
<h3>Manual Build</h3>
<pre><code class="language-bash">git clone https://github.com/your-repo/project-template.git
cd project-template
cargo build --release
</code></pre>
<h2>Usage Guide</h2>
<h3>Basic Command Structure</h3>
<pre><code class="language-bash">template-cli [options]
</code></pre>
<h3>Quick Start Examples</h3>
<pre><code class="language-bash"># Clone a template project (interactive selection)
template-cli https://github.com/my-repo/templates

# Clone with specific parameters
template-cli -r https://github.com/my-repo -b dev -d ./new-project -t my_template

# View cache configuration
template-cli -x

# Use native project selection mode
template-cli --original https://github.com/my-repo.json
</code></pre>
<h2>Parameter Description</h2>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Description</th>
<th>Default Value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-r, --repo</code></td>
<td>Git repository URL to clone</td>
<td>-</td>
</tr>
<tr>
<td><code>-b, --branch</code></td>
<td>Git branch name</td>
<td>main</td>
</tr>
<tr>
<td><code>-d, --target-dir</code></td>
<td>Target directory for the template</td>
<td>Current dir</td>
</tr>
<tr>
<td><code>-t, template</code></td>
<td>Specific template to use</td>
<td>Interactive</td>
</tr>
<tr>
<td><code>-o, --original</code></td>
<td>Use native JSON configuration</td>
<td>-</td>
</tr>
<tr>
<td><code>-c, --clear-cache</code></td>
<td>Clear configuration cache</td>
<td>-</td>
</tr>
<tr>
<td><code>-x, --check-cache</code></td>
<td>View saved configuration cache</td>
<td>-</td>
</tr>
</tbody>
</table>
<h3>Advanced Parameters</h3>
<ul>
<li>
<p><strong>Intelligent Cache</strong>:<br>
Automatically saves recently used repositories, branches, etc., and prioritizes cache on reuse.</p>
<ul>
<li>View cache: <code>template-cli -x</code></li>
<li>Clear cache: <code>template-cli -c</code></li>
</ul>
</li>
<li>
<p><strong>Native Project Selection Mode</strong>:<br>
Use the <code>--original</code> parameter to specify a JSON configuration file URL, which displays a selection interface like this:</p>
<pre><code class="language-bash">Category:
1. Frontend Projects
2. Backend Projects

Select a category (default: Frontend Projects):
</code></pre>
</li>
</ul>
<h2>Workflow</h2>
<ol>
<li>User inputs parameters or selects interactive mode.</li>
<li>Determine repository information based on parameters/cache.</li>
<li>Clone the repository from the specified branch to a temporary directory.</li>
<li>Display available templates for user selection.</li>
<li>Copy the selected template to the target path.</li>
<li>Automatically clean up temporary files and output success message.</li>
</ol>
<h2>FAQ</h2>
<p><strong>Q: Where is the cache stored?</strong></p>
<pre><code class="language-bash">~/.tmpl-cli/{.template_cli_cache.json}
</code></pre>
<p><strong>Q: How to completely reset the configuration?</strong></p>
<pre><code class="language-bash">rm -rf ~/.tmpl-cli &amp;&amp; template-cli --clear-cache
</code></pre>
<p><strong>Q: Supported template repository structure?</strong></p>
<pre><code>&lt;repository&gt;/
├── template1/
├── template2/
└── .gitignore
</code></pre>
<h2>Developer Guide</h2>
<h3>Code Structure</h3>
<pre><code>src/
├── cache.rs    # Cache module
├── cli.rs      # Command-line parsing
├── errors.rs   # Error handling
├── git.rs      # Git operations
├── original.rs # Native mode implementation
├── utils.rs    # Utility methods
└── template.rs # Core template handling
</code></pre>
<h3>Contribution Guide</h3>
<ol>
<li>Fork this repository.</li>
<li>Create a feature branch: <code>git checkout -b feature/X</code>.</li>
<li>Implement the feature and test it.</li>
<li>Create a Pull Request.</li>
</ol>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Template CLI：高效管理项目模板的工具]]></title>
            <link>https://ryanuo.cc/zh/posts/tmpl-cli</link>
            <guid>https://ryanuo.cc/zh/posts/tmpl-cli</guid>
            <pubDate>Mon, 10 Mar 2025 20:00:00 GMT</pubDate>
            <description><![CDATA[一款高效的项目模板管理工具，支持从 Git 仓库快速克隆模板并灵活配置]]></description>
            <content:encoded><![CDATA[<h1>Template CLI 🚀</h1>
<p>便捷的项目模板管理工具，从 Git 仓库快速克隆模板</p>
<h2>特性</h2>
<ul>
<li>✅ 交互式模板选择</li>
<li>✅ 命令行参数灵活配置</li>
<li>✅ 智能缓存配置管理</li>
<li>✅ 支持分支指定和目录重命名</li>
<li>⚡ 支持原生 JSON 项目选择模式</li>
</ul>
<h2>安装</h2>
<h3>通过 Cargo 安装</h3>
<pre><code class="language-bash">cargo install --path .
</code></pre>
<h3>手动构建</h3>
<pre><code class="language-bash">git clone https://github.com/your-repo/project-template.git
cd project-template
cargo build --release
</code></pre>
<h2>使用指南</h2>
<h3>基础命令结构</h3>
<pre><code class="language-bash">template-cli [参数选项]
</code></pre>
<h3>快速开始示例</h3>
<pre><code class="language-bash"># 克隆模板项目（交互式选择）
template-cli https://github.com/my-repo/templates

# 指定参数下载
template-cli -r https://github.com/my-repo -b dev -d ./new-project -t my_template

# 查看缓存配置
template-cli -x

# 使用原生项目选择模式
template-cli --original https://github.com/my-repo.json
</code></pre>
<h2>参数说明</h2>
<table>
<thead>
<tr>
<th>参数</th>
<th>描述</th>
<th>默认值</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-r, --repo</code></td>
<td>需要克隆的 Git 仓库地址</td>
<td>-</td>
</tr>
<tr>
<td><code>-b, --branch</code></td>
<td>Git 分支名称</td>
<td>main</td>
</tr>
<tr>
<td><code>-d, --target-dir</code></td>
<td>模板保存的目标目录</td>
<td>当前目录</td>
</tr>
<tr>
<td><code>-t, template</code></td>
<td>需要使用的具体模板名称</td>
<td>交互式选择</td>
</tr>
<tr>
<td><code>-o, --original</code></td>
<td>使用原生 JSON 配置文件来源</td>
<td>-</td>
</tr>
<tr>
<td><code>-c, --clear-cache</code></td>
<td>清除配置缓存</td>
<td>-</td>
</tr>
<tr>
<td><code>-x, --check-cache</code></td>
<td>查看已保存的配置缓存</td>
<td>-</td>
</tr>
</tbody>
</table>
<h3>进阶参数</h3>
<ul>
<li>
<p><strong>智能缓存</strong>：<br>
自动保存最近使用的仓库、分支等配置，再次使用时会优先读取缓存</p>
<ul>
<li>查看缓存：<code>template-cli -x</code></li>
<li>清除缓存：<code>template-cli -c</code></li>
</ul>
</li>
<li>
<p><strong>原生项目选择模式</strong>：<br>
使用 <code>--original</code> 参数指定 JSON 配置文件地址，会展示类似这样的界面进行选择：</p>
<pre><code class="language-bash">Category：
1. Frontend Projects
2. Backend Projects

Select a category (default: Frontend Projects):
</code></pre>
</li>
</ul>
<h2>工作流程</h2>
<ol>
<li>用户输入参数或选择交互模式</li>
<li>根据参数/缓存确定需要克隆的仓库信息</li>
<li>从指定分支克隆仓库到临时目录</li>
<li>展示可用模板列表供用户选择</li>
<li>将选择的模板复制到目标路径</li>
<li>自动清理临时文件并输出成功提示</li>
</ol>
<h2>常见问题</h2>
<p><strong>Q: 缓存存储在哪儿？</strong></p>
<pre><code class="language-bash">~/.tmpl-cli/{.template_cli_cache.json}
</code></pre>
<p><strong>Q: 如何完全重置配置？</strong></p>
<pre><code class="language-bash">rm -rf ~/.tmpl-cli &amp;&amp; template-cli --clear-cache
</code></pre>
<p><strong>Q: 支持的模板仓库结构？</strong></p>
<pre><code>&lt;repository&gt;/
├── template1/
├── template2/
└── .gitignore
</code></pre>
<h2>开发者指南</h2>
<h3>代码结构</h3>
<pre><code>src/
├── cache.rs    # 缓存模块
├── cli.rs      # 命令行解析
├── errors.rs   # 错误处理
├── git.rs      # Git操作
├── original.rs # 原生模式实现
├── utils.rs    # 通用方法
└── template.rs # 模板处理核心
</code></pre>
<h3>贡献指南</h3>
<ol>
<li>Fork 本项目</li>
<li>创建功能分支：<code>git checkout -b feature/X</code></li>
<li>实现功能并测试</li>
<li>创建 Pull Request</li>
</ol>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Comprehensive Gold Investment Guide]]></title>
            <link>https://ryanuo.cc/posts/aug</link>
            <guid>https://ryanuo.cc/posts/aug</guid>
            <pubDate>Mon, 10 Feb 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A detailed guide on gold investment, covering account setup, trading processes, price conversion, and monitoring techniques.]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h3>Gold Buying Process (Using JD Finance as an Example)</h3>
<ol>
<li><strong>Account Setup</strong></li>
</ol>
<ul>
<li>Download the JD Finance app → Complete identity verification → Link your bank account.</li>
</ul>
<ol start="2">
<li><strong>Fund Transfer</strong></li>
</ol>
<ul>
<li>Recharge your gold account balance via bank card or JD Xiaojinku.</li>
</ul>
<ol start="3">
<li><strong>Buying Gold</strong></li>
</ol>
<ul>
<li>Path: Finance Channel → JD Gold → Enter amount/weight → Confirm real-time gold price and place the order.</li>
<li>Trading hours:
<ul>
<li>Closed on holidays.</li>
<li>24-hour trading.</li>
</ul>
</li>
</ul>
<ol start="4">
<li><strong>Selling and Withdrawal</strong></li>
</ol>
<ul>
<li>Funds are credited to the gold account immediately after selling. Withdrawals to a bank account incur a 0.3% fee.</li>
</ul>
<h3>Gold Conversion Methods</h3>
<ol>
<li><strong>Unit Conversion</strong></li>
</ol>
<ul>
<li>1 ounce ≈ 31.1035 grams.</li>
<li>Domestic prices are quoted in &quot;CNY/gram,&quot; while international prices are quoted in &quot;USD/ounce.&quot;</li>
</ul>
<ol start="2">
<li><strong>Exchange Rate Impact</strong></li>
</ol>
<ul>
<li>
<p>Domestic gold price = International gold price (USD/ounce) × RMB exchange rate ÷ 31.1035.</p>
</li>
<li>
<p>Example: International gold price is $2000/ounce, exchange rate is 7.2 → Domestic gold price ≈ 462.8 CNY/gram.</p>
</li>
<li>
<p><a href="https://www.jins.gold/tools.html">Online Conversion Tool</a></p>
</li>
</ul>
<h3>How to Monitor Gold Prices</h3>
<h3><strong>Global Trading Session Comparison Table (2025)</strong></h3>
<img src="/posts/aug.png"/>
<ol start="2">
<li><strong>Monitoring Tools</strong></li>
</ol>
<ul>
<li>JD Finance app for real-time gold prices/<a href="https://m.jdjygold.com/finance-gold/newgold/home/?orderSource=hjgongzhonghao">Online View</a>.</li>
<li><a href="https://www.jin10.com/">Jin10 Data</a></li>
<li><a href="https://finance.sina.com.cn/futures/quotes/GC.shtml">NY Gold CFD</a></li>
</ul>
<h3>Onshore vs Offshore &amp; Domestic Gold Price Rationality</h3>
<ol>
<li><strong>Reasons for Price Differences</strong></li>
</ol>
<ul>
<li>Onshore gold price: Quoted by the Shanghai Gold Exchange (includes VAT, transportation costs, etc.).</li>
<li>Offshore gold price: International London Gold (LBMA), without tax or fees.</li>
<li>Price difference is usually 1%-3%, mainly due to:
<ul>
<li>RMB exchange rate fluctuations;</li>
<li>Domestic supply, demand, and policies (e.g., import controls).</li>
</ul>
</li>
</ul>
<ol start="2">
<li><strong>Different Actual Trading Prices</strong></li>
</ol>
<ul>
<li>Platforms may charge spreads or fees (e.g., JD Finance charges a 0.3% selling fee).</li>
</ul>
<h3>Frequently Asked Questions</h3>
<ol>
<li><strong>Can I Place Orders Outside Trading Hours?</strong></li>
</ol>
<ul>
<li>You can submit a reservation order, which will take effect in the next trading session.</li>
<li>Orders can be placed 24/7, but transactions are executed during specific trading hours: Monday to Friday 9:00-11:30, 13:30-15:00, 21:00-24:00. No trading on public holidays. To avoid timing discrepancies, operate in advance.</li>
</ul>
<ol start="2">
<li><strong>Why Is Domestic Gold Price Higher Than Converted Price?</strong></li>
</ol>
<ul>
<li>Includes buy-sell spreads (platform profit source).</li>
<li>Premiums may exist.</li>
</ul>
<ol start="3">
<li><strong>How to Break Even at High Prices?</strong></li>
</ol>
<ul>
<li>Single transactions are essentially &quot;cutting losses.&quot; Even with full positions, you can achieve this through single trades. The key is to sell high and buy back at a lower price, maintaining the total position (weight). Through multiple high-sell-low-buy operations, you can not only reduce the average holding cost but also potentially gain some price differences.</li>
</ul>
<ol start="4">
<li><strong>Where to Check Premiums?</strong></li>
</ol>
<ul>
<li>Search for &quot;Premium&quot; in the <code>Pocket Precious Metals</code> app.</li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[黄金投资指南-小白版]]></title>
            <link>https://ryanuo.cc/zh/posts/aug</link>
            <guid>https://ryanuo.cc/zh/posts/aug</guid>
            <pubDate>Mon, 10 Feb 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[一份详细的黄金投资操作指南，涵盖开户、买卖流程、价格换算及盯盘技巧。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h3>买金流程（以京东金融为例）</h3>
<ol>
<li><strong>开户准备</strong></li>
</ol>
<ul>
<li>下载京东金融APP → 实名认证 → 绑定银行卡。</li>
</ul>
<ol start="2">
<li><strong>资金转入</strong></li>
</ol>
<ul>
<li>通过银行卡或京东小金库充值至黄金账户现金余额。</li>
</ul>
<ol start="3">
<li><strong>买入操作</strong></li>
</ol>
<ul>
<li>路径：理财频道 → 京东黄金 → 输入金额/克数 → 确认实时金价后下单。</li>
<li>交易时段：
<ul>
<li>节假日休市</li>
<li>24小时交易。</li>
</ul>
</li>
</ul>
<ol start="4">
<li><strong>卖出与提现</strong></li>
</ol>
<ul>
<li>卖出后资金实时到账黄金账户，提现至银行卡需支付0.3%的手续费。</li>
</ul>
<h3>黄金换算方法</h3>
<ol>
<li><strong>单位换算</strong></li>
</ol>
<ul>
<li>1盎司 ≈ 31.1035克。</li>
<li>国内以“元/克”报价，国际以“美元/盎司”报价。</li>
</ul>
<ol start="2">
<li><strong>汇率影响</strong></li>
</ol>
<ul>
<li>
<p>国内金价 = 国际金价（美元/盎司） × 人民币汇率 ÷ 31.1035。</p>
</li>
<li>
<p>示例：国际金价2000美元/盎司，汇率7.2 → 国内金价≈462.8元/克。</p>
</li>
<li>
<p><a href="https://www.jins.gold/tools.html">在线换算工具</a></p>
</li>
</ul>
<h3>如何盯盘</h3>
<h3><strong>全球交易时段对照表（2025年）</strong></h3>
<img src="/posts/aug.png"/>
<ol start="2">
<li><strong>盯盘工具</strong></li>
</ol>
<ul>
<li>京东金融APP实时金价/<a href="https://m.jdjygold.com/finance-gold/newgold/home/?orderSource=hjgongzhonghao">在线观看</a>。</li>
<li><a href="https://www.jin10.com/">金十数据</a></li>
<li><a href="https://finance.sina.com.cn/futures/quotes/GC.shtml">纽约黄金CFD</a></li>
</ul>
<h3>在岸 vs 离岸 &amp; 国内金价合理性</h3>
<ol>
<li><strong>价格差异原因</strong></li>
</ol>
<ul>
<li>在岸金价：上海黄金交易所报价（含增值税、运输成本等）。</li>
<li>离岸金价：国际伦敦金（LBMA），无税费成本。</li>
<li>价差通常为1%-3%，主要原因：
<ul>
<li>人民币汇率波动；</li>
<li>国内供需与政策（如进口管制）。</li>
</ul>
</li>
</ul>
<ol start="2">
<li><strong>实际交易价不同</strong></li>
</ol>
<ul>
<li>平台可能加收点差或手续费（如京东金融卖出0.3%）。</li>
</ul>
<h3>常见问题</h3>
<ol>
<li><strong>非交易时段能下单吗？</strong></li>
</ol>
<ul>
<li>可提交预约单，下一交易时段生效。</li>
<li>24小时都可以下单买入，但买/卖价格确定时段立即成交，非买/卖价格确定时段需至下个交易时段进行成交。买/卖价格确定时段为周一至周五9:00-11:30，13:30-15:00，21:00-24:00，法定节假日不交易。为避免临界时间的时间差，请提前操作。</li>
</ul>
<ol start="2">
<li><strong>为何国内金价高于换算价格？</strong></li>
</ol>
<ul>
<li>存在买卖点差（平台利润来源）。</li>
<li>存在溢价的情况。</li>
</ul>
<ol start="3">
<li><strong>如何高位解套？</strong></li>
</ol>
<ul>
<li>单笔交易的本质类似于“割肉”，即便是满仓操作也可以通过单笔交易实现。关键在于卖出后能以更低的价格买入，保持总仓位（克重）不变。通过多次高抛低吸操作，不仅可以降低持仓均价，还可能获得一定的价差收益。</li>
</ul>
<ol start="4">
<li><strong>溢价在哪看？</strong></li>
</ol>
<ul>
<li>在<code>口袋贵金属</code>APP上搜索“溢价”即可查看。</li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Local Deployment Guide for Ollama and Open-WebUI on Mac]]></title>
            <link>https://ryanuo.cc/posts/ollama</link>
            <guid>https://ryanuo.cc/posts/ollama</guid>
            <pubDate>Sat, 01 Feb 2025 15:30:00 GMT</pubDate>
            <description><![CDATA[This comprehensive guide details how to deploy large language models locally on Mac systems using Ollama and Open-WebUI, covering installation, model downloads, configuration, and usage workflows to help you quickly set up a local AI interaction environment.]]></description>
            <content:encoded><![CDATA[<h2>Why?</h2>
<p>Efficiency and Convenience: The integrated deployment process allows you to complete Ollama setup, OpenWebUI launch, and browser opening with a single execution, significantly improving efficiency compared to manual operations.</p>
<h2>Deployment Preparation</h2>
<h3>System Requirements</h3>
<ul>
<li>OS Requirements: Recommended to use the latest version of macOS for optimal software compatibility and performance.</li>
<li>Hardware Requirements: Minimum 8GB RAM for smooth model operation. For optional GPU acceleration, your Mac needs to support compatible GPUs (e.g., AMD Radeon Pro series in some MacBook Pro models).</li>
<li>Network: Stable internet connection required for downloading Ollama, model files, and Open-WebUI resources.</li>
</ul>
<h3>Essential Software Installation (Ollama, OpenWebUI, Shell environment):</h3>
<ol>
<li>Python 3.11~3.12</li>
<li>Ollama <a href="https://ollama.com/download/mac">Official Download</a></li>
<li>OpenWebUI</li>
</ol>
<pre><code class="language-zsh">pip install open-webui
</code></pre>
<h3>Model Preparation</h3>
<pre><code class="language-bash">ollama pull qwen3:8b
</code></pre>
<p><a href="https://ollama.com/search">Model Download</a></p>
<h2>Quick Start</h2>
<h3>Create Startup Script</h3>
<p>Save the script anywhere on your Mac, e.g.: /Users/username/Desktop/ollama_start.sh</p>
<pre><code class="language-sh"># Configure ports
PORT=11434
WEB_PORT=9790

# Start Ollama service
start_ollama() {
  echo &quot;Starting Ollama...&quot;
  ollama serve &amp;
  OLLAMA_PID=$!
  echo &quot;Ollama Process ID: $OLLAMA_PID&quot;
}

# Start OpenWebUI
start_webui() {
  echo &quot;Starting OpenWebUI...&quot;
  open-webui serve --port &quot;$WEB_PORT&quot; &amp;
  WEBUI_PID=$!
  echo &quot;OpenWebUI Process ID: $WEBUI_PID&quot;
}

# Auto-open browser
open_browser() {
  echo &quot;Opening browser...&quot;
  # Cross-platform support
  case &quot;$(uname -s)&quot; in
    Darwin) open &quot;http://localhost:$WEB_PORT&quot; ;;
    Linux) xdg-open &quot;http://localhost:$WEB_PORT&quot; ;;
    Windows) start &quot;http://localhost:$WEB_PORT&quot; ;;
    *) echo &quot;Unsupported OS, cannot auto-open browser&quot; ;;
  esac
}

# Main workflow
start_ollama
sleep 5  # Wait for Ollama to start (adjust time based on model size)
start_webui
sleep 5  # Wait for WebUI to start
open_browser  # Auto-open browser

# Display access info
echo -e &quot;\nOllama service running at: http://localhost:$PORT&quot;
echo -e &quot;OpenWebUI running at: \033[32mhttp://localhost:$WEB_PORT\033[0m&quot;
echo &quot;Press Ctrl+C to stop all services&quot;

# Handle termination signals
trap &quot;echo 'Stopping services...'; kill -9 $OLLAMA_PID $WEBUI_PID 2&gt;/dev/null; wait; echo 'Services stopped'.&quot; SIGINT SIGTERM

# Wait for processes
wait
</code></pre>
<h3>Configure Quick-Start Alias</h3>
<p>Create a shell script named ollama_start.sh and add:</p>
<pre><code class="language-sh"># alias ollama_start='/Users/username/Desktop/ollama_start.sh'
echo &quot;alias ollama_start='/Users/username/Desktop/ollama_start.sh'&quot; &gt;&gt; ~/.zshrc
source ~/.zshrc
ollama_start # Launch services
</code></pre>
<h3>Script Explanation</h3>
<ol>
<li>Port Configuration: Defines two ports for Ollama and OpenWebUI (default: 11434 and 9790, customizable).</li>
<li>Start Ollama: Launches Ollama service with <code>ollama serve</code> and records process ID.</li>
<li>Start OpenWebUI: Launches OpenWebUI with <code>open-webui serve</code> and records process ID.</li>
<li>Auto-open Browser: Opens browser automatically with OS-appropriate commands.</li>
<li>Main Workflow: Starts services sequentially with delays, then opens browser and displays access info.</li>
<li>Termination Handling: Gracefully stops services when receiving Ctrl+C.</li>
</ol>
<h2>References</h2>
<ul>
<li><a href="https://ollama.com/">ollama</a></li>
<li><a href="https://github.com/jmorganca/ollama">open-webui</a></li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[在 Mac 上使用 Ollama 和 Open-WebUI 进行本地化部署指南]]></title>
            <link>https://ryanuo.cc/zh/posts/ollama</link>
            <guid>https://ryanuo.cc/zh/posts/ollama</guid>
            <pubDate>Sat, 01 Feb 2025 15:30:00 GMT</pubDate>
            <description><![CDATA[本指南详细介绍如何在 Mac 系统上，通过 Ollama 和 Open-WebUI 进行大模型本地化部署，涵盖安装、模型下载、配置及使用全流程，助力你快速搭建本地 AI 交互环境。]]></description>
            <content:encoded><![CDATA[<h2>为什么？</h2>
<p>高效便捷：整合部署流程，一次执行即可完成 Ollama、OpenWebUI 启动及浏览器打开，相比手动操作大幅提升效率。</p>
<h2>部署准备</h2>
<h3>系统环境要求</h3>
<p>系统环境要求：建议使用最新版本的 macOS 系统，以确保软件兼容性和性能优化。<br>
硬件要求：为了流畅运行模型，推荐至少 8GB 内存。若要使用 GPU 加速（可选），需要 Mac 支持相应的 GPU，如某些 MacBook Pro 机型配备的 AMD Radeon Pro 系列 GPU 。<br>
网络连接：稳定的网络连接，因为在部署过程中需要下载 Ollama、模型文件以及 Open-WebUI 相关资源。</p>
<p>必备软件安装（Ollama、OpenWebUI、Shell 运行环境：</p>
<ol>
<li>Python3.11~3.12</li>
<li>Ollama, <a href="https://ollama.com/download/mac">官方下载</a></li>
<li>OpenWebUI</li>
</ol>
<pre><code class="language-zsh">pip install open-webui
</code></pre>
<h3>模型准备</h3>
<pre><code class="language-bash">ollama pull qwen3:8b
</code></pre>
<p><a href="https://ollama.com/search">模型下载</a></p>
<h2>快速启动</h2>
<h3>创建保存脚本</h3>
<p>保持在mac系统的任意位置，如：/Users/username/Desktop/ollama_start.sh</p>
<pre><code class="language-sh"># 配置端口
PORT=11434
WEB_PORT=9790

# 启动Ollama服务
start_ollama() {
  echo &quot;正在启动 Ollama...&quot;
  ollama serve &amp;
  OLLAMA_PID=$!
  echo &quot;Ollama 进程ID: $OLLAMA_PID&quot;
}

# 启动OpenWebUI
start_webui() {
  echo &quot;正在启动 OpenWebUI...&quot;
  open-webui serve --port &quot;$WEB_PORT&quot; &amp;
  WEBUI_PID=$!
  echo &quot;OpenWebUI 进程ID: $WEBUI_PID&quot;
}

# 自动打开浏览器
open_browser() {
  echo &quot;正在打开浏览器...&quot;
  # 支持macOS/Windows/Linux通用格式
  case &quot;$(uname -s)&quot; in
    Darwin) open &quot;http://localhost:$WEB_PORT&quot; ;;
    Linux) xdg-open &quot;http://localhost:$WEB_PORT&quot; ;;
    Windows) start &quot;http://localhost:$WEB_PORT&quot; ;;
    *) echo &quot;不支持的操作系统，无法自动打开浏览器&quot; ;;
  esac
}

# 主流程
start_ollama
sleep 5  # 等待Ollama服务启动（根据模型大小可调整时间）
start_webui
sleep 5  # 等待WebUI启动
open_browser  # 自动打开浏览器

# 输出访问信息
echo -e &quot;\nOllama 服务运行在：http://localhost:$PORT&quot;
echo -e &quot;OpenWebUI 运行在：\033[32mhttp://localhost:$WEB_PORT\033[0m&quot;
echo &quot;按 Ctrl+C 停止所有服务&quot;

# 终止信号处理
trap &quot;echo '停止服务...'; kill -9 $OLLAMA_PID $WEBUI_PID 2&gt;/dev/null; wait; echo '服务已停止'.&quot; SIGINT SIGTERM

# 等待进程结束
wait
</code></pre>
<h3>配置快速启动别名</h3>
<p>创建一个名为ollama_start.sh的shell脚本，并添加以下内容：</p>
<pre><code class="language-sh"># alias ollama_start='/Users/username/Desktop/ollama_start.sh'
echo &quot;alias ollama_start='/Users/username/Desktop/ollama_start.sh'&quot; &gt;&gt; ~/.zshrc
source ~/.zshrc
ollama_start # 启动服务
</code></pre>
<h3>脚本详解</h3>
<ol>
<li>配置端口：脚本中定义了两个端口，分别用于Ollama和OpenWebUI。默认端口为11434和9790，可以根据需要修改。</li>
<li>启动Ollama服务：使用ollama serve命令启动Ollama服务，并保存进程ID。</li>
<li>启动OpenWebUI：使用open-webui serve命令启动OpenWebUI服务，并保存进程ID。</li>
<li>自动打开浏览器：根据操作系统的不同，使用不同的命令打开浏览器，并指定访问地址。</li>
<li>主流程：启动Ollama和OpenWebUI服务，并等待它们启动。然后自动打开浏览器，并输出访问信息。</li>
<li>输出访问信息：输出Ollama和OpenWebUI的访问地址，并提示按Ctrl+C停止服务。</li>
</ol>
<h2>参考文章</h2>
<ul>
<li><a href="https://ollama.com/">ollama</a></li>
<li><a href="https://github.com/jmorganca/ollama">open-webui</a></li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Generate a random name with Javascript]]></title>
            <link>https://ryanuo.cc/posts/make-name</link>
            <guid>https://ryanuo.cc/posts/make-name</guid>
            <pubDate>Tue, 02 Jul 2024 08:50:00 GMT</pubDate>
            <description><![CDATA[Generate a random name with Javascript]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<NameMake />
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[生成一个随机名字]]></title>
            <link>https://ryanuo.cc/zh/posts/make-name</link>
            <guid>https://ryanuo.cc/zh/posts/make-name</guid>
            <pubDate>Tue, 02 Jul 2024 08:50:00 GMT</pubDate>
            <description><![CDATA[生成一个随机名字]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<NameMake />
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Yunshu (EagleYun) Mac Uninstall Guide]]></title>
            <link>https://ryanuo.cc/posts/yunshu-uninstall</link>
            <guid>https://ryanuo.cc/posts/yunshu-uninstall</guid>
            <pubDate>Mon, 01 Jul 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[This article provides a complete guide to uninstalling Yunshu/EagleYun client on Mac, including residue detection, manual removal, automation script, and important notes.]]></description>
            <content:encoded><![CDATA[<h1>Yunshu (EagleYun) Mac Uninstall Guide</h1>
<h2>1️⃣ Purpose</h2>
<p>Completely remove the Yunshu/EagleYun client and its background services, clean up residual directories and configuration files, and keep your system clean without affecting other applications.</p>
<h2>2️⃣ Detect Yunshu Residue</h2>
<h3>2.1 Detection Script Example</h3>
<pre><code class="language-bash">#!/bin/bash
echo &quot;=== Detecting Yunshu (EagleYun) Residue ===&quot;

echo &quot;&quot;
echo &quot;&gt;&gt; Process Check:&quot;
ps aux | grep -i &quot;yunshu\|eagleyun&quot; | grep -v grep

echo &quot;&quot;
echo &quot;&gt;&gt; Startup Items (LaunchDaemons / LaunchAgents):&quot;
sudo grep -ril &quot;yunshu\|eagleyun&quot; /Library/LaunchDaemons /Library/LaunchAgents ~/Library/LaunchAgents 2&gt;/dev/null

echo &quot;&quot;
echo &quot;&gt;&gt; Application Directories:&quot;
[ -d &quot;/opt/.yunshu&quot; ] &amp;&amp; echo &quot;/opt/.yunshu&quot;
[ -d &quot;/Library/Application Support/Yunshu&quot; ] &amp;&amp; echo &quot;/Library/Application Support/Yunshu&quot;
[ -d &quot;/Library/Application Support/EagleCloud&quot; ] &amp;&amp; echo &quot;/Library/Application Support/EagleCloud&quot;

echo &quot;&quot;
echo &quot;&gt;&gt; Log Directories:&quot;
[ -d &quot;/Library/Logs/com.eagleyun.sase.helper&quot; ] &amp;&amp; echo &quot;/Library/Logs/com.eagleyun.sase.helper&quot;

echo &quot;&quot;
echo &quot;&gt;&gt; User Preferences:&quot;
ls ~/Library/Preferences | grep -i &quot;eagleyun&quot; 2&gt;/dev/null
ls ~/Library/Preferences | grep -i &quot;yunshu&quot; 2&gt;/dev/null

echo &quot;&quot;
echo &quot;=== Detection Complete ===&quot;
</code></pre>
<h3>2.2 How to Run</h3>
<pre><code class="language-bash">chmod +x check_yunshu.sh
./check_yunshu.sh
</code></pre>
<h3>2.3 Detection Result Explanation</h3>
<ul>
<li><strong>Process</strong>: If you see <code>FWRunner</code>, <code>TBRunner</code>, <code>YunshuAgent</code>, etc., the software is still running.</li>
<li><strong>Startup Items</strong>: Files like <code>com.eagleyun*.plist</code> indicate auto-start entries exist.</li>
<li><strong>Application Directories</strong>: <code>/opt/.yunshu</code>, <code>/Library/Application Support/Yunshu</code>, <code>/Library/Application Support/EagleCloud</code></li>
<li><strong>Log Directories</strong>: <code>/Library/Logs/com.eagleyun.sase.helper</code></li>
<li><strong>User Preferences</strong>: <code>~/Library/Preferences/com.eagleyun.sase.plist</code></li>
</ul>
<h2>3️⃣ Manual Uninstall Steps</h2>
<h3>3.1 Stop Background Processes</h3>
<pre><code class="language-bash">sudo kill -9 $(ps aux | grep -i &quot;yunshu\|eagleyun&quot; | grep -v grep | awk '{print $2}')
</code></pre>
<h3>3.2 Remove Startup Items</h3>
<pre><code class="language-bash">sudo launchctl bootout system /Library/LaunchDaemons/com.eagleyun*.plist 2&gt;/dev/null
sudo rm -f /Library/LaunchDaemons/com.eagleyun*.plist
rm -f ~/Library/LaunchAgents/com.eagleyun*
</code></pre>
<h3>3.3 Delete Application Directories and Logs</h3>
<pre><code class="language-bash">sudo rm -rf /opt/.yunshu
sudo rm -rf &quot;/Library/Application Support/Yunshu&quot;
sudo rm -rf &quot;/Library/Application Support/EagleCloud&quot;
sudo rm -rf &quot;/Library/Logs/com.eagleyun.sase.helper&quot;
rm -f ~/Library/Preferences/com.eagleyun.sase.plist
</code></pre>
<h2>4️⃣ Uninstall Confirmation</h2>
<p>Run the detection script <code>check_yunshu.sh</code> again or manually check:</p>
<pre><code class="language-bash">ps aux | grep -i &quot;yunshu\|eagleyun&quot; | grep -v grep
launchctl list | grep -i &quot;yunshu\|eagleyun&quot;
</code></pre>
<p><strong>If the output is empty</strong>, the uninstall is complete.</p>
<h2>5️⃣ Notes</h2>
<ol>
<li><strong>Permissions</strong>: Removing files under <code>/opt/.yunshu</code> and <code>/Library</code> requires <code>sudo</code> privileges.</li>
<li><strong>System Integrity</strong>: Do not delete files under <code>/System/Library</code> to avoid damaging macOS.</li>
<li><strong>Backup</strong>: Backup important data before uninstalling.</li>
<li><strong>Restart Recommended</strong>: Restart your system after uninstalling to ensure all background services are cleared.</li>
</ol>
<h2>6️⃣ Automated Uninstall Script (Optional)</h2>
<p>For one-click uninstall, use the following script:</p>
<pre><code class="language-bash">#!/bin/bash
echo &quot;=== Starting Yunshu (EagleYun) Uninstall ===&quot;

echo &quot;&gt;&gt; Stopping related processes...&quot;
sudo kill -9 $(ps aux | grep -i &quot;yunshu\|eagleyun&quot; | grep -v grep | awk '{print $2}') 2&gt;/dev/null

echo &quot;&gt;&gt; Removing startup items...&quot;
sudo launchctl bootout system /Library/LaunchDaemons/com.eagleyun*.plist 2&gt;/dev/null
sudo rm -f /Library/LaunchDaemons/com.eagleyun*.plist
rm -f ~/Library/LaunchAgents/com.eagleyun*

echo &quot;&gt;&gt; Deleting application directories and logs...&quot;
sudo rm -rf /opt/.yunshu
sudo rm -rf &quot;/Library/Application Support/Yunshu&quot;
sudo rm -rf &quot;/Library/Application Support/EagleCloud&quot;
sudo rm -rf &quot;/Library/Logs/com.eagleyun.sase.helper&quot;
rm -f ~/Library/Preferences/com.eagleyun.sase.plist

echo &quot;=== Uninstall complete. Please restart your system. ===&quot;
</code></pre>
<p><strong>How to use</strong>:</p>
<ol>
<li>Save the above content as <code>uninstall_yunshu.sh</code></li>
<li>Make it executable and run:<pre><code class="language-bash">chmod +x uninstall_yunshu.sh
./uninstall_yunshu.sh
</code></pre>
</li>
</ol>
<p><strong>Note</strong>: The automation script will perform all uninstall steps. Make sure to backup important data and close related applications.</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Yunshu (EagleYun) Mac 卸载指南]]></title>
            <link>https://ryanuo.cc/zh/posts/yunsuh-uninstall</link>
            <guid>https://ryanuo.cc/zh/posts/yunsuh-uninstall</guid>
            <pubDate>Mon, 01 Jul 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[本文详细介绍如何在 Mac 上彻底卸载 Yunshu/EagleYun 客户端，包括检测残留、手动卸载、自动化脚本及注意事项，帮助用户清理系统环境。]]></description>
            <content:encoded><![CDATA[<h1>Yunshu (EagleYun) Mac 卸载文档</h1>
<h2>1️⃣ 卸载目的</h2>
<p>彻底移除 Yunshu/EagleYun 客户端及其后台服务，清理残留目录和配置文件，保证系统干净，不影响其他应用。</p>
<h2>2️⃣ 检测 Yunshu 残留</h2>
<h3>2.1 检测脚本示例</h3>
<pre><code class="language-bash">#!/bin/bash
echo &quot;=== 检测 Yunshu (EagleYun) 残留 ===&quot;

echo &quot;&quot;
echo &quot;&gt;&gt; 进程检查:&quot;
ps aux | grep -i &quot;yunshu\|eagleyun&quot; | grep -v grep

echo &quot;&quot;
echo &quot;&gt;&gt; 启动项 (LaunchDaemons / LaunchAgents):&quot;
sudo grep -ril &quot;yunshu\|eagleyun&quot; /Library/LaunchDaemons /Library/LaunchAgents ~/Library/LaunchAgents 2&gt;/dev/null

echo &quot;&quot;
echo &quot;&gt;&gt; 应用目录:&quot;
[ -d &quot;/opt/.yunshu&quot; ] &amp;&amp; echo &quot;/opt/.yunshu&quot;
[ -d &quot;/Library/Application Support/Yunshu&quot; ] &amp;&amp; echo &quot;/Library/Application Support/Yunshu&quot;
[ -d &quot;/Library/Application Support/EagleCloud&quot; ] &amp;&amp; echo &quot;/Library/Application Support/EagleCloud&quot;

echo &quot;&quot;
echo &quot;&gt;&gt; 日志目录:&quot;
[ -d &quot;/Library/Logs/com.eagleyun.sase.helper&quot; ] &amp;&amp; echo &quot;/Library/Logs/com.eagleyun.sase.helper&quot;

echo &quot;&quot;
echo &quot;&gt;&gt; 用户偏好文件:&quot;
ls ~/Library/Preferences | grep -i &quot;eagleyun&quot; 2&gt;/dev/null
ls ~/Library/Preferences | grep -i &quot;yunshu&quot; 2&gt;/dev/null

echo &quot;&quot;
echo &quot;=== 检测完成 ===&quot;
</code></pre>
<h3>2.2 运行方式</h3>
<pre><code class="language-bash">chmod +x check_yunshu.sh
./check_yunshu.sh
</code></pre>
<h3>2.3 检测结果说明</h3>
<ul>
<li><strong>进程</strong>：如有 <code>FWRunner</code>、<code>TBRunner</code>、<code>YunshuAgent</code> 等进程表示软件仍在运行</li>
<li><strong>启动项</strong>：包含 <code>com.eagleyun*.plist</code> 表示开机自启存在</li>
<li><strong>应用目录</strong>：<code>/opt/.yunshu</code>、<code>/Library/Application Support/Yunshu</code>、<code>/Library/Application Support/EagleCloud</code></li>
<li><strong>日志目录</strong>：<code>/Library/Logs/com.eagleyun.sase.helper</code></li>
<li><strong>用户偏好文件</strong>：<code>~/Library/Preferences/com.eagleyun.sase.plist</code></li>
</ul>
<h2>3️⃣ 手动卸载步骤</h2>
<h3>3.1 停止后台进程</h3>
<pre><code class="language-bash">sudo kill -9 $(ps aux | grep -i &quot;yunshu\|eagleyun&quot; | grep -v grep | awk '{print $2}')
</code></pre>
<h3>3.2 卸载启动项</h3>
<pre><code class="language-bash">sudo launchctl bootout system /Library/LaunchDaemons/com.eagleyun*.plist 2&gt;/dev/null
sudo rm -f /Library/LaunchDaemons/com.eagleyun*.plist
rm -f ~/Library/LaunchAgents/com.eagleyun*
</code></pre>
<h3>3.3 删除应用目录及日志</h3>
<pre><code class="language-bash">sudo rm -rf /opt/.yunshu
sudo rm -rf &quot;/Library/Application Support/Yunshu&quot;
sudo rm -rf &quot;/Library/Application Support/EagleCloud&quot;
sudo rm -rf &quot;/Library/Logs/com.eagleyun.sase.helper&quot;
rm -f ~/Library/Preferences/com.eagleyun.sase.plist
</code></pre>
<h2>4️⃣ 卸载确认</h2>
<p>再次运行检测脚本 <code>check_yunshu.sh</code> 或手动检查：</p>
<pre><code class="language-bash">ps aux | grep -i &quot;yunshu\|eagleyun&quot; | grep -v grep
launchctl list | grep -i &quot;yunshu\|eagleyun&quot;
</code></pre>
<p><strong>确认输出为空</strong>，说明卸载彻底。</p>
<h2>5️⃣ 注意事项</h2>
<ol>
<li><strong>权限</strong>：删除 <code>/opt/.yunshu</code> 和 <code>/Library</code> 下文件需要 <code>sudo</code> 权限</li>
<li><strong>系统完整性</strong>：不要删除 <code>/System/Library</code> 下的文件，避免破坏 macOS</li>
<li><strong>备份</strong>：卸载前若有重要数据，建议备份</li>
<li><strong>重启建议</strong>：卸载完成后重启系统，确保后台服务全部清除</li>
</ol>
<h2>6️⃣ 自动化卸载脚本（可选）</h2>
<p>如需一键卸载，可使用如下脚本：</p>
<pre><code class="language-bash">#!/bin/bash
echo &quot;=== 开始自动卸载 Yunshu (EagleYun) ===&quot;

echo &quot;&gt;&gt; 停止相关进程...&quot;
sudo kill -9 $(ps aux | grep -i &quot;yunshu\|eagleyun&quot; | grep -v grep | awk '{print $2}') 2&gt;/dev/null

echo &quot;&gt;&gt; 卸载启动项...&quot;
sudo launchctl bootout system /Library/LaunchDaemons/com.eagleyun*.plist 2&gt;/dev/null
sudo rm -f /Library/LaunchDaemons/com.eagleyun*.plist
rm -f ~/Library/LaunchAgents/com.eagleyun*

echo &quot;&gt;&gt; 删除应用目录及日志...&quot;
sudo rm -rf /opt/.yunshu
sudo rm -rf &quot;/Library/Application Support/Yunshu&quot;
sudo rm -rf &quot;/Library/Application Support/EagleCloud&quot;
sudo rm -rf &quot;/Library/Logs/com.eagleyun.sase.helper&quot;
rm -f ~/Library/Preferences/com.eagleyun.sase.plist

echo &quot;=== 卸载完成，请重启系统 ===&quot;
</code></pre>
<p><strong>使用方法</strong>：</p>
<ol>
<li>将上述内容保存为 <code>uninstall_yunshu.sh</code></li>
<li>赋予执行权限并运行：<pre><code class="language-bash">chmod +x uninstall_yunshu.sh
./uninstall_yunshu.sh
</code></pre>
</li>
</ol>
<p><strong>注意</strong>：自动化脚本会执行所有卸载步骤，请确保已备份重要数据，并关闭相关应用程序。</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Rewrite BlazeB2 Image Hosting Service using Next.js]]></title>
            <link>https://ryanuo.cc/posts/rewrite-blazeb2</link>
            <guid>https://ryanuo.cc/posts/rewrite-blazeb2</guid>
            <pubDate>Tue, 25 Jun 2024 20:00:00 GMT</pubDate>
            <description><![CDATA[Rewrite BlazeB2 Image Hosting Service using Next.js]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<table>
<thead>
<tr>
<th>Old page view</th>
<th>New page view</th>
</tr>
</thead>
<tbody>
<tr>
<td><img src="https://cloud.ryanuo.cc/hexo/2/7039c267-f161-4cf2-ab4b-754159d8e2e3.png" alt=""></td>
<td><img src="https://cloud.ryanuo.cc/hexo/2/7039c267-f161-4cf2-ab4b-754159d8e2e3.png" alt=""></td>
</tr>
</tbody>
</table>
<p>Due to the fact that the frame is older, I decided to rewrite it using Next.js. It also has a lot of features that I want to add, and easy to maintain.</p>
<h2>Technical Selection</h2>
<ol>
<li>Next.js</li>
<li>Tailwind CSS</li>
<li>shadcn/ui</li>
</ol>
<h2>Plan Design Features</h2>
<ol>
<li>support config(config.json).</li>
<li>support upload(drag、paste、click、select).</li>
<li>support download (download once、download selected).</li>
<li>support delete (delete once、delete selected).</li>
<li>support sort (name、size、time).</li>
<li>support preview (image、video、audio).</li>
<li>support copy markdown link or html link.</li>
<li>support pwa (offline support).</li>
<li>support dark mode.</li>
<li>support multi-language.</li>
<li>support image masonry Layouts or gird Layouts.</li>
<li>support image watermark/compress.</li>
<li>support and so on ...</li>
</ol>
<h2>Design Inspiration Source</h2>
<ul>
<li><a href="https://dribbble.com/shots/22846049-File-manager-UI">File manager UI</a></li>
<li><a href="https://dribbble.com/shots/18526739-Photo-Management">Photo Management</a></li>
</ul>
<h2>Development</h2>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[使用 Next.js 重写 BlazeB2 图像托管服务]]></title>
            <link>https://ryanuo.cc/zh/posts/rewrite-blazeb2</link>
            <guid>https://ryanuo.cc/zh/posts/rewrite-blazeb2</guid>
            <pubDate>Tue, 25 Jun 2024 20:00:00 GMT</pubDate>
            <description><![CDATA[使用 Next.js 重写 BlazeB2 图像托管服务]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<table>
<thead>
<tr>
<th>Old page view</th>
<th>New page view</th>
</tr>
</thead>
<tbody>
<tr>
<td><img src="https://cloud.ryanuo.cc/hexo/2/7039c267-f161-4cf2-ab4b-754159d8e2e3.png" alt=""></td>
<td><img src="https://cloud.ryanuo.cc/hexo/2/7039c267-f161-4cf2-ab4b-754159d8e2e3.png" alt=""></td>
</tr>
</tbody>
</table>
<p>Due to the fact that the frame is older, I decided to rewrite it using Next.js. It also has a lot of features that I want to add, and easy to maintain.</p>
<h2>Technical Selection</h2>
<ol>
<li>Next.js</li>
<li>Tailwind CSS</li>
<li>shadcn/ui</li>
</ol>
<h2>Plan Design Features</h2>
<ol>
<li>support config(config.json).</li>
<li>support upload(drag、paste、click、select).</li>
<li>support download (download once、download selected).</li>
<li>support delete (delete once、delete selected).</li>
<li>support sort (name、size、time).</li>
<li>support preview (image、video、audio).</li>
<li>support copy markdown link or html link.</li>
<li>support pwa (offline support).</li>
<li>support dark mode.</li>
<li>support multi-language.</li>
<li>support image masonry Layouts or gird Layouts.</li>
<li>support image watermark/compress.</li>
<li>support and so on ...</li>
</ol>
<h2>Design Inspiration Source</h2>
<ul>
<li><a href="https://dribbble.com/shots/22846049-File-manager-UI">File manager UI</a></li>
<li><a href="https://dribbble.com/shots/18526739-Photo-Management">Photo Management</a></li>
</ul>
<h2>Development</h2>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Markdown-it Diagram: A Powerful Plugin for Flowcharts]]></title>
            <link>https://ryanuo.cc/posts/md-it-diagarm</link>
            <guid>https://ryanuo.cc/posts/md-it-diagarm</guid>
            <pubDate>Sat, 08 Jun 2024 21:01:00 GMT</pubDate>
            <description><![CDATA[A control plugin for drawing flowcharts in markdown syntax]]></description>
            <content:encoded><![CDATA[<table>
<thead>
<tr>
<th><a href="https://npmjs.com/package/markdown-it-diagram"><img src="https://img.shields.io/npm/v/markdown-it-diagram?style=flat&amp;colorA=080f12&amp;colorB=5e5e5e" alt="npm version"></a></th>
<th><a href="https://npmjs.com/package/markdown-it-diagram"><img src="https://img.shields.io/npm/dm/markdown-it-diagram?style=flat&amp;colorA=080f12&amp;colorB=5e5e5e" alt="npm downloads"></a></th>
<th><a href="https://bundlephobia.com/result?p=markdown-it-diagram"><img src="https://img.shields.io/bundlephobia/minzip/markdown-it-diagram?style=flat&amp;colorA=080f12&amp;colorB=5e5e5e&amp;label=minzip" alt="bundle"></a></th>
<th><a href="https://www.jsdocs.io/package/markdown-it-diagram"><img src="https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&amp;colorA=080f12&amp;colorB=5e5e5e" alt="JSDocs"></a></th>
<th><a href="https://github.com/ryanuo/markdown-it-diagram/blob/main/LICENSE"><img src="https://img.shields.io/github/license/rr210/markdown-it-diagram.svg?style=flat&amp;colorA=080f12&amp;colorB=5e5e5e" alt="License"></a></th>
</tr>
</thead>
</table>
<p><code>markdown-it-diagram </code>is a markdown-it plugin for creating diagrams in Markdown documents. It supports mermaid and plantuml, and also provides control functions such as zooming and moving.</p>
<h2>UML 示例</h2>
<p>Markdown supports：<a href="https://plantuml.com/">plantuml</a>、<a href="https://github.com/mermaid-js/mermaid">mermaid</a>、<a href="https://graphviz.gitlab.io/doc/info/lang.html">dot</a>、<a href="https://ditaa.sourceforge.net/">ditaa</a></p>
<h2>Features</h2>
<ul>
<li>Support PlantUML、Mermaid、Dot、Ditaa syntax;</li>
<li>Support zoom、move、rough、download、copy origin code and soon contorls;</li>
<li>Support Shift and mouse wheel to zoom in or out;</li>
<li>Support modal preview;</li>
<li>Support long press mouse click to drag the chart</li>
</ul>
<h3>PlantUML</h3>
<p><a href="https://www.plantuml.com/plantuml/uml/">try it</a></p>
<pre><code>```plantuml
Bob -&gt; Alice : Chào cô, chúc cô buổi trưa vui vẻ!
```
</code></pre>
<pre><code class="language-plantuml">Bob -&gt; Alice : Chào cô, chúc cô buổi trưa vui vẻ!
</code></pre>
<h3>DOT</h3>
<pre><code>```dot
digraph example1 {
    1 -&gt; 2 -&gt; { 4, 5 };
    1 -&gt; 3 -&gt; { 6, 7 };
}
```
</code></pre>
<pre><code class="language-dot">digraph example1 {
    1 -&gt; 2 -&gt; { 4, 5 };
    1 -&gt; 3 -&gt; { 6, 7 };
}
</code></pre>
<h3>ditaa</h3>
<blockquote>
<p><strong>Warning</strong>: In PlantUML, only PNG, ASCII Art image generation is supported.</p>
</blockquote>
<pre><code>```ditaa
+--------+   +-------+    +-------+
    |        | --+ ditaa +--&gt; |       |
    |  Text  |   +-------+    |diagram|
    |Document|   |!magic!|    |       |
    |     {d}|   |       |    |       |
    +---+----+   +-------+    +-------+
        :                         ^
        |       Lots of work      |
        +-------------------------+
```
</code></pre>
<pre><code class="language-ditaa">+--------+   +-------+    +-------+
    |        | --+ ditaa +--&gt; |       |
    |  Text  |   +-------+    |diagram|
    |Document|   |!magic!|    |       |
    |     {d}|   |       |    |       |
    +---+----+   +-------+    +-------+
        :                         ^
        |       Lots of work      |
        +-------------------------+
</code></pre>
<h3>mermaid</h3>
<pre><code>```mermaid
graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;
```
</code></pre>
<pre><code class="language-mermaid">graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;
</code></pre>
<h2>Installation</h2>
<pre><code class="language-bash">npm install markdown-it-diagram --save
</code></pre>
<h2>Usage</h2>
<h3>Vite Configuration</h3>
<pre><code class="language-ts">// vite.config.ts
import MarkdownItDiagrams from 'markdown-it-diagram'
import Markdown from 'unplugin-vue-markdown/vite'

export default defineConfig({
  plugins: [
    Markdown({
      markdownItSetup(md) {
        md.use(MarkdownItDiagrams, {
          showController: true, // show controller,default:false
          /**
           * PlantUML options
           * ditaa:imageFormat 'png| txt'
           * plantuml: imageFormat'png| svg| txt'
           * dot: imageFormat'png| svg| txt'
           */
          // imageFormat: 'svg', // image format:svg|png|txt,default:svg
          // server: '', // plantuml server,default:http://www.plantuml.com/plantuml
          // ditaa: {
          // imageFormat: 'svg', // image format:png|txt,default:svg
          // server: '', // plantuml server,default:http://www.plantuml.com/plantuml
          // }
        })
      }
    })
  ]
})
</code></pre>
<p>If you open the controller, you need to import the script in the initialization.<br>
vue3 example:</p>
<pre><code class="language-vue">&lt;script setup lang=&quot;ts&quot;&gt;
import { markdownItDiagramDom } from 'markdown-it-diagram/dom'
import { onMounted } from 'vue'

onMounted(async () =&gt; {
  // if you want to use mermaid, you need to install mermaid.js
  // npm install mermaid
  // import mermaid from 'mermaid'
  mermaid.initialize({ startOnLoad: false })
  await mermaid.run()
  // initialize markdown-it-diagram/dom script
  await markdownItDiagramDom()
})
&lt;/script&gt;
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Markdown-it Diagram：强大且灵活的流程图控件插件]]></title>
            <link>https://ryanuo.cc/zh/posts/md-it-diagarm</link>
            <guid>https://ryanuo.cc/zh/posts/md-it-diagarm</guid>
            <pubDate>Sat, 08 Jun 2024 21:01:00 GMT</pubDate>
            <description><![CDATA[markdown语法绘制流程图控件插件]]></description>
            <content:encoded><![CDATA[<table>
<thead>
<tr>
<th><a href="https://npmjs.com/package/markdown-it-diagram"><img src="https://img.shields.io/npm/v/markdown-it-diagram?style=flat&amp;colorA=080f12&amp;colorB=5e5e5e" alt="npm version"></a></th>
<th><a href="https://npmjs.com/package/markdown-it-diagram"><img src="https://img.shields.io/npm/dm/markdown-it-diagram?style=flat&amp;colorA=080f12&amp;colorB=5e5e5e" alt="npm downloads"></a></th>
<th><a href="https://bundlephobia.com/result?p=markdown-it-diagram"><img src="https://img.shields.io/bundlephobia/minzip/markdown-it-diagram?style=flat&amp;colorA=080f12&amp;colorB=5e5e5e&amp;label=minzip" alt="bundle"></a></th>
<th><a href="https://www.jsdocs.io/package/markdown-it-diagram"><img src="https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&amp;colorA=080f12&amp;colorB=5e5e5e" alt="JSDocs"></a></th>
<th><a href="https://github.com/ryanuo/markdown-it-diagram/blob/main/LICENSE"><img src="https://img.shields.io/github/license/rr210/markdown-it-diagram.svg?style=flat&amp;colorA=080f12&amp;colorB=5e5e5e" alt="License"></a></th>
</tr>
</thead>
</table>
<p><code>markdown-it-diagram</code> 是一个用于在 Markdown 文档中创建图表的 <code>markdown-it</code> 插件。它支持 mermaid 和 plantuml，同时提供了如缩放、移动等控件功能。</p>
<h2>UML 示例</h2>
<p>Markdown 支持：<a href="https://plantuml.com/">plantuml</a>、<a href="https://github.com/mermaid-js/mermaid">mermaid</a>、<a href="https://graphviz.gitlab.io/doc/info/lang.html">dot</a>、<a href="https://ditaa.sourceforge.net/">ditaa</a></p>
<h2>功能</h2>
<ul>
<li>支持PlantUML、Mermaid、Dot、Ditaa图形语法</li>
<li>包含缩放、移动、粗糙渲染、下载、复制源代码等快捷操作</li>
<li>支持Shift键结合鼠标滚轮进行缩放</li>
<li>集成模态窗口预览模式</li>
<li>实现长按鼠标左键拖动图表功能</li>
</ul>
<h3>PlantUML</h3>
<p><a href="https://www.plantuml.com/plantuml/uml/">在线尝试</a></p>
<pre><code>```plantuml
Bob -&gt; Alice : Chào cô, chúc cô buổi trưa vui vẻ!
```
</code></pre>
<pre><code class="language-plantuml">Bob -&gt; Alice : Chào cô, chúc cô buổi trưa vui vẻ!
</code></pre>
<h3>DOT</h3>
<pre><code>```dot
digraph example1 {
    1 -&gt; 2 -&gt; { 4, 5 };
    1 -&gt; 3 -&gt; { 6, 7 };
}
```
</code></pre>
<pre><code class="language-dot">digraph example1 {
    1 -&gt; 2 -&gt; { 4, 5 };
    1 -&gt; 3 -&gt; { 6, 7 };
}
</code></pre>
<h3>ditaa</h3>
<blockquote>
<p><strong>警告</strong>: 在 PlantUML 中，仅支持生成 PNG,ASCII Art 图片。</p>
</blockquote>
<pre><code>```ditaa
+--------+   +-------+    +-------+
    |        | --+ ditaa +--&gt; |       |
    |  Text  |   +-------+    |diagram|
    |Document|   |!magic!|    |       |
    |     {d}|   |       |    |       |
    +---+----+   +-------+    +-------+
        :                         ^
        |       Lots of work      |
        +-------------------------+
```
</code></pre>
<pre><code class="language-ditaa">+--------+   +-------+    +-------+
    |        | --+ ditaa +--&gt; |       |
    |  Text  |   +-------+    |diagram|
    |Document|   |!magic!|    |       |
    |     {d}|   |       |    |       |
    +---+----+   +-------+    +-------+
        :                         ^
        |       Lots of work      |
        +-------------------------+
</code></pre>
<h3>mermaid</h3>
<pre><code>```mermaid
graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;
```
</code></pre>
<pre><code class="language-mermaid">graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;
</code></pre>
<h2>安装</h2>
<pre><code class="language-bash">npm install markdown-it-diagram --save
</code></pre>
<h2>使用方法</h2>
<h3>Vite 配置</h3>
<pre><code class="language-ts">// vite.config.ts
import MarkdownItDiagrams from 'markdown-it-diagram'
import Markdown from 'unplugin-vue-markdown/vite'

export default defineConfig({
  plugins: [
    Markdown({
      markdownItSetup(md) {
        md.use(MarkdownItDiagrams, {
          showController: true, // show controller,default:false
          /**
           * PlantUML options
           * ditaa:imageFormat 'png| txt'
           * plantuml: imageFormat'png| svg| txt'
           * dot: imageFormat'png| svg| txt'
           */
          // imageFormat: 'svg', // image format:svg|png|txt,default:svg
          // server: '', // plantuml server,default:http://www.plantuml.com/plantuml
          // ditaa: {
          // imageFormat: 'svg', // image format:png|txt,default:svg
          // server: '', // plantuml server,default:http://www.plantuml.com/plantuml
          // }
        })
      }
    })
  ]
})
</code></pre>
<p>If you open the controller, you need to import the script in the initialization.<br>
vue3 example:</p>
<pre><code class="language-vue">&lt;script setup lang=&quot;ts&quot;&gt;
import { markdownItDiagramDom } from 'markdown-it-diagram/dom'
import { onMounted } from 'vue'

onMounted(async () =&gt; {
  // if you want to use mermaid, you need to install mermaid.js
  // npm install mermaid
  // import mermaid from 'mermaid'
  mermaid.initialize({ startOnLoad: false })
  await mermaid.run()
  // initialize markdown-it-diagram/dom script
  await markdownItDiagramDom()
})
&lt;/script&gt;
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Configuring Oh My Zsh through Git Bash on Windows]]></title>
            <link>https://ryanuo.cc/posts/zsh-windows</link>
            <guid>https://ryanuo.cc/posts/zsh-windows</guid>
            <pubDate>Sat, 01 Jun 2024 08:00:00 GMT</pubDate>
            <description><![CDATA[How to configure Oh My Zsh through Git Bash on Windows]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<p>Oh My Zsh enhances terminal usability in Unix environments. Here's a guide to configure it on Windows with Git Bash.</p>
<pre><code class="language-mermaid">graph TD
    A[Install Git Bash] --&gt; B[Download zsh]
    B --&gt; C[Extract zsh]
    C --&gt; D[Set zsh as default shell]
    D --&gt; E[Download install.sh for Oh-My-Zsh]
    E --&gt; F[Modify installation source in install.sh]
    F --&gt; G[Execute install.sh]
    G --&gt; H[Configure Oh-My-Zsh]
    H --&gt; I[Set theme in .zshrc]
    H --&gt; J[Download syntax highlighting plugin]
    H --&gt; K[Download autosuggestions plugin]
    I --&gt; L[Configure plugins in .zshrc]
    L --&gt; M[Apply configuration with source ~/.zshrc]
    H --&gt; N[Install Windows Terminal]
    N --&gt; O[Set Git Bash as default terminal in Windows Terminal]
    H --&gt; P[Configure IDE to use Git Bash]
</code></pre>
<h2>Step 1: Install Git Bash</h2>
<p>Download and install from the <a href="https://git-scm.com/downloads">Git website</a>.</p>
<h2>Step 2: Install zsh</h2>
<h3>1. Download zsh</h3>
<ul>
<li><a href="https://grr.lanzouo.com/iIgdg21xqqfe">Download zsh-5.9-2-x86_64.zip</a></li>
<li><a href="https://packages.msys2.org/package/zsh?repo=msys&amp;variant=x86_64">Download from official source</a></li>
<li><a href="https://peazip.github.io/">Download extraction tool</a></li>
</ul>
<h3>2. Install zsh</h3>
<p>Extract zsh and copy <code>etc</code> and <code>usr</code> directories to the Git installation directory (e.g., <code>C:\Program Files\Git</code>).</p>
<h3>3. Set zsh as the Default Shell</h3>
<p>Run <code>chsh -s $(which zsh)</code> or add the following to <code>~/.bashrc</code>:</p>
<pre><code class="language-sh">if [ -t 1 ]; then
  exec zsh
fi
</code></pre>
<h2>Step 3: Install Oh-My-Zsh</h2>
<h3>1. Download <code>install.sh</code></h3>
<p>Run:</p>
<pre><code class="language-sh">curl -# -O https://mirror.ghproxy.com/https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
</code></pre>
<h3>2. Modify Installation Source</h3>
<p>Edit <code>install.sh</code>:</p>
<pre><code class="language-sh">REPO=${REPO:-ohmyzsh/ohmyzsh}
REMOTE=${REMOTE:-https://mirror.ghproxy.com/https://github.com/${REPO}.git}
BRANCH=${BRANCH:-master}
</code></pre>
<h3>3. Execute the Script</h3>
<p>Run:</p>
<pre><code class="language-sh">sh install.sh
</code></pre>
<h2>Step 4: Configure Oh-My-Zsh</h2>
<h3>1. Set Theme</h3>
<p>Edit <code>~/.zshrc</code> to change <code>ZSH_THEME=&quot;robbyrussell&quot;</code> to <code>ZSH_THEME=&quot;ys&quot;</code>.</p>
<h3>2. Install Plugins</h3>
<p>Run:</p>
<pre><code class="language-sh">git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
</code></pre>
<h3>3. Configure Plugins</h3>
<p>Edit <code>~/.zshrc</code>:</p>
<pre><code class="language-sh">plugins=(git zsh-syntax-highlighting zsh-autosuggestions)
</code></pre>
<h3>4. Apply Configuration</h3>
<p>Run:</p>
<pre><code class="language-sh">source ~/.zshrc
</code></pre>
<h2>Step 5: Configure Windows Terminal</h2>
<h3>Installation</h3>
<p>Install from the Microsoft Store or <a href="https://learn.microsoft.com/zh-cn/windows/terminal/install">official documentation</a>.</p>
<h3>Configuration</h3>
<p>Set Windows Terminal to default to Git Bash.</p>
<h2>Step 6: Configure IDEs (e.g., IDEA, VSCode)</h2>
<p>Set the default terminal to Git Bash in the IDE settings.</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[通过 Git Bash 在 Windows 上配置 Oh My Zsh]]></title>
            <link>https://ryanuo.cc/zh/posts/zsh-windows</link>
            <guid>https://ryanuo.cc/zh/posts/zsh-windows</guid>
            <pubDate>Sat, 01 Jun 2024 08:00:00 GMT</pubDate>
            <description><![CDATA[如何在 Windows 上通过 Git Bash 配置 Oh My Zsh]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<p>在 Unix 环境中，Oh My Zsh 能极大提高终端易用性，并提供高度之定义功能。本文讲解在 Windows 下通过 Git Bash 配置 Oh My Zsh，实现让终端如在 Unix 环境下般便捷易用。</p>
<pre><code class="language-mermaid">graph TD
    A[安装 Git Bash] --&gt; B[下载 zsh]
    B --&gt; C[解压 zsh]
    C --&gt; D[设置 zsh 为默认 shell]
    D --&gt; E[下载 Oh-My-Zsh 的 install.sh]
    E --&gt; F[修改 install.sh 中的安装源]
    F --&gt; G[执行 install.sh]
    G --&gt; H[配置 Oh-My-Zsh]
    H --&gt; I[在 .zshrc 中设置主题]
    H --&gt; J[下载语法高亮插件]
    H --&gt; K[下载自动建议插件]
    I --&gt; L[在 .zshrc 中配置插件]
    L --&gt; M[通过 source ~/.zshrc 应用配置]
    H --&gt; N[安装 Windows Terminal]
    N --&gt; O[在 Windows Terminal 中设置 Git Bash 为默认终端]
    H --&gt; P[配置 IDE 使用 Git Bash]
</code></pre>
<h2>一、安装 Git Bash</h2>
<p>从 Git 官网下载并安装 Git Bash。<a href="https://git-scm.com/downloads">下载地址</a></p>
<h2>二、安装 zsh</h2>
<h3>1. 下载 zsh</h3>
<ul>
<li><a href="https://grr.lanzouo.com/iIgdg21xqqfe">下载 <code>zsh-5.9-2-x86_64.zip</code></a> 或从官方渠道下载最新版。</li>
<li><a href="https://packages.msys2.org/package/zsh?repo=msys&amp;variant=x86_64">下载 zsh</a></li>
<li><a href="https://peazip.github.io/">下载解压软件</a></li>
</ul>
<h3>2. 安装 zsh</h3>
<ul>
<li>解压 zsh 包，将 <code>etc</code> 和 <code>usr</code> 目录复制到 Git 安装目录（如 <code>C:\Program Files\Git</code>）。</li>
</ul>
<h3>3. 设置 zsh 为默认终端</h3>
<ul>
<li>执行 <code>chsh -s $(which zsh)</code> 或在 <code>~/.bashrc</code> 中添加以下配置：</li>
</ul>
<pre><code class="language-sh">if [ -t 1 ]; then
  exec zsh
fi
</code></pre>
<h2>三、安装 Oh-My-Zsh</h2>
<h3>1. 下载 <code>install.sh</code></h3>
<ul>
<li>执行命令：</li>
</ul>
<pre><code class="language-sh">curl -# -O https://mirror.ghproxy.com/https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh
</code></pre>
<h3>2. 修改安装源</h3>
<ul>
<li>编辑 <code>install.sh</code> 文件，将以下内容：</li>
</ul>
<pre><code class="language-sh">REPO=${REPO:-ohmyzsh/ohmyzsh}
REMOTE=${REMOTE:-https://github.com/${REPO}.git}
BRANCH=${BRANCH:-master}
</code></pre>
<p>修改为：</p>
<pre><code class="language-sh">REPO=${REPO:-ohmyzsh/ohmyzsh}
REMOTE=${REMOTE:-https://mirror.ghproxy.com/https://github.com/${REPO}.git}
BRANCH=${BRANCH:-master}
</code></pre>
<h3>3. 执行安装脚本</h3>
<ul>
<li>运行命令：</li>
</ul>
<pre><code class="language-sh">sh install.sh
</code></pre>
<h2>四、配置 Oh-My-Zsh</h2>
<h3>1. 配置主题</h3>
<ul>
<li>修改 <code>~/.zshrc</code> 文件，将 <code>ZSH_THEME=&quot;robbyrussell&quot;</code> 修改为 <code>ZSH_THEME=&quot;ys&quot;</code>。</li>
</ul>
<h3>2. 下载常用插件</h3>
<ul>
<li>语法高亮插件：</li>
</ul>
<pre><code class="language-sh">git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
</code></pre>
<ul>
<li>自动补全插件：</li>
</ul>
<pre><code class="language-sh">git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
</code></pre>
<h3>3. 配置插件</h3>
<ul>
<li>修改 <code>~/.zshrc</code> 文件，将 <code>plugins=(git)</code> 修改为：</li>
</ul>
<pre><code class="language-sh">plugins=(git zsh-syntax-highlighting zsh-autosuggestions)
</code></pre>
<h3>4. 使配置生效</h3>
<ul>
<li>执行命令：</li>
</ul>
<pre><code class="language-sh">source ~/.zshrc
</code></pre>
<h2>五、配置 Windows Terminal</h2>
<h3>安装</h3>
<ul>
<li>
<p>从微软商店安装 Windows Terminal 或从官网下载安装包。</p>
</li>
<li>
<p><a href="https://learn.microsoft.com/zh-cn/windows/terminal/install">安装文档</a></p>
</li>
<li>
<p>配置 Windows Terminal 默认打开 Git Bash。</p>
</li>
</ul>
<h2>六、配置 IDE（如 IDEA、VSCode）</h2>
<ul>
<li>在对应 IDE 软件设置中配置默认终端为 Git Bash。</li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[My API Showcase - Real-Time Data and Service Interfaces]]></title>
            <link>https://ryanuo.cc/posts/own-api</link>
            <guid>https://ryanuo.cc/posts/own-api</guid>
            <pubDate>Sat, 27 Apr 2024 08:00:00 GMT</pubDate>
            <description><![CDATA[own api,including oil price,weather,etc.]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>Oil Price</h2>
<OilPrice/>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[我的 API 展示页 - 实时数据与服务接口]]></title>
            <link>https://ryanuo.cc/zh/posts/own-api</link>
            <guid>https://ryanuo.cc/zh/posts/own-api</guid>
            <pubDate>Sat, 27 Apr 2024 08:00:00 GMT</pubDate>
            <description><![CDATA[own api,including oil price,weather,etc.]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>Oil Price</h2>
<OilPrice/>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Remember the problems you encountered when using swiper]]></title>
            <link>https://ryanuo.cc/posts/swiper</link>
            <guid>https://ryanuo.cc/posts/swiper</guid>
            <pubDate>Fri, 01 Mar 2024 17:08:00 GMT</pubDate>
            <description><![CDATA[swiper intercepts click events and the solution is to adjust the configuration of swiper to prevent swiper from intercepting click events of the select component.]]></description>
            <content:encoded><![CDATA[<h2>问题描述</h2>
<p>在使用Swiper实现内容滑动的Web应用中，Ant Design（antd）的Select组件下拉框无法被触发。</p>
<h2>问题复现</h2>
<ol>
<li>在Web页面中引入Swiper和Ant Design组件库。</li>
<li>在Swiper组件中添加Select组件。</li>
<li>尝试点击Select以展开下拉列表。</li>
</ol>
<pre><code class="language-tsx">&lt;Swiper&gt;
  &lt;SwiperSlide&gt;
    &lt;Select&gt;
      &lt;Select.Option value=&quot;1&quot;&gt;选项1&lt;/Select.Option&gt;
      &lt;Select.Option value=&quot;2&quot;&gt;选项2&lt;/Select.Option&gt;
    &lt;/Select&gt;
  &lt;/SwiperSlide&gt;
&lt;/Swiper&gt;
</code></pre>
<iframe src="https://codesandbox.io/p/devbox/ancient-rain-z75sgv?file=%2Fsrc%2FApp.tsx&embed=1"
     style="width:100%; height: 500px; border:0; border-radius: 4px; overflow:hidden;"
     title="ancient-rain"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>
<h2>发生原因</h2>
<ul>
<li>Swiper拦截了点击事件。</li>
</ul>
<h2>解决方法</h2>
<p>调整Swiper的配置<br>
防止Swiper拦截Select组件的点击事件。</p>
<pre><code class="language-ts">const swiper = new Swiper('.swiper-container', {
  simulateTouch: false,
  // 其他必要配置...
})
</code></pre>
<h3>参考文档</h3>
<p><a href="https://www.swiper.com.cn/api/touch/56.html">Swiper simulateTouch</a></p>
<p><a href="https://stackoverflow.com/questions/16997628/swiper-plugin-causes-select-can-not-pop-drop-down-box">swiper plugin causes select can not pop drop-down box</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[记一次使用swiper遇到的问题]]></title>
            <link>https://ryanuo.cc/zh/posts/swiper</link>
            <guid>https://ryanuo.cc/zh/posts/swiper</guid>
            <pubDate>Fri, 01 Mar 2024 17:08:00 GMT</pubDate>
            <description><![CDATA[Swiper拦截了点击事件，解决方法是调整Swiper的配置，防止Swiper拦截Select组件的点击事件。]]></description>
            <content:encoded><![CDATA[<h2>问题描述</h2>
<p>在使用Swiper实现内容滑动的Web应用中，Ant Design（antd）的Select组件下拉框无法被触发。</p>
<h2>问题复现</h2>
<ol>
<li>在Web页面中引入Swiper和Ant Design组件库。</li>
<li>在Swiper组件中添加Select组件。</li>
<li>尝试点击Select以展开下拉列表。</li>
</ol>
<pre><code class="language-tsx">&lt;Swiper&gt;
  &lt;SwiperSlide&gt;
    &lt;Select&gt;
      &lt;Select.Option value=&quot;1&quot;&gt;选项1&lt;/Select.Option&gt;
      &lt;Select.Option value=&quot;2&quot;&gt;选项2&lt;/Select.Option&gt;
    &lt;/Select&gt;
  &lt;/SwiperSlide&gt;
&lt;/Swiper&gt;
</code></pre>
<iframe src="https://codesandbox.io/p/devbox/ancient-rain-z75sgv?file=%2Fsrc%2FApp.tsx&embed=1"
     style="width:100%; height: 500px; border:0; border-radius: 4px; overflow:hidden;"
     title="ancient-rain"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>
<h2>发生原因</h2>
<ul>
<li>Swiper拦截了点击事件。</li>
</ul>
<h2>解决方法</h2>
<p>调整Swiper的配置<br>
防止Swiper拦截Select组件的点击事件。</p>
<pre><code class="language-ts">const swiper = new Swiper('.swiper-container', {
  simulateTouch: false,
  // 其他必要配置...
})
</code></pre>
<h3>参考文档</h3>
<p><a href="https://www.swiper.com.cn/api/touch/56.html">Swiper simulateTouch</a></p>
<p><a href="https://stackoverflow.com/questions/16997628/swiper-plugin-causes-select-can-not-pop-drop-down-box">swiper plugin causes select can not pop drop-down box</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Integrating Text Rendering Flowchart Plugins]]></title>
            <link>https://ryanuo.cc/posts/mermaid</link>
            <guid>https://ryanuo.cc/posts/mermaid</guid>
            <pubDate>Fri, 01 Mar 2024 15:30:00 GMT</pubDate>
            <description><![CDATA[Plant Uml Mermaid]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<style>
  img[alt="uml diagram"] {
            border: 1px dashed #000;
            max-width: 30%;
            height: auto;
            display: block;
            margin: 20px auto;
          border-radius: 6px;
 }
 .mermaid svg{
   margin: 20px auto;
 }
</style>
<h2>是什么？</h2>
<p>Mermaid 是一个基于JavaScript的图表和图表工具，可呈现Markdown启发的文本定义以动态创建和修改图表。<a href="https://mermaid.js.org/intro/">Docs</a></p>
<p>PlantUML 是一个多功能组件，可快速、直接地创建图表。<a href="https://plantuml.com/zh/guide">PDF</a></p>
<h2>调研选择</h2>
<h3>markdown-it-plantuml</h3>
<p><a href="https://www.npmjs.com/package/markdown-it-plantuml">github</a></p>
<pre><code class="language-ts">import plantuml from 'markdown-it-plantuml'
import Markdown from 'unplugin-vue-markdown/vite'

export default defineConfig({
  plugins: [
    Markdown({
      async markdownItSetup(md) {
        md.use(plantuml, {
          server: 'http://www.plantuml.com/plantuml',
        })
      },
    }),
  ],
})
</code></pre>
<p>这样就部署好了。<br>
但是因为这个插件是将PlantUML代码发送到PlantUML服务器，考虑到远程服务器可能会增加渲染图表的时间，并且服务器部署在国外（国内用户访问较慢，实际的体验不是很好）。<br>
想折腾可部署一个在线服务，使用<a href="https://plantuml.com/zh/starting">docker</a>部署</p>
<h3>markdown-it-textual-uml</h3>
<p><span style="color:#28437e">[推荐]</span></p>
<p>用于基于plantuml，mermaid等从文本创建uml图。<a href="https://github.com/manastalukdar/markdown-it-textual-uml">地址</a><br>
Mermaid相对于PlantUML而言，更简单易用且无需服务器，适合快速创建各种图表。</p>
<pre><code class="language-ts">import textualUml from 'markdown-it-textual-uml'
import Markdown from 'unplugin-vue-markdown/vite'

export default defineConfig({
  plugins: [
    Markdown({
      async markdownItSetup(md) {
        md.use(textualUml)
      },
    }),
  ],
})
</code></pre>
<pre><code>```plantuml
Bob -&gt; Alice : hello
```
</code></pre>
<pre><code class="language-plantuml">Bob -&gt; Alice : hello
</code></pre>
<p>使用<strong>mermaid</strong>的注意事项。<a href="https://github.com/manastalukdar/markdown-it-textual-uml?tab=readme-ov-file#additional-steps-for-mermaid">Here</a></p>
<pre><code>```mermaid
graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;
```
</code></pre>
<pre><code class="language-mermaid">graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[记一次接入文本渲染流程图插件]]></title>
            <link>https://ryanuo.cc/zh/posts/mermaid</link>
            <guid>https://ryanuo.cc/zh/posts/mermaid</guid>
            <pubDate>Fri, 01 Mar 2024 15:30:00 GMT</pubDate>
            <description><![CDATA[PlantUML, mermaid的实际应用]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<style>
  img[alt="uml diagram"] {
            border: 1px dashed #000;
            max-width: 30%;
            height: auto;
            display: block;
            margin: 20px auto;
          border-radius: 6px;
 }
 .mermaid svg{
   margin: 20px auto;
 }
</style>
<h2>是什么？</h2>
<p>Mermaid 是一个基于JavaScript的图表和图表工具，可呈现Markdown启发的文本定义以动态创建和修改图表。<a href="https://mermaid.js.org/intro/">Docs</a></p>
<p>PlantUML 是一个多功能组件，可快速、直接地创建图表。<a href="https://plantuml.com/zh/guide">PDF</a></p>
<h2>调研选择</h2>
<h3>markdown-it-plantuml</h3>
<p><a href="https://www.npmjs.com/package/markdown-it-plantuml">github</a></p>
<pre><code class="language-ts">import plantuml from 'markdown-it-plantuml'
import Markdown from 'unplugin-vue-markdown/vite'

export default defineConfig({
  plugins: [
    Markdown({
      async markdownItSetup(md) {
        md.use(plantuml, {
          server: 'http://www.plantuml.com/plantuml',
        })
      },
    }),
  ],
})
</code></pre>
<p>这样就部署好了。<br>
但是因为这个插件是将PlantUML代码发送到PlantUML服务器，考虑到远程服务器可能会增加渲染图表的时间，并且服务器部署在国外（国内用户访问较慢，实际的体验不是很好）。<br>
想折腾可部署一个在线服务，使用<a href="https://plantuml.com/zh/starting">docker</a>部署</p>
<h3>markdown-it-textual-uml</h3>
<p><span style="color:#28437e">[推荐]</span></p>
<p>用于基于plantuml，mermaid等从文本创建uml图。<a href="https://github.com/manastalukdar/markdown-it-textual-uml">地址</a><br>
Mermaid相对于PlantUML而言，更简单易用且无需服务器，适合快速创建各种图表。</p>
<pre><code class="language-ts">import textualUml from 'markdown-it-textual-uml'
import Markdown from 'unplugin-vue-markdown/vite'

export default defineConfig({
  plugins: [
    Markdown({
      async markdownItSetup(md) {
        md.use(textualUml)
      },
    }),
  ],
})
</code></pre>
<pre><code>```plantuml
Bob -&gt; Alice : hello
```
</code></pre>
<pre><code class="language-plantuml">Bob -&gt; Alice : hello
</code></pre>
<p>使用<strong>mermaid</strong>的注意事项。<a href="https://github.com/manastalukdar/markdown-it-textual-uml?tab=readme-ov-file#additional-steps-for-mermaid">Here</a></p>
<pre><code>```mermaid
graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;
```
</code></pre>
<pre><code class="language-mermaid">graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Adaptive large screen web page design solution]]></title>
            <link>https://ryanuo.cc/posts/big-screen</link>
            <guid>https://ryanuo.cc/posts/big-screen</guid>
            <pubDate>Thu, 15 Feb 2024 20:38:09 GMT</pubDate>
            <description><![CDATA[in responsive web design there are various ways to ensure that elements are adaptable across different resolutions and devices]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>在响应式Web设计中，有多种方法可以确保元素在不同分辨率和设备上的适应性。以下是三种流行的设计方案和它们的实现方法：</p>
</blockquote>
<h2>方案一：使用vw和vh单位</h2>
<h3>基本概念</h3>
<ul>
<li><code>vw</code> (viewport width) 是相对于视口宽度的单位，100vw 等于视口宽度的100%。</li>
<li><code>vh</code> (viewport height) 是相对于视口高度的单位，100vh 等于视口高度的100%。</li>
</ul>
<h3>实现方法</h3>
<p>以设计稿尺寸为 <code>1920x1080</code> 为例，要把px单位转化为vw或vh，公式如下：</p>
<pre><code>vwDiv = (300px / 1920px) * 100vw
vhDiv = (200px / 1080px) * 100vh
</code></pre>
<h3>注意事项</h3>
<ul>
<li>需要对屏幕尺寸的变化进行监听，并在变化时重新计算元素的大小。</li>
<li>在Vue项目中，可以使用<code>element-resize-detector</code>或自定义的resize指令，帮助自动管理尺寸变化。</li>
</ul>
<h2>方案二：使用CSS的scale属性</h2>
<h3>核心思路</h3>
<p>通过CSS的<code>transform: scale(x)</code>属性对页面进行等比缩放。</p>
<h3>实现逻辑</h3>
<ul>
<li>当屏幕尺寸保持16:9时，内容全屏显示。</li>
<li>当屏幕宽高比小于16:9时，页面上下留白，内容比例为16:9。</li>
<li>当屏幕宽高比大于16:9时，页面左右留白，内容比例为16:9。</li>
</ul>
<h2>方案三：结合rem单位和vw/vh单位</h2>
<h3>实施步骤</h3>
<ol>
<li>确定rem的基准值。</li>
<li>编写JavaScript代码动态地设置<code>html</code>元素的<code>font-size</code>。</li>
<li>监听屏幕尺寸变化，确保元素随着屏幕大小自动调整以及字体，间距，和位移等的自适应性。</li>
</ol>
<h3>示例代码</h3>
<pre><code class="language-javascript">document.addEventListener('DOMContentLoaded', () =&gt; {
  const baseWidth = 1920 // 设计稿宽度
  const baseFontSize = 100 // 基准font-size
  const scale = document.documentElement.clientWidth / baseWidth
  document.documentElement.style.fontSize = `${baseFontSize * scale}px`
})

window.addEventListener('resize', () =&gt; {
  // 重新计算并设置 font-size
})
</code></pre>
<h2>解决大屏问题的库</h2>
<p><a href="https://github.com/jp-liu/fit-screen">fit-screen</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[自适应大屏Web页面设计方案]]></title>
            <link>https://ryanuo.cc/zh/posts/big-screen</link>
            <guid>https://ryanuo.cc/zh/posts/big-screen</guid>
            <pubDate>Thu, 15 Feb 2024 20:38:09 GMT</pubDate>
            <description><![CDATA[在响应式Web设计中，有多种方法可以确保元素在不同分辨率和设备上的适应性]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>在响应式Web设计中，有多种方法可以确保元素在不同分辨率和设备上的适应性。以下是三种流行的设计方案和它们的实现方法：</p>
</blockquote>
<h2>方案一：使用vw和vh单位</h2>
<h3>基本概念</h3>
<ul>
<li><code>vw</code> (viewport width) 是相对于视口宽度的单位，100vw 等于视口宽度的100%。</li>
<li><code>vh</code> (viewport height) 是相对于视口高度的单位，100vh 等于视口高度的100%。</li>
</ul>
<h3>实现方法</h3>
<p>以设计稿尺寸为 <code>1920x1080</code> 为例，要把px单位转化为vw或vh，公式如下：</p>
<pre><code>vwDiv = (300px / 1920px) * 100vw
vhDiv = (200px / 1080px) * 100vh
</code></pre>
<h3>注意事项</h3>
<ul>
<li>需要对屏幕尺寸的变化进行监听，并在变化时重新计算元素的大小。</li>
<li>在Vue项目中，可以使用<code>element-resize-detector</code>或自定义的resize指令，帮助自动管理尺寸变化。</li>
</ul>
<h2>方案二：使用CSS的scale属性</h2>
<h3>核心思路</h3>
<p>通过CSS的<code>transform: scale(x)</code>属性对页面进行等比缩放。</p>
<h3>实现逻辑</h3>
<ul>
<li>当屏幕尺寸保持16:9时，内容全屏显示。</li>
<li>当屏幕宽高比小于16:9时，页面上下留白，内容比例为16:9。</li>
<li>当屏幕宽高比大于16:9时，页面左右留白，内容比例为16:9。</li>
</ul>
<h2>方案三：结合rem单位和vw/vh单位</h2>
<h3>实施步骤</h3>
<ol>
<li>确定rem的基准值。</li>
<li>编写JavaScript代码动态地设置<code>html</code>元素的<code>font-size</code>。</li>
<li>监听屏幕尺寸变化，确保元素随着屏幕大小自动调整以及字体，间距，和位移等的自适应性。</li>
</ol>
<h3>示例代码</h3>
<pre><code class="language-javascript">document.addEventListener('DOMContentLoaded', () =&gt; {
  const baseWidth = 1920 // 设计稿宽度
  const baseFontSize = 100 // 基准font-size
  const scale = document.documentElement.clientWidth / baseWidth
  document.documentElement.style.fontSize = `${baseFontSize * scale}px`
})

window.addEventListener('resize', () =&gt; {
  // 重新计算并设置 font-size
})
</code></pre>
<h2>解决大屏问题的库</h2>
<p><a href="https://github.com/jp-liu/fit-screen">fit-screen</a></p>
<p><a href="https://juejin.cn/post/7202598910337138748">参考文献</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Frontend npm CDN Alternative Solutions Overview]]></title>
            <link>https://ryanuo.cc/posts/npm-cdn</link>
            <guid>https://ryanuo.cc/posts/npm-cdn</guid>
            <pubDate>Thu, 01 Feb 2024 15:30:00 GMT</pubDate>
            <description><![CDATA[Detailed Overview of Domestic Frontend npm CDN Alternative Solutions, including Classic Established CDN Acceleration, Domestic Available CDNs, and Comparatively Recommended CDNs, to assist developers in better choosing suitable CDN acceleration services.]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>经典老牌的CDN加速</h2>
<ul>
<li><a href="https://unpkg.com">unpkg.com</a></li>
<li><a href="https://cdn.jsdelivr.net">cdn.jsdelivr.net</a></li>
<li><a href="https://fastly.jsdelivr.net">fastly.jsdelivr.net</a></li>
</ul>
<p>使用方法：直接进官网，搜NPM包名使用。</p>
<p>缺点：有时候不是很稳定，而且国内有些地方没法访问。</p>
<h2>国内能用的CDN</h2>
<ul>
<li><a href="https://www.bootcdn.cn">BootCDN</a></li>
<li><a href="https://www.staticfile.org">七牛云</a></li>
<li><a href="http://cdn.baomitu.com">360</a></li>
<li><a href="https://cdn.bytedance.com">字节跳动</a></li>
</ul>
<p>使用方法：直接进官网，搜NPM包名使用。</p>
<p>缺点：这些CDN并不全，某些NPM包在这些CDN上找不到。</p>
<h2>比较推荐的CDN</h2>
<ul>
<li>知乎: <a href="https://unpkg.zhimg.com">unpkg.zhimg.com</a></li>
<li><a href="https://jsd.onmicrosoft.cn">jsd.onmicrosoft.cn</a> (回源jsDelivr)</li>
<li><a href="https://npm.onmicrosoft.cn">npm.onmicrosoft.cn</a> (回源UNPKG)</li>
<li><a href="https://cdnjs.onmicrosoft.cn">cdnjs.onmicrosoft.cn</a> (回源cdnjs)</li>
</ul>
<p>使用方法：参考unpkg、jsdelivr等的使用方法，更换CDN的域名即可。</p>
<p>优点：CDN包更全一些，能搜到的NPM包与unpkg上的基本一致。</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[前端npm CDN替代方案盘点]]></title>
            <link>https://ryanuo.cc/zh/posts/npm-cdn</link>
            <guid>https://ryanuo.cc/zh/posts/npm-cdn</guid>
            <pubDate>Thu, 01 Feb 2024 15:30:00 GMT</pubDate>
            <description><![CDATA[国内前端npm CDN替代方案的详细盘点，包括经典老牌CDN加速、国内可用的CDN以及比较推荐的CDN，帮助开发者更好地选择合适的CDN加速服务。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>经典老牌的CDN加速</h2>
<ul>
<li><a href="https://unpkg.com">unpkg.com</a></li>
<li><a href="https://cdn.jsdelivr.net">cdn.jsdelivr.net</a></li>
<li><a href="https://fastly.jsdelivr.net">fastly.jsdelivr.net</a></li>
</ul>
<p>使用方法：直接进官网，搜NPM包名使用。</p>
<p>缺点：有时候不是很稳定，而且国内有些地方没法访问。</p>
<h2>国内能用的CDN</h2>
<ul>
<li><a href="https://www.bootcdn.cn">BootCDN</a></li>
<li><a href="https://www.staticfile.org">七牛云</a></li>
<li><a href="http://cdn.baomitu.com">360</a></li>
<li><a href="https://cdn.bytedance.com">字节跳动</a></li>
</ul>
<p>使用方法：直接进官网，搜NPM包名使用。</p>
<p>缺点：这些CDN并不全，某些NPM包在这些CDN上找不到。</p>
<h2>比较推荐的CDN</h2>
<ul>
<li>知乎: <a href="https://unpkg.zhimg.com">unpkg.zhimg.com</a></li>
<li><a href="https://jsd.onmicrosoft.cn">jsd.onmicrosoft.cn</a> (回源jsDelivr)</li>
<li><a href="https://npm.onmicrosoft.cn">npm.onmicrosoft.cn</a> (回源UNPKG)</li>
<li><a href="https://cdnjs.onmicrosoft.cn">cdnjs.onmicrosoft.cn</a> (回源cdnjs)</li>
</ul>
<p>使用方法：参考unpkg、jsdelivr等的使用方法，更换CDN的域名即可。</p>
<p>优点：CDN包更全一些，能搜到的NPM包与unpkg上的基本一致。</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[File suffix issue when Chrome downloads files]]></title>
            <link>https://ryanuo.cc/posts/file-download</link>
            <guid>https://ryanuo.cc/posts/file-download</guid>
            <pubDate>Sun, 01 Oct 2023 09:09:09 GMT</pubDate>
            <description><![CDATA[File suffix issue when Chrome downloads files]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h3>Problem Description</h3>
<p>When handling Blob files and triggering downloads, if the file name specified for download does not include a file suffix, the downloaded file may lack an extension, which can affect its recognizability and usability.</p>
<h3>Default Browser Behavior</h3>
<ul>
<li>When a browser detects that the file name includes a suffix during download, it usually does not modify or process the file name.</li>
<li>The browser determines whether the file name includes a suffix by checking if there is a decimal part (i.e., file extension) after a dot (.) in the file name. If such a decimal part exists, the browser will not automatically append a suffix.</li>
</ul>
<p>If the file name does not explicitly include a suffix, the browser may automatically append one based on the following scenarios:</p>
<ol>
<li>
<p>HTTP Response Content-Disposition Header<br>
If the server explicitly specifies the file name and suffix via the Content-Disposition header, the browser will use that name.</p>
<p><code>Content-Disposition: attachment; filename=&quot;report.pdf&quot;</code></p>
</li>
<li>
<p>Response Content-Type Header<br>
Based on the MIME type of the response, some browsers may attempt to assign an appropriate file suffix.</p>
<p><code>Content-Type: application/pdf</code></p>
</li>
<li>
<p>URL Path Contains File Name<br>
If the URL path of the download appears to include a file name and suffix, the browser may use that part as the file name for the download.</p>
<p><code>https://example.com/download/receipt.pdf</code></p>
</li>
<li>
<p>Text or Attribute of the Download Link<br>
In HTML, the <code>download</code> attribute of a download link (e.g., <code>&lt;a&gt;</code> element) can specify the file name. If a file name and suffix are provided, the browser will use that name for the download.</p>
</li>
</ol>
<h3>Recommended Measures</h3>
<p>To ensure that the downloaded file includes the correct suffix, the following measures are recommended:</p>
<ul>
<li>On the server side, set the Content-Disposition and Content-Type headers to ensure that the file name and MIME type are explicitly specified.</li>
<li>On the client side, when generating Blob files and creating download links via JavaScript, ensure that the file suffix is included when setting the download attribute.</li>
</ul>
<p>Example Code</p>
<pre><code class="language-javascript">// Assume you have some data to download
const data = ''

// Create a Blob object representing the data and specify the MIME type
const blob = new Blob([data], { type: 'application/pdf' })

// Generate a URL pointing to the Blob
const url = URL.createObjectURL(blob)

// Create a link element for downloading and set the file name with a suffix
const link = document.createElement('a')
link.href = url
link.download = 'example.pdf' // Ensure the file name includes a suffix
document.body.appendChild(link)

// Trigger the download
link.click()

// Cleanup: Remove the link element and revoke the Blob URL
document.body.removeChild(link)
URL.revokeObjectURL(url)
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Chrome下载文件时文件后缀问题]]></title>
            <link>https://ryanuo.cc/zh/posts/file-download</link>
            <guid>https://ryanuo.cc/zh/posts/file-download</guid>
            <pubDate>Sun, 01 Oct 2023 09:09:09 GMT</pubDate>
            <description><![CDATA[Chrome下载文件时文件后缀问题]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h3>问题描述</h3>
<p>在处理Blob文件并触发下载时,如果在指定下载文件的名称时未包含文件后缀，可能会导致下载的文件缺失扩展名，从而影响文件的可识别性和可用性。</p>
<h3>浏览器的默认行为</h3>
<ul>
<li>当浏览器在下载文件时识别到文件名中包含后缀，它通常不会对文件名进行任何修改或处理。</li>
<li>浏览器判断文件名是否包含后缀的依据是检查文件名中是否存在点（.）后的小数位（即文件扩展名）。如果文件名中已经包含此类小数位，则浏览器不会自动添加后缀。</li>
</ul>
<p>在没有明确文件后缀的情况下，浏览器可能根据以下几种情况自动为文件名拼接后缀：</p>
<ol>
<li>
<p>HTTP响应的Content-Disposition头部<br>
如果服务器通过Content-Disposition头部明确指定了文件名和后缀，浏览器将采用该名称。</p>
<p><code>Content-Disposition: attachment; filename=&quot;report.pdf&quot;</code></p>
</li>
<li>
<p>响应的Content-Type头部<br>
根据响应的MIME类型，某些浏览器可能会尝试分配一个合适的文件后缀。</p>
<p><code>Content-Type: application/pdf</code></p>
</li>
<li>
<p>URL路径包含文件名<br>
如果下载的URL路径看起来像是包含文件名和后缀，浏览器可能会使用该部分作为下载的文件名。</p>
<p><code>https://example.com/download/receipt.pdf</code></p>
</li>
<li>
<p>下载链接的文本或标签属性<br>
在HTML中，下载链接（比如<code>&lt;a&gt;</code>元素）的download属性可以指定下载的文件名。如果提供了文件名和后缀，浏览器会使用该名称进行下载。</p>
</li>
</ol>
<h3>推荐措施</h3>
<p>为了确保文件下载时包含正确的后缀，建议采取以下措施：</p>
<ul>
<li>在服务器端设置Content-Disposition和Content-Type头部，确保文件名和MIME类型被明确指定。</li>
<li>在客户端，通过JavaScript生成Blob文件并创建下载链接时，确保在设置下载属性时包含了文件后缀名。</li>
</ul>
<p>示例代码</p>
<pre><code class="language-javascript">// 假设你有一些要下载的数据
const data = ''

// 创建一个表示该数据的Blob对象，并指定MIME类型
const blob = new Blob([data], { type: 'application/pdf' })

// 生成一个指向该Blob的URL
const url = URL.createObjectURL(blob)

// 创建一个用于下载的链接元素，并设置文件名及后缀
const link = document.createElement('a')
link.href = url
link.download = 'example.pdf' // 确保文件名包含后缀
document.body.appendChild(link)

// 触发下载
link.click()

// 清理：移除链接元素，释放Blob URL
document.body.removeChild(link)
URL.revokeObjectURL(url)
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[A Complete Guide to Patching with pnpm]]></title>
            <link>https://ryanuo.cc/posts/pnpm-pkg</link>
            <guid>https://ryanuo.cc/posts/pnpm-pkg</guid>
            <pubDate>Tue, 01 Aug 2023 21:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to use pnpm to patch and fix issues in third-party libraries within front-end projects.]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h3>Patching with pnpm</h3>
<p>Patching is an essential skill in front-end development, allowing you to modify the code of third-party libraries.</p>
<h3>Why Patching is Necessary</h3>
<p>In project development, we often use third-party libraries. While most third-party libraries are reliable, they may still contain bugs or require optimization. Common ways to address issues in third-party libraries include:</p>
<ul>
<li>Replacing the library: However, the new library may not necessarily be better than the original one;</li>
<li>Submitting an issue: Waiting for the author to respond, which may not be feasible when time is tight;</li>
<li>Submitting a PR: The author may not merge it immediately;</li>
<li>Patching: Making local modifications that only affect our project without waiting for the author's review.</li>
</ul>
<h3>Using pnpm for Patching</h3>
<pre><code class="language-mermaid">sequenceDiagram
    participant Developer
    participant pnpm

    Developer -&gt;&gt; pnpm: pnpm patch pkg
    pnpm --&gt;&gt; Developer: Enter the package content for modification
    Developer -&gt;&gt; pnpm: Modify the code
    pnpm --&gt;&gt; Developer: Modification completed
    Developer -&gt;&gt; pnpm: pnpm patch-commit path
    pnpm --&gt;&gt; Developer: Commit the changes
    Developer -&gt;&gt; pnpm: Reinstall dependencies
    pnpm --&gt;&gt; Developer: Apply the modifications
</code></pre>
<p>Compared to yarn and npm, pnpm natively supports patching without requiring additional libraries. The specific steps are as follows:</p>
<ol>
<li>Use <code>pnpm patch pkg</code> to enter the content of the package for modification;</li>
<li>After modifying the code, use <code>pnpm patch-commit path</code> to commit the changes;</li>
<li>Once completed, reinstall the dependencies, and pnpm will automatically read the content from the patch folder.</li>
</ol>
<p>For example, when using a third-party library, suppose a specific function or method in the library contains a bug or does not meet our requirements. In this case, we can use patching to modify it. Assume we are using a third-party library called <code>awesome-library</code> in our project, but its function <code>doSomething()</code> has a bug that prevents our application from working properly. To resolve this issue, we can use pnpm's patching feature as follows:</p>
<ol>
<li>First, use the command <code>pnpm patch awesome-library</code> to enter the content of the <code>awesome-library</code> package for modification.</li>
<li>Locate and fix the bug in the <code>doSomething()</code> function.</li>
<li>Commit the changes using the command <code>pnpm patch-commit path</code>.</li>
<li>Once completed, reinstall the dependencies, and pnpm will automatically read the content from the patch folder and apply our modifications.</li>
</ol>
<p>In this way, we can fix or optimize issues in the library to meet the needs of our project without affecting the original library.</p>
<h3>Summary</h3>
<p>Patching is an effective way to address issues in third-party libraries and improve front-end development efficiency. By patching, we can quickly fix problems without waiting for the author's response.</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[全面指南：如何使用 pnpm 打补丁]]></title>
            <link>https://ryanuo.cc/zh/posts/pnpm-pkg</link>
            <guid>https://ryanuo.cc/zh/posts/pnpm-pkg</guid>
            <pubDate>Tue, 01 Aug 2023 21:00:00 GMT</pubDate>
            <description><![CDATA[学习如何使用 pnpm 在前端项目中打补丁修复第三方库问题]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h3>pnpm 打补丁</h3>
<p>打补丁是前端开发中必备的技能之一，通过这种方式可以修改第三方库的代码。</p>
<h3>为什么需要打补丁</h3>
<p>在项目开发中，我们经常会使用第三方库。尽管大多数第三方库都很可靠，但仍然可能存在 bug 或需要优化的情况。因此，解决第三方库问题的常见方式包括：</p>
<ul>
<li>更换库：但新库未必比原库更好；</li>
<li>提交 issue：等待作者回应，但时间紧迫时无法等待；</li>
<li>提交 PR：作者可能无法立即合并；</li>
<li>打补丁：本地修改，仅在我们的项目中生效，无需等待作者审核。</li>
</ul>
<h3>使用 pnpm 打补丁</h3>
<pre><code class="language-mermaid">sequenceDiagram
    participant Developer
    participant pnpm

    Developer -&gt;&gt; pnpm: pnpm patch pkg
    pnpm --&gt;&gt; Developer: 进入对应包的内容进行修改
    Developer -&gt;&gt; pnpm: 修改代码
    pnpm --&gt;&gt; Developer: 修改完成
    Developer -&gt;&gt; pnpm: pnpm patch-commit path
    pnpm --&gt;&gt; Developer: 提交修改
    Developer -&gt;&gt; pnpm: 重新安装依赖
    pnpm --&gt;&gt; Developer: 应用修改完成
</code></pre>
<p>相比于 yarn 和 npm，pnpm 默认支持打补丁功能，无需额外安装库。具体步骤如下：</p>
<ol>
<li>使用 <code>pnpm patch pkg</code> 进入对应包的内容进行修改；</li>
<li>修改代码后，使用 <code>pnpm patch-commit path</code> 提交修改；</li>
<li>完成后，重新安装依赖，pnpm 将自动读取 patch 文件夹下的内容。</li>
</ol>
<p>例如，当使用一个第三方库时，假设该库中有一个特定的函数或方法存在 bug 或者不符合我们的需求，这时我们可以通过打补丁来修改它。假设我们在项目中使用了一个名为 <code>awesome-library</code> 的第三方库，但它的某个函数 <code>doSomething()</code> 存在一个 bug，导致我们的应用无法正常工作。为了解决这个问题，我们可以使用 pnpm 打补丁的方式进行修复：</p>
<ol>
<li>首先，使用命令 <code>pnpm patch awesome-library</code> 进入 <code>awesome-library</code> 包的内容进行修改。</li>
<li>找到并修复 <code>doSomething()</code> 函数中的 bug。</li>
<li>提交修改，使用命令 <code>pnpm patch-commit path</code> 来提交所做的更改。</li>
<li>完成后，重新安装依赖，pnpm 将自动读取 patch 文件夹下的内容，并应用我们的修改。</li>
</ol>
<p>通过这种方式，我们可以在不影响原始库的情况下，对其中的问题进行修复或优化，以满足我们项目的需求。</p>
<h3>总结</h3>
<p>打补丁是解决第三方库问题的有效方式，可以提升前端开发效率。通过打补丁，我们可以快速修复问题，而不必等待作者的响应。</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Connect the document to the algolia search plugin]]></title>
            <link>https://ryanuo.cc/posts/algolia</link>
            <guid>https://ryanuo.cc/posts/algolia</guid>
            <pubDate>Tue, 30 May 2023 16:47:08 GMT</pubDate>
            <description><![CDATA[integrate algolia search into your documents with algolia you can provide powerful search capabilities for your documents or websites]]></description>
            <content:encoded><![CDATA[<h2>开始之前</h2>
<p>在开始之前，请确保你有一个有效的Algolia帐户。如果没有，请先创建一个。</p>
<h2>集成步骤</h2>
<pre><code class="language-mermaid">graph TD;
A[创建Algolia帐户] --&gt; B[创建一个新的索引]
B --&gt; C[获取API密钥]
C --&gt; D[集成Algolia到你的文档]
D --&gt; E[索引你的文档]
E --&gt; F[在你的文档中添加搜索功能]
</code></pre>
<h3>1.创建Algolia帐户</h3>
<ol>
<li>访问 <a href="https://www.algolia.com/">Algolia官网</a>。</li>
<li>点击&quot;Sign Up&quot;注册，按照页面提示完成注册流程。</li>
</ol>
<h3>2.创建一个新的索引</h3>
<ol>
<li>登录你的Algolia帐户。</li>
<li>导航到&quot;Indices&quot;页面，点击&quot;Create Index&quot;。</li>
<li>给你的索引起一个名字，并确认创建。</li>
</ol>
<h3>3.获取API密钥</h3>
<ol>
<li>在Algolia仪表盘中，点击&quot;API Keys&quot;。</li>
<li>记录下你的<code>Application ID</code>、<code>Search-Only API Key</code>和<code>Admin API Key</code>。</li>
</ol>
<h3>4.集成Algolia到你的文档</h3>
<p>集成方法取决于你文档的托管方式，这里我以我的文档为例，我使用了开源的DocSearch插件。</p>
<h4>集成DocSearch到Vue 3项目</h4>
<p>1.申请DocSearch</p>
<p>在集成之前，你需要为你的文档申请DocSearch。访问 <a href="https://docsearch.algolia.com/apply/">DocSearch 申请页面</a>，按照指示填写并提交表单。一旦你的申请被接受，Algolia 的团队将为你的文档创建一个索引，并发送给你一个唯一的 <code>API key</code> 和 <code>index name</code>。</p>
<p>2.安装DocSearch依赖</p>
<p>在你的Vue 3项目中，安装DocSearch的依赖。</p>
<pre><code class="language-bash">pnpm install @docsearch/js@alpha
# 注意引用官方的组件，需要将样式依赖也安装好
pnpm install @docsearch/css
</code></pre>
<p>编写组件，可自定义也可直接使用DocSearch官方提供的组件。这里我借鉴<a href="https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/components/VPAlgoliaSearchBox.vue">vitePress</a>定义的AlgoliaSearch组件，下面展示为主要的代码</p>
<pre><code class="language-vue">&lt;script setup lang=&quot;ts&quot;&gt;
import docsearch from '@docsearch/js'
import '@docsearch/css'

const props = defineProps&lt;{
  algolia: AlgoliaSearchOptions
}&gt;()

onMounted(() =&gt; {
  docsearch(algolia)
})
&lt;/script&gt;

&lt;template&gt;
  &lt;div id=&quot;docsearch&quot; /&gt;
&lt;/template&gt;
</code></pre>
<p>这里我使用的时候覆盖了原有的样式,如下</p>
<pre><code class="language-vue">&lt;template&gt;
  &lt;a href=&quot; &quot; class=&quot;relative&quot;&gt;
    &lt;div class=&quot;i-mynaui-search-square h-1.3em w-1.3em&quot; /&gt;
    &lt;AlgoliaSearchBox
      class=&quot;absolute left-0 top-0 h-full w-full op0!&quot;
      :algolia=&quot;{
        apiKey: 'db0e9b82d77e75c9fc8aee05b1e14334',
        indexName: 'ryan',
        appId: 'X0NE0GCGVB',
      }&quot;
    /&gt;
  &lt;/a&gt;
&lt;/template&gt;

&lt;style&gt;
:root {
  /* docsearch */
  --docsearch-primary-color: #313237 !important;
  --docsearch-logo-color: #313237 !important;
  --docsearch-text-color: #9d9fa1 !important;
}

.cls-1,
.cls-2 {
  fill: #060606 !important;
}

.DocSearch-Button-Placeholder,
span.DocSearch-Button-Keys {
  display: none;
}

button.DocSearch-Button {
  margin: 0;
  background: transparent;
  height: auto;
  padding: 0;
}

button.DocSearch-Button:active,
button.DocSearch-Button:focus,
button.DocSearch-Button:hover {
  background: transparent;
  box-shadow: none;
  color: var(--c-hover-color);
}
&lt;/style&gt;
</code></pre>
<h3>5.编写爬虫的规则</h3>
<p><a href="https://github.com/ryanuo/docs-crawler">爬虫规则</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[给文档接入Algolia搜索插件]]></title>
            <link>https://ryanuo.cc/zh/posts/algolia</link>
            <guid>https://ryanuo.cc/zh/posts/algolia</guid>
            <pubDate>Tue, 30 May 2023 16:47:08 GMT</pubDate>
            <description><![CDATA[将Algolia搜索功能集成到你的文档中。通过Algolia，你可以为你的文档或网站提供强大的搜索能力。]]></description>
            <content:encoded><![CDATA[<h2>开始之前</h2>
<p>在开始之前，请确保你有一个有效的Algolia帐户。如果没有，请先创建一个。</p>
<h2>集成步骤</h2>
<pre><code class="language-mermaid">graph TD;
A[创建Algolia帐户] --&gt; B[创建一个新的索引]
B --&gt; C[获取API密钥]
C --&gt; D[集成Algolia到你的文档]
D --&gt; E[索引你的文档]
E --&gt; F[在你的文档中添加搜索功能]
</code></pre>
<h3>1.创建Algolia帐户</h3>
<ol>
<li>访问 <a href="https://www.algolia.com/">Algolia官网</a>。</li>
<li>点击&quot;Sign Up&quot;注册，按照页面提示完成注册流程。</li>
</ol>
<h3>2.创建一个新的索引</h3>
<ol>
<li>登录你的Algolia帐户。</li>
<li>导航到&quot;Indices&quot;页面，点击&quot;Create Index&quot;。</li>
<li>给你的索引起一个名字，并确认创建。</li>
</ol>
<h3>3.获取API密钥</h3>
<ol>
<li>在Algolia仪表盘中，点击&quot;API Keys&quot;。</li>
<li>记录下你的<code>Application ID</code>、<code>Search-Only API Key</code>和<code>Admin API Key</code>。</li>
</ol>
<h3>4.集成Algolia到你的文档</h3>
<p>集成方法取决于你文档的托管方式，这里我以我的文档为例，我使用了开源的DocSearch插件。</p>
<h4>集成DocSearch到Vue 3项目</h4>
<p>1.申请DocSearch</p>
<p>在集成之前，你需要为你的文档申请DocSearch。访问 <a href="https://docsearch.algolia.com/apply/">DocSearch 申请页面</a>，按照指示填写并提交表单。一旦你的申请被接受，Algolia 的团队将为你的文档创建一个索引，并发送给你一个唯一的 <code>API key</code> 和 <code>index name</code>。</p>
<p>2.安装DocSearch依赖</p>
<p>在你的Vue 3项目中，安装DocSearch的依赖。</p>
<pre><code class="language-bash">pnpm install @docsearch/js@alpha
# 注意引用官方的组件，需要将样式依赖也安装好
pnpm install @docsearch/css
</code></pre>
<p>编写组件，可自定义也可直接使用DocSearch官方提供的组件。这里我借鉴<a href="https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/components/VPAlgoliaSearchBox.vue">vitePress</a>定义的AlgoliaSearch组件，下面展示为主要的代码</p>
<pre><code class="language-vue">&lt;script setup lang=&quot;ts&quot;&gt;
import docsearch from '@docsearch/js'
import '@docsearch/css'

const props = defineProps&lt;{
  algolia: AlgoliaSearchOptions
}&gt;()

onMounted(() =&gt; {
  docsearch(algolia)
})
&lt;/script&gt;

&lt;template&gt;
  &lt;div id=&quot;docsearch&quot; /&gt;
&lt;/template&gt;
</code></pre>
<p>这里我使用的时候覆盖了原有的样式,如下</p>
<pre><code class="language-vue">&lt;template&gt;
  &lt;a href=&quot; &quot; class=&quot;relative&quot;&gt;
    &lt;div class=&quot;i-mynaui-search-square h-1.3em w-1.3em&quot; /&gt;
    &lt;AlgoliaSearchBox
      class=&quot;absolute left-0 top-0 h-full w-full op0!&quot;
      :algolia=&quot;{
        apiKey: 'db0e9b82d77e75c9fc8aee05b1e14334',
        indexName: 'ryan',
        appId: 'X0NE0GCGVB',
      }&quot;
    /&gt;
  &lt;/a&gt;
&lt;/template&gt;

&lt;style&gt;
:root {
  /* docsearch */
  --docsearch-primary-color: #313237 !important;
  --docsearch-logo-color: #313237 !important;
  --docsearch-text-color: #9d9fa1 !important;
}

.cls-1,
.cls-2 {
  fill: #060606 !important;
}

.DocSearch-Button-Placeholder,
span.DocSearch-Button-Keys {
  display: none;
}

button.DocSearch-Button {
  margin: 0;
  background: transparent;
  height: auto;
  padding: 0;
}

button.DocSearch-Button:active,
button.DocSearch-Button:focus,
button.DocSearch-Button:hover {
  background: transparent;
  box-shadow: none;
  color: var(--c-hover-color);
}
&lt;/style&gt;
</code></pre>
<h3>5.编写爬虫的规则</h3>
<p><a href="https://github.com/ryanuo/docs-crawler">爬虫规则</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Comprehensive Guide to Git Commit Message Conventions]]></title>
            <link>https://ryanuo.cc/posts/git-commit</link>
            <guid>https://ryanuo.cc/posts/git-commit</guid>
            <pubDate>Wed, 04 Jan 2023 15:07:09 GMT</pubDate>
            <description><![CDATA[The Git commit convention includes types, scopes, etc., facilitating clear collaboration and maintenance, recommended Conventional Commits, AngularJS convention for reference.]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>Introduction</h2>
<p>In any version control software, proper commit messages are crucial for team collaboration and code maintenance. Git, as the most popular distributed version control system, benefits greatly from standardized commit messages. They help team members better understand the code history, speed up code reviews, and automate changelog generation. This article introduces some common Git commit message conventions.</p>
<h2>Importance of Git Commit Conventions</h2>
<ul>
<li><strong>Improved Readability</strong>: Clear commit messages allow team members to quickly understand the purpose and changes of a commit.</li>
<li><strong>Ease of Tracking</strong>: Standardized commits make it easier to search for specific changes in the history.</li>
<li><strong>Support for Automation Tools</strong>: Standardized commit messages can be used by various tools, such as automatically generating CHANGELOGs or analyzing code changes through scripts.</li>
</ul>
<h2>Common Format of Git Commit Messages</h2>
<p>A standardized Git commit message typically includes the following parts:</p>
<ol>
<li><strong>Type</strong>: The type of the commit, such as fixing a bug (<code>fix</code>) or adding a new feature (<code>feat</code>).</li>
<li><strong>Scope</strong>: (Optional) The scope of the changes, such as a component or a module.</li>
<li><strong>Subject</strong>: A brief description of the commit, not exceeding 50 characters.</li>
<li><strong>Body</strong>: (Optional) A more detailed description, which can span multiple lines.</li>
<li><strong>Footer</strong>: (Optional) Descriptions of related issues or breaking changes.</li>
</ol>
<p>For example:</p>
<pre><code>feat(login): add the remember me button

- Add a &quot;remember me&quot; checkbox in the login form
- Save the login state to local storage when checked

Closes #123
</code></pre>
<h2>Common Commit Types</h2>
<p>Below are some common commit types and their meanings:</p>
<ul>
<li><strong>feat</strong>: New feature</li>
<li><strong>fix</strong>: Bug fix</li>
<li><strong>docs</strong>: Documentation</li>
<li><strong>style</strong>: Formatting (changes that do not affect code execution)</li>
<li><strong>refactor</strong>: Code refactoring (neither adding a feature nor fixing a bug)</li>
<li><strong>test</strong>: Adding tests</li>
<li><strong>chore</strong>: Changes to the build process or auxiliary tools</li>
</ul>
<h2>Reference Articles</h2>
<p>When defining your own Git commit conventions, you can refer to the following articles and resources:</p>
<ul>
<li><a href="https://www.conventionalcommits.org/">Conventional Commits</a></li>
<li><a href="https://docs.angularjs.org/misc/contribute">AngularJS Git Commit Message Conventions</a></li>
<li><a href="https://semver.org/">Semantic Versioning Specification (SemVer)</a></li>
</ul>
<p>The above outlines the basic Git commit conventions and some recommendations. You can customize your commit conventions based on your team's specific needs.</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Git Commit 规范]]></title>
            <link>https://ryanuo.cc/zh/posts/git-commit</link>
            <guid>https://ryanuo.cc/zh/posts/git-commit</guid>
            <pubDate>Wed, 04 Jan 2023 15:07:09 GMT</pubDate>
            <description><![CDATA[Git提交规范包括类型、范围等，助力清晰协作与维护，推荐Conventional Commits、AngularJS规范参考。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>前言</h2>
<p>在任何版本控制软件中，合理的提交（commit）信息对于团队合作和代码维护来说都是至关重要的。Git，作为目前最流行的分布式版本控制系统，其提交信息的规范化可以帮助团队成员更好地了解代码历史，加快代码审查过程，以及自动化生成变更日志。本文将介绍一些常见的 Git 提交信息规范。</p>
<h2>Git Commit 规范的重要性</h2>
<ul>
<li><strong>提高可读性</strong>：清晰的提交信息可以让其他团队成员快速了解这次提交的目的和改动内容。</li>
<li><strong>方便追踪</strong>：规范化的提交便于在历史记录中搜索特定的更改。</li>
<li><strong>自动化工具支持</strong>：规范的提交信息可以被各种工具所使用，比如自动生成 CHANGELOG，或者通过脚本分析代码变更情况。</li>
</ul>
<h2>Git Commit 规范的常见格式</h2>
<p>一个规范化的 Git 提交信息通常包括以下几个部分：</p>
<ol>
<li><strong>类型（Type）</strong>: 这次提交的类型，比如是修复一个 Bug（fix），还是添加一个新功能（feat）。</li>
<li><strong>范围（Scope）</strong>: （可选）改动影响的范围，比如一个组件或者一个功能模块。</li>
<li><strong>主题（Subject）</strong>: 提交的简短描述，不超过50个字符。</li>
<li><strong>正文（Body）</strong>: （可选）更详细的描述，可以分成多行。</li>
<li><strong>页脚（Footer）</strong>: （可选）相关联的 issue 或者 breaking changes 的描述。</li>
</ol>
<p>例如：</p>
<pre><code>feat(login): add the remember me button

- Add a &quot;remember me&quot; checkbox in the login form
- Save the login state to local storage when checked

Closes #123
</code></pre>
<h2>常用的提交类型</h2>
<p>以下是一些常用的提交类型以及它们的含义：</p>
<ul>
<li><strong>feat</strong>: 新功能（feature）</li>
<li><strong>fix</strong>: 修补bug</li>
<li><strong>docs</strong>: 文档（documentation）</li>
<li><strong>style</strong>: 格式（不影响代码运行的变动）</li>
<li><strong>refactor</strong>: 重构（即不是新增功能，也不是修改bug的代码变动）</li>
<li><strong>test</strong>: 增加测试</li>
<li><strong>chore</strong>: 构建过程或辅助工具的变动</li>
</ul>
<h2>参考文章</h2>
<p>在制定自己的 Git Commit 规范时，可以参考以下文章和资源：</p>
<ul>
<li><a href="https://www.conventionalcommits.org/">Conventional Commits</a></li>
<li><a href="https://docs.angularjs.org/misc/contribute">AngularJS Git Commit Message Conventions</a></li>
<li><a href="https://semver.org/">Semantic Versioning Specification (SemVer)</a></li>
</ul>
<p>以上是 Git Commit 的基本规范和一些建议，你可以根据团队的实际需求定制自己的提交规范。</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Comprehensive and Detailed Study Notes on Pinia]]></title>
            <link>https://ryanuo.cc/posts/pinia</link>
            <guid>https://ryanuo.cc/posts/pinia</guid>
            <pubDate>Tue, 14 Jun 2022 15:22:58 GMT</pubDate>
            <description><![CDATA[Pinia study notes, advantages and disadvantages of Pinia, introduction to usage, initializing the store repository, several ways to modify values, destructuring state, getters and actions, Pinia plugins]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>Advantages and Disadvantages of Vuex and Pinia</h2>
<h3>Advantages of Pinia</h3>
<ul>
<li>Full TypeScript support: Adding TypeScript is easier compared to Vuex.</li>
<li>Extremely lightweight (about 1KB in size).</li>
<li>Store actions are dispatched as regular function calls instead of using the dispatch method or MapAction helper functions, which are common in Vuex.</li>
<li>Supports multiple stores, Vue devtools, SSR, and webpack code splitting.</li>
</ul>
<h3>Disadvantages of Pinia</h3>
<ul>
<li>Does not support debugging features like time travel and editing.</li>
</ul>
<h3>Advantages of Vuex</h3>
<ul>
<li>Supports debugging features such as time travel and editing.</li>
<li>Suitable for large, highly complex Vue.js projects.</li>
</ul>
<h3>Disadvantages of Vuex</h3>
<ul>
<li>Starting from Vue 3, the results of getters are no longer cached like computed properties.</li>
<li>Vuex 4 has some issues related to type safety.</li>
</ul>
<h2>Introduction to Pinia</h2>
<ul>
<li>
<p>Pinia.js has the following features:</p>
</li>
<li>
<p>Full TypeScript support;</p>
</li>
<li>
<p>Lightweight, with a compressed size of only about 1KB;</p>
</li>
<li>
<p>Removes mutations, leaving only state, getters, and actions;</p>
</li>
<li>
<p>Actions support both synchronous and asynchronous operations;</p>
</li>
<li>
<p>Flat code structure without module nesting, only the concept of stores, and stores can be freely used with each other. Each store is independent;</p>
</li>
<li>
<p>No need to manually add stores; once a store is created, it is automatically added;</p>
</li>
<li>
<p>Supports both Vue 3 and Vue 2;</p>
</li>
</ul>
<p><a href="https://blog.csdn.net/qq1195566313/article/details/123338137">Introduction to Pinia Usage</a></p>
<h2>Initializing the Pinia Store Repository</h2>
<ul>
<li><a href="https://blog.csdn.net/qq1195566313/article/details/123342785">Store Repository</a></li>
</ul>
<h2>Several Ways to Modify Values in Pinia</h2>
<ul>
<li><a href="https://blog.csdn.net/qq1195566313/article/details/123360349">Ways to Modify Values</a></li>
</ul>
<h2>Destructuring State</h2>
<ul>
<li>Directly destructuring state does not achieve reactivity.</li>
<li>Use storeToRefs to achieve reactivity for destructured data on the page.</li>
</ul>
<h2>Actions and Getters in Pinia</h2>
<ul>
<li><a href="https://blog.csdn.net/qq1195566313/article/details/123376269">Getters and Actions</a></li>
</ul>
<h2>Pinia Plugins</h2>
<ul>
<li><a href="https://blog.csdn.net/qq1195566313/article/details/123431769">Pinia Caching Plugin</a></li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[全面且详细的Pinia学习笔记指南]]></title>
            <link>https://ryanuo.cc/zh/posts/pinia</link>
            <guid>https://ryanuo.cc/zh/posts/pinia</guid>
            <pubDate>Tue, 14 Jun 2022 15:22:58 GMT</pubDate>
            <description><![CDATA[Pinia学习笔记：Pinia优缺点、使用介绍、初始化store仓库、修改值的几种方式、解构state、getter和actions、pinia插件。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>Vuex 和 Pinia 的优缺点</h2>
<h3>Pinia的优点</h3>
<ul>
<li>完整的 TypeScript 支持：与在 Vuex 中添加 TypeScript 相比，添加 TypeScript 更容易极其轻巧(体积约 1KB);store 的 action 被调度为常规的函数调用，而不是使用 dispatch 方法或 MapAction 辅助函数，这在 Vuex 中很常见支持多个Store,支持 Vue devtools、SSR 和 webpack 代码拆分。</li>
</ul>
<h3>Pinia的缺点</h3>
<ul>
<li>不支持时间旅行和编辑等调试功能</li>
</ul>
<h3>Vuex的优点</h3>
<ul>
<li>支持调试功能，如时间旅行和编辑</li>
<li>适用于大型、高复杂度的Vue.js项目</li>
</ul>
<h3>Vuex的缺点</h3>
<ul>
<li>从 Vue 3 开始，getter 的结果不会像计算属性那样缓存</li>
<li>Vuex 4有一些与类型安全相关的问题</li>
</ul>
<h2>pinia介绍</h2>
<ul>
<li>
<p>Pinia.js 有如下特点：</p>
</li>
<li>
<p>完整的 ts 的支持；</p>
</li>
<li>
<p>足够轻量，压缩后的体积只有1kb左右;</p>
</li>
<li>
<p>去除 mutations，只有 state，getters，actions；</p>
</li>
<li>
<p>actions 支持同步和异步；</p>
</li>
<li>
<p>代码扁平化没有模块嵌套，只有 store 的概念，store 之间可以自由使用，每一个store都是独立的</p>
</li>
<li>
<p>无需手动添加 store，store 一旦创建便会自动添加；</p>
</li>
<li>
<p>支持Vue3 和 Vue2；</p>
</li>
</ul>
<p><a href="https://blog.csdn.net/qq1195566313/article/details/123338137">pinia使用介绍</a></p>
<h2>pinia初始化store仓库</h2>
<ul>
<li><a href="https://blog.csdn.net/qq1195566313/article/details/123342785">store仓库</a></li>
</ul>
<h2>pinia修改值的几种方式</h2>
<ul>
<li><a href="https://blog.csdn.net/qq1195566313/article/details/123360349">修改值的几种方式</a></li>
</ul>
<h2>解构state</h2>
<ul>
<li>直接解构state无法实现响应式</li>
<li>使用storeToRefs将解构后 实现页面数据的响应式</li>
</ul>
<h2>pinia中的actions和getters</h2>
<ul>
<li><a href="https://blog.csdn.net/qq1195566313/article/details/123376269">getter和actions</a></li>
</ul>
<h2>pinia插件</h2>
<ul>
<li><a href="https://blog.csdn.net/qq1195566313/article/details/123431769">pinia缓冲插件</a></li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Pest Identification System for Herbal Medicine]]></title>
            <link>https://ryanuo.cc/posts/bs2022</link>
            <guid>https://ryanuo.cc/posts/bs2022</guid>
            <pubDate>Sun, 01 May 2022 21:09:32 GMT</pubDate>
            <description><![CDATA[The Chinese herbal medicine pest identification system has been completed using Vue3 and YOLO v5. Technology stack includes front-end optimization, modular back-end, gzip acceleration, and open-source repositories for both front-end and back-end.]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>Graduation Project Video Showcase</h2>
<iframe height=400px width=100% src="//player.bilibili.com/player.html?bvid=BV1UN4y137yk&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
<h2>Image Showcase</h2>
<h2>System Architecture</h2>
<p><img src="https://cloud.ryanuo.cc/hexo/4/4c969c9e-cb39-42c8-a665-47fd067fc3df.jpeg" alt="System Architecture"></p>
<h2>Front-End Functional Design</h2>
<h3>Libraries Used in the Front-End</h3>
<ul>
<li><code>Vue3</code></li>
<li><code>vant3</code></li>
<li><code>echart</code> for data analysis</li>
<li><code>swiper</code> for carousel effects</li>
<li><code>html2canvas</code> for poster creation</li>
<li><code>nprogress</code> for request loading</li>
<li><code>compressorjs</code> for image compression</li>
</ul>
<h3>Front-End Performance Optimization</h3>
<ol>
<li>Use webpack to analyze performance-consuming files and plugins (BundleAnalyzerPlugin) and separate them one by one.</li>
<li>Use CDN for static acceleration. Common components like Vue, vant, swiper, echart, and html2canvas are loaded via CDN (handled differently for production and online environments).</li>
<li>Enable Gzip acceleration for both front-end and back-end. The front-end generates compressed files using gzip, and the back-end uses nginx to enable gzip static acceleration.</li>
<li>Use asynchronous loading for components. Vue3's <code>defineAsyncComponent</code> is used to separate asynchronous components into individual chunks.</li>
</ol>
<h2>Back-End Introduction</h2>
<h3>Technologies Used in the Back-End</h3>
<ul>
<li>YOLO-v5</li>
<li>Flask</li>
<li>Tornado (using Tornado's WSGI module to host Flask applications)</li>
<li>flask_restful for modular handling and easier maintenance</li>
<li>MySQL 8.0.23</li>
</ul>
<h3>Modular Display</h3>
<p><img src="https://cloud.ryanuo.cc/hexo/4/b718ebdf-6b88-49b5-ba8e-57dce92914e1.jpeg" alt=""></p>
<h2>Admin Panel Introduction</h2>
<h2>Repository Links</h2>
<ul>
<li><a href="https://github.com/ryanuo/bs2022">Front-End Open-Source Repository</a></li>
<li><a href="https://github.com/ryanuo/xpalmworm">Back-End Open-Source Repository</a></li>
<li>Database and back-end code are not yet developed.</li>
<li>Contributions and Stars are welcome!</li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[基于深度学习的中草药害虫识别系统的设计与实现]]></title>
            <link>https://ryanuo.cc/zh/posts/bs2022</link>
            <guid>https://ryanuo.cc/zh/posts/bs2022</guid>
            <pubDate>Sun, 01 May 2022 21:09:32 GMT</pubDate>
            <description><![CDATA[中草药害虫识别系统毕设，Vue3、YOLO-v5技术栈，前端优化、模块化后端，Gzip加速，开源前端、后台仓库。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>毕业设计项目视频展示</h2>
<iframe height=400px width=100% src="//player.bilibili.com/player.html?bvid=BV1UN4y137yk&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
<h2>图片展示</h2>
<h2>系统架构</h2>
<p><img src="https://cloud.ryanuo.cc/hexo/4/4c969c9e-cb39-42c8-a665-47fd067fc3df.jpeg" alt="系统架构"></p>
<h2>前端功能设计</h2>
<h3>前端用到的库</h3>
<ul>
<li><code>Vue3</code></li>
<li><code>vant3</code></li>
<li><code>echart</code> 数据的分析</li>
<li><code>swiper</code> 轮播效果</li>
<li><code>html2canvas</code> 海报制作</li>
<li><code>nprogress</code> 请求加载</li>
<li><code>compressorjs</code> 图片压缩</li>
</ul>
<h3>前端的性能优化处理</h3>
<ol>
<li>使用webpack分析出占用性能的文件，插件（BundleAnalyzerPlugin ），逐一分离</li>
<li>使用cdn静态加速，Vue,vant,swiper,echart,html2canvas这些公共的组件 都是以CDN的形式进行加载请求（生产环境和线上环境区别处理）</li>
<li>前后端开启Gzip加速，前端生成gzip处理后得压缩文件，后端使用nginx开启gzip静态加速</li>
<li>组件使用异步加载的方式，Vue3使用defineAsyncComponent才能实现，将异步组件进行分离生成一个单独的chunk</li>
</ol>
<h2>后端介绍</h2>
<h3>后端用到的技术</h3>
<ul>
<li>YOLO-v5</li>
<li>Flask</li>
<li>tornado 利用Tornado的wsgi模块来托管flask应用</li>
<li>flask_restful 模块化处理 方便维护</li>
<li>Mysql 8.0.23</li>
</ul>
<h3>模块化展示</h3>
<p><img src="https://cloud.ryanuo.cc/hexo/4/b718ebdf-6b88-49b5-ba8e-57dce92914e1.jpeg" alt=""></p>
<h2>后台管理介绍</h2>
<h2>仓库地址</h2>
<ul>
<li><a href="https://github.com/ryanuo/bs2022">前端开源仓库</a></li>
<li><a href="https://github.com/ryanuo/xpalmworm">后台开源仓库</a></li>
<li>暂未开发数据库和后端代码</li>
<li>欢迎Star</li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Convolutional Neural Network Learning]]></title>
            <link>https://ryanuo.cc/posts/convolution</link>
            <guid>https://ryanuo.cc/posts/convolution</guid>
            <pubDate>Fri, 15 Apr 2022 12:50:59 GMT</pubDate>
            <description><![CDATA[convolutional network learning parameter reduction feature extraction edge detection filling step size 3 d convolution rgb processing network structure including convolution re lu pooling and fully connected layers]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>为什么使用卷积运算</h2>
<ol>
<li>使用卷积比全连接网络少很多参数，为了计算高分辨率的图片，很少的参数以便我们可以使用较小的训练集来训练，有利于预防过拟合</li>
<li>使用卷积就是为了提取显著特征，减少特征维数，减少计算量。</li>
</ol>
<h2>边缘检测</h2>
<ol>
<li>
<ul>
<li>表示卷积，使用3x3过滤器</li>
</ul>
</li>
<li>如图是垂直边缘检测器<br>
<img src="https://cloud.ryanuo.cc/hexo/4/20220415102757.png" alt=""></li>
</ol>
<h2>正边缘与负边缘</h2>
<p><img src="https://cloud.ryanuo.cc/hexo/4/20220415103726.png" alt=""><br>
<img src="https://cloud.ryanuo.cc/hexo/4/20220415104101.png" alt=""></p>
<h2>填充降维度</h2>
<p><img src="https://cloud.ryanuo.cc/hexo/4/20220415104949.png" alt=""><br>
公式：<code>nxn * fxf = (n-f+1)x(n-f+1)</code></p>
<ol>
<li>缺陷：不希望每一步都缩小维度，使得图片丢失了许多边界的信息</li>
<li>解决方法：填充图片扩大图片，给图片增加边框像素也就是 给一个padding为一像素</li>
<li>公式就变成了：<code>n+2p-f+1=n</code>如果让填充后的图片进行卷积等于原来图片的大小 <code>p=(f-1)/2</code> 并且过滤器建议使奇数</li>
</ol>
<h2>带步长的卷积</h2>
<ol>
<li>公式：<code>(n+2p-f)/s+1</code> 带步长卷积后的维度大小，除不尽可以向下取整<code>floor=⌊ ⌋</code></li>
<li>总结公式：<code>⌊(n+2p-f)/s+1⌋ * ⌊(n+2p-f)/s+1⌋</code></li>
</ol>
<h2>三维卷积</h2>
<ol>
<li>使用rgb图片 则是三通道（red,green,blue）<br>
<img src="https://cloud.ryanuo.cc/hexo/4/20220415111153.png" alt=""></li>
</ol>
<h2>卷积神经网络结构</h2>
<ol>
<li>卷积神经网络主要由这几类层构成：输入层、卷积层，ReLU层、池化（Pooling）层和全连接层（全连接层和常规神经网络中的一样）。</li>
</ol>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[卷积神经网络学习]]></title>
            <link>https://ryanuo.cc/zh/posts/convolution</link>
            <guid>https://ryanuo.cc/zh/posts/convolution</guid>
            <pubDate>Fri, 15 Apr 2022 12:50:59 GMT</pubDate>
            <description><![CDATA[卷积网络学习：减少参数、特征提取、边缘检测、填充、步长、三维卷积、RGB处理、网络结构含卷积、ReLU、池化、全连接层。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>为什么使用卷积运算</h2>
<ol>
<li>使用卷积比全连接网络少很多参数，为了计算高分辨率的图片，很少的参数以便我们可以使用较小的训练集来训练，有利于预防过拟合</li>
<li>使用卷积就是为了提取显著特征，减少特征维数，减少计算量。</li>
</ol>
<h2>边缘检测</h2>
<ol>
<li>
<ul>
<li>表示卷积，使用3x3过滤器</li>
</ul>
</li>
<li>如图是垂直边缘检测器<br>
<img src="https://cloud.ryanuo.cc/hexo/4/20220415102757.png" alt=""></li>
</ol>
<h2>正边缘与负边缘</h2>
<p><img src="https://cloud.ryanuo.cc/hexo/4/20220415103726.png" alt=""><br>
<img src="https://cloud.ryanuo.cc/hexo/4/20220415104101.png" alt=""></p>
<h2>填充降维度</h2>
<p><img src="https://cloud.ryanuo.cc/hexo/4/20220415104949.png" alt=""><br>
公式：<code>nxn * fxf = (n-f+1)x(n-f+1)</code></p>
<ol>
<li>缺陷：不希望每一步都缩小维度，使得图片丢失了许多边界的信息</li>
<li>解决方法：填充图片扩大图片，给图片增加边框像素也就是 给一个padding为一像素</li>
<li>公式就变成了：<code>n+2p-f+1=n</code>如果让填充后的图片进行卷积等于原来图片的大小 <code>p=(f-1)/2</code> 并且过滤器建议使奇数</li>
</ol>
<h2>带步长的卷积</h2>
<ol>
<li>公式：<code>(n+2p-f)/s+1</code> 带步长卷积后的维度大小，除不尽可以向下取整<code>floor=⌊ ⌋</code></li>
<li>总结公式：<code>⌊(n+2p-f)/s+1⌋ * ⌊(n+2p-f)/s+1⌋</code></li>
</ol>
<h2>三维卷积</h2>
<ol>
<li>使用rgb图片 则是三通道（red,green,blue）<br>
<img src="https://cloud.ryanuo.cc/hexo/4/20220415111153.png" alt=""></li>
</ol>
<h2>卷积神经网络结构</h2>
<ol>
<li>卷积神经网络主要由这几类层构成：输入层、卷积层，ReLU层、池化（Pooling）层和全连接层（全连接层和常规神经网络中的一样）。</li>
</ol>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Docker Basics, Commands, and Key Concepts]]></title>
            <link>https://ryanuo.cc/posts/docker</link>
            <guid>https://ryanuo.cc/posts/docker</guid>
            <pubDate>Tue, 11 Jan 2022 11:17:29 GMT</pubDate>
            <description><![CDATA[learn about docker images containers and repositories underlying principles and common commands]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>Docker</h2>
<h3>Image</h3>
<ul>
<li>A template that can be used to create container services.</li>
<li>Images are immutable and consist of multiple layers.</li>
<li>Commonly used base images include <code>ubuntu</code>, <code>alpine</code>, and <code>node</code>.</li>
</ul>
<h3>Container</h3>
<ul>
<li>Independently runs one or a group of applications, created from an image.</li>
<li>A simplified Linux system.</li>
<li>Containers are lightweight and portable, making them ideal for microservices.</li>
</ul>
<h3>Repository</h3>
<ul>
<li>A place to store images.</li>
<li>Divided into public and private repositories.</li>
<li>Popular public repositories include Docker Hub and GitHub Container Registry.</li>
</ul>
<h2>Underlying Principles</h2>
<h3>How It Works</h3>
<ul>
<li>A Client-Server structured system, where Docker's daemon runs on the host.</li>
<li>DockerServer receives commands from Docker-Client.</li>
<li>Docker uses namespaces for isolation and cgroups for resource management.</li>
</ul>
<h3>Key Components</h3>
<ul>
<li><strong>Docker Daemon</strong>: Runs on the host machine and manages Docker objects.</li>
<li><strong>Docker CLI</strong>: A command-line interface to interact with the Docker Daemon.</li>
<li><strong>Docker Compose</strong>: A tool for defining and running multi-container applications.</li>
</ul>
<h2>Commands</h2>
<h3>Container</h3>
<p><code>docker container my_command</code><br>
<code>create</code> — Create a container from an image.<br>
<code>start</code> — Start an existing container.<br>
<code>run</code> — Create and start a new container.<br>
<code>ls</code> — List running containers.<br>
<code>inspect</code> — View detailed information about a container.<br>
<code>logs</code> — Print logs.<br>
<code>stop</code> — Gracefully stop a running container.<br>
<code>kill</code> — Abruptly stop the main process in a container.<br>
<code>rm</code> — Remove a stopped container.<br>
<code>exec</code> — Run a command inside a running container.<br>
<code>cp</code> — Copy files or directories between a container and the host.</p>
<h3>Image</h3>
<ul>
<li>Use docker <code>image my_command</code><br>
<code>build</code> — Build an image.<br>
<code>push</code> — Push an image to a remote registry.<br>
<code>pull</code> — Download an image from a remote registry.<br>
<code>ls</code> — List images.<br>
<code>history</code> — View intermediate image information.<br>
<code>inspect</code> — View detailed information about an image, including layers.<br>
<code>rm</code> — Remove an image.<br>
<code>tag</code> — Tag an image for easier reference.</li>
</ul>
<h3>Network</h3>
<ul>
<li>Use docker <code>network my_command</code><br>
<code>create</code> — Create a new network.<br>
<code>ls</code> — List all networks.<br>
<code>inspect</code> — View detailed information about a network.<br>
<code>rm</code> — Remove a network.<br>
<code>connect</code> — Connect a container to a network.<br>
<code>disconnect</code> — Disconnect a container from a network.</li>
</ul>
<p><a href="https://juejin.cn/post/6969877845531181086">Docker Syntax</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Docker基础、命令和关键概念]]></title>
            <link>https://ryanuo.cc/zh/posts/docker</link>
            <guid>https://ryanuo.cc/zh/posts/docker</guid>
            <pubDate>Tue, 11 Jan 2022 11:17:29 GMT</pubDate>
            <description><![CDATA[Docker学习：镜像、容器、仓库；底层原理；常用命令。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>Docker</h2>
<h3>镜像（Image）</h3>
<ul>
<li>一个模板，可以通过这个模板来创建容器服务。</li>
<li>镜像是不可变的，由多个层组成。</li>
<li>常用的基础镜像包括 <code>ubuntu</code>、<code>alpine</code> 和 <code>node</code>。</li>
</ul>
<h3>容器（Container）</h3>
<ul>
<li>独立运行一个或一组应用，由镜像创建。</li>
<li>一个简化的 Linux 系统。</li>
<li>容器轻量且可移植，非常适合微服务。</li>
</ul>
<h3>仓库（Repository）</h3>
<ul>
<li>存放镜像的地方。</li>
<li>分为公有仓库和私有仓库。</li>
<li>常见的公有仓库包括 Docker Hub 和 GitHub Container Registry。</li>
</ul>
<h2>底层原理</h2>
<h3>怎么工作的</h3>
<ul>
<li>Client-Server 结构的系统，Docker 的守护进程运行在主机上。</li>
<li>DockerServer 接收 Docker-Client 的指令。</li>
<li>Docker 使用命名空间（namespaces）进行隔离，使用控制组（cgroups）进行资源管理。</li>
</ul>
<h3>核心组件</h3>
<ul>
<li><strong>Docker Daemon</strong>：运行在主机上，管理 Docker 对象。</li>
<li><strong>Docker CLI</strong>：用于与 Docker Daemon 交互的命令行界面。</li>
<li><strong>Docker Compose</strong>：用于定义和运行多容器应用的工具。</li>
</ul>
<h2>命令</h2>
<h3>容器</h3>
<p><code>docker container my_command</code><br>
<code>create</code> — 从镜像创建容器。<br>
<code>start</code> — 启动现有容器。<br>
<code>run</code> — 创建并启动新容器。<br>
<code>ls</code> — 列出正在运行的容器。<br>
<code>inspect</code> — 查看有关容器的详细信息。<br>
<code>logs</code> — 打印日志。<br>
<code>stop</code> — 优雅地停止运行的容器。<br>
<code>kill</code> — 突然停止容器中的主进程。<br>
<code>rm</code> — 删除已停止的容器。<br>
<code>exec</code> — 在运行的容器中执行命令。<br>
<code>cp</code> — 在容器和主机之间复制文件或目录。</p>
<h3>镜像</h3>
<ul>
<li>使用 docker <code>image my_command</code><br>
<code>build</code> — 构建镜像。<br>
<code>push</code> — 将镜像推送到远程注册表。<br>
<code>pull</code> — 从远程注册表下载镜像。<br>
<code>ls</code> — 列出镜像。<br>
<code>history</code> — 查看中间镜像信息。<br>
<code>inspect</code> — 查看有关镜像的详细信息，包括图层。<br>
<code>rm</code> — 删除镜像。<br>
<code>tag</code> — 为镜像打标签以便于引用。</li>
</ul>
<h3>网络</h3>
<ul>
<li>使用 docker <code>network my_command</code><br>
<code>create</code> — 创建新网络。<br>
<code>ls</code> — 列出所有网络。<br>
<code>inspect</code> — 查看有关网络的详细信息。<br>
<code>rm</code> — 删除网络。<br>
<code>connect</code> — 将容器连接到网络。<br>
<code>disconnect</code> — 将容器从网络断开。</li>
</ul>
<p><a href="https://juejin.cn/post/6969877845531181086">docker语法</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[About Deep Learning Pytorch]]></title>
            <link>https://ryanuo.cc/posts/pytorch</link>
            <guid>https://ryanuo.cc/posts/pytorch</guid>
            <pubDate>Wed, 01 Dec 2021 11:34:19 GMT</pubDate>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>深度学习</h2>
<ul>
<li>机器语言的分支, 对数据进行特征学习, 人工神经网络为基础</li>
</ul>
<h3>机器学习和深度学习的区别</h3>
<ul>
<li>特征抽取: 01. 人工的特征抽取的过程 02. 深度学习：自动的进行特征抽取</li>
<li>数据量: 01. 机器学习：数据少 02. 深度学习：数据多</li>
</ul>
<h3>深度学习的应用场景</h3>
<ol>
<li>图像识别：物体识别场景识别人脸检测跟踪人脸身份认证自然语言处理技术</li>
<li>机器翻译：文本识别聊天对话语音技术</li>
<li>语音识别</li>
</ol>
<h3>神经网络</h3>
<ol>
<li>人工神经网络（英语：Artificial Neural Network，ANN），简称神经网络（Neural Network，NN）或类神经网络，是一种模仿生物神经网络（动物的中枢神经系统，特别是大脑）的结构和功能的数学模型，用于对函数进行估计或近似。<br>
和其他机器学习方法一样, 神经网络已经被用于解决各种各样的问题, 例如机器视觉和语音识别. 这些问题都是很难被传统基于规则的编程所解决的.</li>
<li>模拟生物的神经元，对函数进行评估或者近似。神经网络中的基础单元，相互连接，组成神经网络</li>
<li><code>t=f(W^TA+b)</code> 一个神经元的功能是求得输入向量与权向量的内积后, 经一个非线性传递函数得到一个标量结果.</li>
</ol>
<h3>单项神经网络</h3>
<ul>
<li>最基本的神经元形式由有限个神经元构成, 所有神经元的输入向量都是同一个向量. 由于每一个神经元都会产生一个标量结果, 所以单层神经元的输出是一个向量, 向量的维数等于神经元的数目.</li>
</ul>
<h3>感知机</h3>
<ul>
<li>两层神经网络组成, 输出层输入层, 感知机由两层神经网络组成, 输入层接收外界输入信号后传递给输出层(输出+1正例, -1反例), 输出层是 M-P 神经元</li>
<li>作用: 把一个n维向量空间用一个超平面分割成两部分, 给定一个输入向量, 超平面可以判断出这个向量位于超平面的哪一边, 得到输入时正类或者是反类, 对应到2维空间就是一条直线把一个平面分为两个部分.</li>
</ul>
<h2>初步使用</h2>
<h3>Pytorch的入门使用</h3>
<h2>目标</h2>
<ol>
<li>知道张量和Pytorch中的张量</li>
<li>知道pytorch中如何创建张量</li>
<li>知道pytorch中tensor的常见方法</li>
<li>知道pytorch中tensor的数据类型</li>
<li>知道pytorch中如何实现tensor在cpu和cuda中转化</li>
</ol>
<h2>1. 张量Tensor</h2>
<p>张量是一个统称, 其中包含很多类型:</p>
<ol>
<li>0阶张量：标量、常数，0-D Tensor</li>
<li>1阶张量：向量，1-D Tensor</li>
<li>2阶张量：矩阵，2-D Tensor</li>
<li>3阶张量</li>
<li>...</li>
<li>N阶张量</li>
</ol>
<h2>2. Pytorch中创建张量</h2>
<ol>
<li>使用python中的列表或者序列创建tensor</li>
</ol>
<pre><code class="language-python">   torch.tensor([[1., -1.], [1., -1.]])
   tensor([[ 1.0000, -1.0000],
           [ 1.0000, -1.0000]])
</code></pre>
<ol start="2">
<li>使用numpy中的数组创建tensor</li>
</ol>
<pre><code class="language-python">   torch.tensor(np.array([[1, 2, 3], [4, 5, 6]]))
   tensor([[ 1,  2,  3],
           [ 4,  5,  6]])
</code></pre>
<ol start="3">
<li>
<p>使用torch的api创建tensor</p>
</li>
<li>
<p><code>torch.empty(3, 4)</code> 创建3行4列的空的tensor, 会用无用数据进行填充</p>
</li>
<li>
<p><code>torch.ones([3,4])</code> 创建3行4列的<strong>全为1</strong>的tensor</p>
</li>
<li>
<p><code>torch.zeros([3, 4])</code> 创建3行4列的<strong>全为0</strong>的tensor</p>
</li>
<li>
<p><code>torch.rand([3,4])</code> 创建3行4列的<strong>随机值</strong>的tensor, 随机值的区间是 <code>[0, 1)</code></p>
</li>
</ol>
<pre><code class="language-python">      &gt;&gt;&gt; torch.rand(2, 3)
      tensor([[ 0.8237,  0.5781,  0.6879],
      [ 0.3816,  0.7249,  0.0998]])
</code></pre>
<ol start="4">
<li><code>torch.randint(low=0,high=10,size=[3,4])</code> 创建3行4列的<strong>随机整数</strong>的tensor, 随机值的区间是 <code>[low, high)</code></li>
</ol>
<pre><code class="language-python">      &gt;&gt;&gt; torch.randint(3, 10, (2, 2))
      tensor([[4, 5],
      [6, 7]])
</code></pre>
<ol start="5">
<li><code>torch.randn([3,4])</code> 创建3行4列的<strong>随机数</strong>的tensor, 随机值的分布式均值为0, 方差为1</li>
</ol>
<h2>3. Pytorch中tensor的常用方法</h2>
<ol>
<li>获取tensor中的数据(当tensor中只有一个元素可用)：<code>tensor.item()</code></li>
</ol>
<pre><code class="language-python">   In [10]: a = torch.tensor(np.arange(1))

   In [11]: a
   Out[11]: tensor([0])

   In [12]: a.item()
   Out[12]: 0
</code></pre>
<ol start="2">
<li>转化为numpy数组</li>
</ol>
<pre><code class="language-python">   In [55]: z.numpy()
   Out[55]:
   array([[-2.5871205],
          [ 7.3690367],
          [-2.4918075]], dtype=float32)
</code></pre>
<ol start="3">
<li>获取形状：<code>tensor.size()</code></li>
</ol>
<pre><code class="language-python">   In [72]: x
   Out[72]:
   tensor([[    1,     2],
           [    3,     4],
           [    5,    10]], dtype=torch.int32)

   In [73]: x.size()
   Out[73]: torch.Size([3, 2])
</code></pre>
<ol start="4">
<li>形状改变：<code>tensor.view((3,4))</code>。类似numpy中的reshape，是一种浅拷贝，仅仅是形状发生改变</li>
</ol>
<pre><code class="language-python">   In [76]: x.view(2,3)
   Out[76]:
   tensor([[    1,     2,     3],
           [    4,     5,    10]], dtype=torch.int32)
</code></pre>
<ol start="5">
<li>获取阶数：<code>tensor.dim()</code></li>
</ol>
<pre><code class="language-python">   In [77]: x.dim()
   Out[77]: 2
</code></pre>
<ol start="7">
<li>获取最大值：<code>tensor.max()</code></li>
</ol>
<pre><code class="language-python">   In [78]: x.max()
   Out[78]: tensor(10, dtype=torch.int32)
</code></pre>
<ol start="8">
<li>转置：<code>tensor.t()</code></li>
</ol>
<pre><code class="language-python">   In [79]: x.t()
   Out[79]:
   tensor([[    1,     3,     5],
           [    2,     4,   10]], dtype=torch.int32)
</code></pre>
<ol start="9">
<li>
<p><code>tensor[1,3]</code> 获取tensor中第一行第三列的值</p>
</li>
<li>
<p><code>tensor[1,3]=100</code> 对tensor中第一行第三列的位置进行赋值100</p>
</li>
<li>
<p>tensor的切片</p>
</li>
</ol>
<pre><code class="language-python">   In [101]: x
   Out[101]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])

   In [102]: x[:,1]
   Out[102]: tensor([1.9439, 1.9575, 1.0123, 1.5939, 1.4858])
</code></pre>
<h2>4. tensor的数据类型</h2>
<ol>
<li>获取tensor的数据类型:<code>tensor.dtype</code></li>
</ol>
<pre><code class="language-python">   In [80]: x.dtype
   Out[80]: torch.int32
</code></pre>
<ol start="2">
<li>创建数据的时候指定类型</li>
</ol>
<pre><code class="language-python">   In [88]: torch.ones([2,3],dtype=torch.float32)
   Out[88]:
   tensor([[9.1167e+18, 0.0000e+00, 7.8796e+15],
           [8.3097e-43, 0.0000e+00, -0.0000e+00]])
</code></pre>
<ol start="3">
<li>类型的修改</li>
</ol>
<pre><code class="language-python">   In [17]: a
   Out[17]: tensor([1, 2], dtype=torch.int32)

   In [18]: a.type(torch.float)
   Out[18]: tensor([1., 2.])

   In [19]: a.double()
   Out[19]: tensor([1., 2.], dtype=torch.float64)
</code></pre>
<h2>5. tensor的其他操作</h2>
<ol>
<li>tensor和tensor相加</li>
</ol>
<pre><code class="language-python">   In [94]: x = x.new_ones(5, 3, dtype=torch.float)

   In [95]: y = torch.rand(5, 3)

   In [96]: x+y
   Out[96]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])
   In [98]: torch.add(x,y)
   Out[98]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])
   In [99]: x.add(y)
   Out[99]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])
   In [100]: x.add_(y)  #带下划线的方法会对x进行就地修改
   Out[100]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])

   In [101]: x #x发生改变
   Out[101]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])
</code></pre>
<p>注意: 带下划线的方法(比如: <code>add_</code> )会对tensor进行就地修改</p>
<ol start="2">
<li>tensor和数字操作</li>
</ol>
<pre><code class="language-python">   In [97]: x +10
   Out[97]:
   tensor([[11., 11., 11.],
           [11., 11., 11.],
           [11., 11., 11.],
           [11., 11., 11.],
           [11., 11., 11.]])
</code></pre>
<ol start="3">
<li>CUDA中的tensor</li>
</ol>
<p>CUDA(Compute Unified Device Architecture), 是NVIDIA推出的运算平台. CUDA™是一种由NVIDIA推出的通用并行计算架构, 该架构使GPU能够解决复杂的计算问题.</p>
<p><code>torch.cuda</code> 这个模块增加了对CUDA tensor的支持, 能够在cpu和gpu上使用相同的方法操作tensor</p>
<p>通过 <code>.to</code> 方法能够把一个tensor转移到另外一个设备(比如从CPU转到GPU)</p>
<pre><code class="language-python">   #device = torch.device(&quot;cuda:0&quot; if torch.cuda.is_available() else &quot;cpu&quot;)
   if torch.cuda.is_available():
       device = torch.device(&quot;cuda&quot;)          # cuda device对象
       y = torch.ones_like(x, device=device)  # 创建一个在cuda上的tensor
       x = x.to(device)                       # 使用方法把x转为cuda 的tensor
       z = x + y
       print(z)
       print(z.to(&quot;cpu&quot;, torch.double))       # .to方法也能够同时设置类型

   &gt;&gt;tensor([1.9806], device='cuda:0')
   &gt;&gt;tensor([1.9806], dtype=torch.float64)
</code></pre>
<p>通过前面的学习, 可以发现torch的各种操作几乎和numpy一样</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[关于深度学习Pytorch]]></title>
            <link>https://ryanuo.cc/zh/posts/pytorch</link>
            <guid>https://ryanuo.cc/zh/posts/pytorch</guid>
            <pubDate>Wed, 01 Dec 2021 11:34:19 GMT</pubDate>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>深度学习</h2>
<ul>
<li>机器语言的分支, 对数据进行特征学习, 人工神经网络为基础</li>
</ul>
<h3>机器学习和深度学习的区别</h3>
<ul>
<li>特征抽取: 01. 人工的特征抽取的过程 02. 深度学习：自动的进行特征抽取</li>
<li>数据量: 01. 机器学习：数据少 02. 深度学习：数据多</li>
</ul>
<h3>深度学习的应用场景</h3>
<ol>
<li>图像识别：物体识别场景识别人脸检测跟踪人脸身份认证自然语言处理技术</li>
<li>机器翻译：文本识别聊天对话语音技术</li>
<li>语音识别</li>
</ol>
<h3>神经网络</h3>
<ol>
<li>人工神经网络（英语：Artificial Neural Network，ANN），简称神经网络（Neural Network，NN）或类神经网络，是一种模仿生物神经网络（动物的中枢神经系统，特别是大脑）的结构和功能的数学模型，用于对函数进行估计或近似。<br>
和其他机器学习方法一样, 神经网络已经被用于解决各种各样的问题, 例如机器视觉和语音识别. 这些问题都是很难被传统基于规则的编程所解决的.</li>
<li>模拟生物的神经元，对函数进行评估或者近似。神经网络中的基础单元，相互连接，组成神经网络</li>
<li><code>t=f(W^TA+b)</code> 一个神经元的功能是求得输入向量与权向量的内积后, 经一个非线性传递函数得到一个标量结果.</li>
</ol>
<h3>单项神经网络</h3>
<ul>
<li>最基本的神经元形式由有限个神经元构成, 所有神经元的输入向量都是同一个向量. 由于每一个神经元都会产生一个标量结果, 所以单层神经元的输出是一个向量, 向量的维数等于神经元的数目.</li>
</ul>
<h3>感知机</h3>
<ul>
<li>两层神经网络组成, 输出层输入层, 感知机由两层神经网络组成, 输入层接收外界输入信号后传递给输出层(输出+1正例, -1反例), 输出层是 M-P 神经元</li>
<li>作用: 把一个n维向量空间用一个超平面分割成两部分, 给定一个输入向量, 超平面可以判断出这个向量位于超平面的哪一边, 得到输入时正类或者是反类, 对应到2维空间就是一条直线把一个平面分为两个部分.</li>
</ul>
<h2>初步使用</h2>
<h3>Pytorch的入门使用</h3>
<h2>目标</h2>
<ol>
<li>知道张量和Pytorch中的张量</li>
<li>知道pytorch中如何创建张量</li>
<li>知道pytorch中tensor的常见方法</li>
<li>知道pytorch中tensor的数据类型</li>
<li>知道pytorch中如何实现tensor在cpu和cuda中转化</li>
</ol>
<h2>1. 张量Tensor</h2>
<p>张量是一个统称, 其中包含很多类型:</p>
<ol>
<li>0阶张量：标量、常数，0-D Tensor</li>
<li>1阶张量：向量，1-D Tensor</li>
<li>2阶张量：矩阵，2-D Tensor</li>
<li>3阶张量</li>
<li>...</li>
<li>N阶张量</li>
</ol>
<h2>2. Pytorch中创建张量</h2>
<ol>
<li>使用python中的列表或者序列创建tensor</li>
</ol>
<pre><code class="language-python">   torch.tensor([[1., -1.], [1., -1.]])
   tensor([[ 1.0000, -1.0000],
           [ 1.0000, -1.0000]])
</code></pre>
<ol start="2">
<li>使用numpy中的数组创建tensor</li>
</ol>
<pre><code class="language-python">   torch.tensor(np.array([[1, 2, 3], [4, 5, 6]]))
   tensor([[ 1,  2,  3],
           [ 4,  5,  6]])
</code></pre>
<ol start="3">
<li>
<p>使用torch的api创建tensor</p>
</li>
<li>
<p><code>torch.empty(3, 4)</code> 创建3行4列的空的tensor, 会用无用数据进行填充</p>
</li>
<li>
<p><code>torch.ones([3,4])</code> 创建3行4列的<strong>全为1</strong>的tensor</p>
</li>
<li>
<p><code>torch.zeros([3, 4])</code> 创建3行4列的<strong>全为0</strong>的tensor</p>
</li>
<li>
<p><code>torch.rand([3,4])</code> 创建3行4列的<strong>随机值</strong>的tensor, 随机值的区间是 <code>[0, 1)</code></p>
</li>
</ol>
<pre><code class="language-python">      &gt;&gt;&gt; torch.rand(2, 3)
      tensor([[ 0.8237,  0.5781,  0.6879],
      [ 0.3816,  0.7249,  0.0998]])
</code></pre>
<ol start="4">
<li><code>torch.randint(low=0,high=10,size=[3,4])</code> 创建3行4列的<strong>随机整数</strong>的tensor, 随机值的区间是 <code>[low, high)</code></li>
</ol>
<pre><code class="language-python">      &gt;&gt;&gt; torch.randint(3, 10, (2, 2))
      tensor([[4, 5],
      [6, 7]])
</code></pre>
<ol start="5">
<li><code>torch.randn([3,4])</code> 创建3行4列的<strong>随机数</strong>的tensor, 随机值的分布式均值为0, 方差为1</li>
</ol>
<h2>3. Pytorch中tensor的常用方法</h2>
<ol>
<li>获取tensor中的数据(当tensor中只有一个元素可用)：<code>tensor.item()</code></li>
</ol>
<pre><code class="language-python">   In [10]: a = torch.tensor(np.arange(1))

   In [11]: a
   Out[11]: tensor([0])

   In [12]: a.item()
   Out[12]: 0
</code></pre>
<ol start="2">
<li>转化为numpy数组</li>
</ol>
<pre><code class="language-python">   In [55]: z.numpy()
   Out[55]:
   array([[-2.5871205],
          [ 7.3690367],
          [-2.4918075]], dtype=float32)
</code></pre>
<ol start="3">
<li>获取形状：<code>tensor.size()</code></li>
</ol>
<pre><code class="language-python">   In [72]: x
   Out[72]:
   tensor([[    1,     2],
           [    3,     4],
           [    5,    10]], dtype=torch.int32)

   In [73]: x.size()
   Out[73]: torch.Size([3, 2])
</code></pre>
<ol start="4">
<li>形状改变：<code>tensor.view((3,4))</code>。类似numpy中的reshape，是一种浅拷贝，仅仅是形状发生改变</li>
</ol>
<pre><code class="language-python">   In [76]: x.view(2,3)
   Out[76]:
   tensor([[    1,     2,     3],
           [    4,     5,    10]], dtype=torch.int32)
</code></pre>
<ol start="5">
<li>获取阶数：<code>tensor.dim()</code></li>
</ol>
<pre><code class="language-python">   In [77]: x.dim()
   Out[77]: 2
</code></pre>
<ol start="7">
<li>获取最大值：<code>tensor.max()</code></li>
</ol>
<pre><code class="language-python">   In [78]: x.max()
   Out[78]: tensor(10, dtype=torch.int32)
</code></pre>
<ol start="8">
<li>转置：<code>tensor.t()</code></li>
</ol>
<pre><code class="language-python">   In [79]: x.t()
   Out[79]:
   tensor([[    1,     3,     5],
           [    2,     4, 10]], dtype=torch.int32)
</code></pre>
<ol start="9">
<li>
<p><code>tensor[1,3]</code> 获取tensor中第一行第三列的值</p>
</li>
<li>
<p><code>tensor[1,3]=100</code> 对tensor中第一行第三列的位置进行赋值100</p>
</li>
<li>
<p>tensor的切片</p>
</li>
</ol>
<pre><code class="language-python">   In [101]: x
   Out[101]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])

   In [102]: x[:,1]
   Out[102]: tensor([1.9439, 1.9575, 1.0123, 1.5939, 1.4858])
</code></pre>
<h2>4. tensor的数据类型</h2>
<ol>
<li>获取tensor的数据类型:<code>tensor.dtype</code></li>
</ol>
<pre><code class="language-python">   In [80]: x.dtype
   Out[80]: torch.int32
</code></pre>
<ol start="2">
<li>创建数据的时候指定类型</li>
</ol>
<pre><code class="language-python">   In [88]: torch.ones([2,3],dtype=torch.float32)
   Out[88]:
   tensor([[9.1167e+18, 0.0000e+00, 7.8796e+15],
           [8.3097e-43, 0.0000e+00, -0.0000e+00]])
</code></pre>
<ol start="3">
<li>类型的修改</li>
</ol>
<pre><code class="language-python">   In [17]: a
   Out[17]: tensor([1, 2], dtype=torch.int32)

   In [18]: a.type(torch.float)
   Out[18]: tensor([1., 2.])

   In [19]: a.double()
   Out[19]: tensor([1., 2.], dtype=torch.float64)
</code></pre>
<h2>5. tensor的其他操作</h2>
<ol>
<li>tensor和tensor相加</li>
</ol>
<pre><code class="language-python">   In [94]: x = x.new_ones(5, 3, dtype=torch.float)

   In [95]: y = torch.rand(5, 3)

   In [96]: x+y
   Out[96]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])
   In [98]: torch.add(x,y)
   Out[98]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])
   In [99]: x.add(y)
   Out[99]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])
   In [100]: x.add_(y)  #带下划线的方法会对x进行就地修改
   Out[100]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])

   In [101]: x #x发生改变
   Out[101]:
   tensor([[1.6437, 1.9439, 1.5393],
           [1.3491, 1.9575, 1.0552],
           [1.5106, 1.0123, 1.0961],
           [1.4382, 1.5939, 1.5012],
           [1.5267, 1.4858, 1.4007]])
</code></pre>
<p>注意: 带下划线的方法(比如: <code>add_</code> )会对tensor进行就地修改</p>
<ol start="2">
<li>tensor和数字操作</li>
</ol>
<pre><code class="language-python">   In [97]: x +10
   Out[97]:
   tensor([[11., 11., 11.],
           [11., 11., 11.],
           [11., 11., 11.],
           [11., 11., 11.],
           [11., 11., 11.]])
</code></pre>
<ol start="3">
<li>CUDA中的tensor</li>
</ol>
<p>CUDA(Compute Unified Device Architecture), 是NVIDIA推出的运算平台. CUDA™是一种由NVIDIA推出的通用并行计算架构, 该架构使GPU能够解决复杂的计算问题.</p>
<p><code>torch.cuda</code> 这个模块增加了对CUDA tensor的支持, 能够在cpu和gpu上使用相同的方法操作tensor</p>
<p>通过 <code>.to</code> 方法能够把一个tensor转移到另外一个设备(比如从CPU转到GPU)</p>
<pre><code class="language-python">   #device = torch.device(&quot;cuda:0&quot; if torch.cuda.is_available() else &quot;cpu&quot;)
   if torch.cuda.is_available():
       device = torch.device(&quot;cuda&quot;)          # cuda device对象
       y = torch.ones_like(x, device=device)  # 创建一个在cuda上的tensor
       x = x.to(device)                       # 使用方法把x转为cuda 的tensor
       z = x + y
       print(z)
       print(z.to(&quot;cpu&quot;, torch.double))       # .to方法也能够同时设置类型

   &gt;&gt;tensor([1.9806], device='cuda:0')
   &gt;&gt;tensor([1.9806], dtype=torch.float64)
</code></pre>
<p>通过前面的学习, 可以发现torch的各种操作几乎和numpy一样</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[ES6 Promise Advanced Usage]]></title>
            <link>https://ryanuo.cc/posts/es6</link>
            <guid>https://ryanuo.cc/posts/es6</guid>
            <pubDate>Sun, 12 Sep 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[ES6 module - Promise - Advanced Usage]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>前端模块化规范</h2>
<ul>
<li><code>AMD</code> <code>CMD</code> <code>CommonJS</code></li>
<li>ES6模块化规范取代之前的非标准语法, 导入使用<code>import</code> 共享使用<code>export</code></li>
<li>配置package.json 加入 <code>type:&quot;module&quot;</code></li>
</ul>
<h2>es6模块化三种用法</h2>
<h3>默认导出, 默认导入</h3>
<ul>
<li>默认导出</li>
</ul>
<pre><code class="language-js">const n1 = 10
const n2 = 20

function show() {
  console.log(1)
}

export default {
  n1,
  show
}
</code></pre>
<ul>
<li>默认导入</li>
</ul>
<pre><code class="language-js">import m1 from './index.js'

console.log(m1)
</code></pre>
<h3>按需导出导入</h3>
<ul>
<li>名称保持一致</li>
<li>可以使用 <code>as</code> 重新命名</li>
<li>可以和默认导出配合使用</li>
</ul>
<pre><code class="language-js">// 按需导出
export const n1 = 'aaa'
export const n2 = 'vcv'
export function say() {}

export default {
  a: 20
}
</code></pre>
<pre><code class="language-js">// 按需导入,info 表示默认导出
import info, { n1 } from './index.js'

console.log(n1)
</code></pre>
<h3>直接导入</h3>
<ul>
<li>直接使用import, 直接导入执行模块中的代码</li>
</ul>
<h2>Promise</h2>
<ul>
<li>解决回调地狱的问题, 构造函数</li>
<li>使用promise读取文件</li>
</ul>
<pre><code class="language-js">import thenfs from 'then-fs'

thenfs.readFile('./1.txt', 'utf-8').then((res) =&gt; {
  console.log(res)
  return thenfs.readFile('./2.txt', 'utf-8')
}).then((res) =&gt; {
  console.log(res)
  return thenfs.readFile('./3.txt', 'utf-8')
}).then((res) =&gt; {
  console.log(res)
})
</code></pre>
<ul>
<li>使用.catch 捕获错误 如果放到最后, 前面如果发生错误, 无法执行其他正确的程序;</li>
</ul>
<h3>使用promise.all方法</h3>
<ul>
<li>等待所有的异步操作完成后执行</li>
</ul>
<pre><code class="language-js">import thenfs from 'then-fs'

const promiseArr = [
  thenfs.readFile('./1.txt', 'utf-8'),
  thenfs.readFile('./2.txt', 'utf-8'),
  thenfs.readFile('./3.txt', 'utf-8'),
]
Promise.all(promiseArr).then((res) =&gt; {
  console.log(res)
})
</code></pre>
<h3>使用promise.race方法</h3>
<ul>
<li>返回执行速度最快的promise</li>
</ul>
<pre><code class="language-js">import thenfs from 'then-fs'

const promiseArr = [
  thenfs.readFile('./1.txt', 'utf-8'),
  thenfs.readFile('./2.txt', 'utf-8'),
  thenfs.readFile('./3.txt', 'utf-8'),
]
Promise.race(promiseArr).then((res) =&gt; {
  console.log(res)
})
</code></pre>
<h3>创建异步操作</h3>
<ul>
<li>失败的回调函数可以省略不写, 使用.catch来捕获</li>
</ul>
<pre><code class="language-js">import fs from 'node:fs'

function getFile() {
  return new Promise((resolve, reject) =&gt; {
    fs.readFile(fpath, 'utf8', (err, datastr) =&gt; {
      if (err)
        return reject(err)
      resolve(datastr)
    })
  })
}

getFile('./111.txt').then((res) =&gt; {
  console.log(res)
}, (err) =&gt; {
  console.log(err.message)
})
</code></pre>
<h3>使用async/await获取返回值</h3>
<pre><code class="language-js">async function getfile() {
  const a = await thenfs.readFile('./1.txt', 'utf-8')
  console.log(a)
}

getfile()
</code></pre>
<ul>
<li>注意事项</li>
<li>使用await必须使用async来修饰</li>
<li>在await第一次出现之前的代码都是同步执行, 之后的都是等待异步执行完成后执行</li>
</ul>
<h2>同步任务和异步任务</h2>
<h2>EventLoop</h2>
<h2>宏任务 微任务</h2>
<ul>
<li>宏任务</li>
</ul>
<ol>
<li>异步Ajax请求</li>
<li>setTimeout,setInterval</li>
<li>文件操作</li>
<li>其他宏任务</li>
</ol>
<ul>
<li>微任务</li>
</ul>
<ol>
<li>Promise.then .catch,.finally</li>
<li>process.nextTick</li>
<li>其他微任务</li>
</ol>
<ul>
<li>每一个宏任务执行完之后, 都会进行一次判断微任务队列中是否存在未执行的微任务, 如果存在微任务 需要将所有的微任务执行完成后在继续执行下一个宏任务</li>
</ul>
<h2>try catch 捕获错误</h2>
<ul>
<li>使用try catch 来捕获错误, 并且在catch中进行处理</li>
</ul>
<pre><code class="language-js">import db from '../db/index.js'

export async function getAlluser(req, res) {
  try {
    const [rows] = await db.query('select age,username from ev_users')
    res.send({
      status: 200,
      data: rows,
      message: '数据获取成功'
    })
  }
  catch (error) {
    res.send({
      status: -1,
      message: '请求出错'
    })
  }
}
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[ES6模块化-Promise-高级用法]]></title>
            <link>https://ryanuo.cc/zh/posts/es6</link>
            <guid>https://ryanuo.cc/zh/posts/es6</guid>
            <pubDate>Sun, 12 Sep 2021 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>前端模块化规范</h2>
<ul>
<li><code>AMD</code> <code>CMD</code> <code>CommonJS</code></li>
<li>ES6模块化规范取代之前的非标准语法, 导入使用<code>import</code> 共享使用<code>export</code></li>
<li>配置package.json 加入 <code>type:&quot;module&quot;</code></li>
</ul>
<h2>es6模块化三种用法</h2>
<h3>默认导出, 默认导入</h3>
<ul>
<li>默认导出</li>
</ul>
<pre><code class="language-js">const n1 = 10
const n2 = 20

function show() {
  console.log(1)
}

export default {
  n1,
  show
}
</code></pre>
<ul>
<li>默认导入</li>
</ul>
<pre><code class="language-js">import m1 from './index.js'

console.log(m1)
</code></pre>
<h3>按需导出导入</h3>
<ul>
<li>名称保持一致</li>
<li>可以使用 <code>as</code> 重新命名</li>
<li>可以和默认导出配合使用</li>
</ul>
<pre><code class="language-js">// 按需导出
export const n1 = 'aaa'
export const n2 = 'vcv'
export function say() {}

export default {
  a: 20
}
</code></pre>
<pre><code class="language-js">// 按需导入,info 表示默认导出
import info, { n1 } from './index.js'

console.log(n1)
</code></pre>
<h3>直接导入</h3>
<ul>
<li>直接使用import, 直接导入执行模块中的代码</li>
</ul>
<h2>Promise</h2>
<ul>
<li>解决回调地狱的问题, 构造函数</li>
<li>使用promise读取文件</li>
</ul>
<pre><code class="language-js">import thenfs from 'then-fs'

thenfs.readFile('./1.txt', 'utf-8').then((res) =&gt; {
  console.log(res)
  return thenfs.readFile('./2.txt', 'utf-8')
}).then((res) =&gt; {
  console.log(res)
  return thenfs.readFile('./3.txt', 'utf-8')
}).then((res) =&gt; {
  console.log(res)
})
</code></pre>
<ul>
<li>使用.catch 捕获错误 如果放到最后, 前面如果发生错误, 无法执行其他正确的程序;</li>
</ul>
<h3>使用promise.all方法</h3>
<ul>
<li>等待所有的异步操作完成后执行</li>
</ul>
<pre><code class="language-js">import thenfs from 'then-fs'

const promiseArr = [
  thenfs.readFile('./1.txt', 'utf-8'),
  thenfs.readFile('./2.txt', 'utf-8'),
  thenfs.readFile('./3.txt', 'utf-8'),
]
Promise.all(promiseArr).then((res) =&gt; {
  console.log(res)
})
</code></pre>
<h3>使用promise.race方法</h3>
<ul>
<li>返回执行速度最快的promise</li>
</ul>
<pre><code class="language-js">import thenfs from 'then-fs'

const promiseArr = [
  thenfs.readFile('./1.txt', 'utf-8'),
  thenfs.readFile('./2.txt', 'utf-8'),
  thenfs.readFile('./3.txt', 'utf-8'),
]
Promise.race(promiseArr).then((res) =&gt; {
  console.log(res)
})
</code></pre>
<h3>创建异步操作</h3>
<ul>
<li>失败的回调函数可以省略不写, 使用.catch来捕获</li>
</ul>
<pre><code class="language-js">import fs from 'node:fs'

function getFile() {
  return new Promise((resolve, reject) =&gt; {
    fs.readFile(fpath, 'utf8', (err, datastr) =&gt; {
      if (err)
        return reject(err)
      resolve(datastr)
    })
  })
}

getFile('./111.txt').then((res) =&gt; {
  console.log(res)
}, (err) =&gt; {
  console.log(err.message)
})
</code></pre>
<h3>使用async/await获取返回值</h3>
<pre><code class="language-js">async function getfile() {
  const a = await thenfs.readFile('./1.txt', 'utf-8')
  console.log(a)
}

getfile()
</code></pre>
<ul>
<li>注意事项</li>
<li>使用await必须使用async来修饰</li>
<li>在await第一次出现之前的代码都是同步执行, 之后的都是等待异步执行完成后执行</li>
</ul>
<h2>同步任务和异步任务</h2>
<h2>EventLoop</h2>
<h2>宏任务 微任务</h2>
<ul>
<li>宏任务</li>
</ul>
<ol>
<li>异步Ajax请求</li>
<li>setTimeout,setInterval</li>
<li>文件操作</li>
<li>其他宏任务</li>
</ol>
<ul>
<li>微任务</li>
</ul>
<ol>
<li>Promise.then .catch,.finally</li>
<li>process.nextTick</li>
<li>其他微任务</li>
</ol>
<ul>
<li>每一个宏任务执行完之后, 都会进行一次判断微任务队列中是否存在未执行的微任务, 如果存在微任务 需要将所有的微任务执行完成后在继续执行下一个宏任务</li>
</ul>
<h2>try catch 捕获错误</h2>
<ul>
<li>使用try catch 来捕获错误, 并且在catch中进行处理</li>
</ul>
<pre><code class="language-js">import db from '../db/index.js'

export async function getAlluser(req, res) {
  try {
    const [rows] = await db.query('select age,username from ev_users')
    res.send({
      status: 200,
      data: rows,
      message: '数据获取成功'
    })
  }
  catch (error) {
    res.send({
      status: -1,
      message: '请求出错'
    })
  }
}
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Python Web Crawling: XPath, BeautifulSoup, re, Selenium]]></title>
            <link>https://ryanuo.cc/posts/re-soup</link>
            <guid>https://ryanuo.cc/posts/re-soup</guid>
            <pubDate>Sun, 22 Aug 2021 10:34:06 GMT</pubDate>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>Project Code Display</h2>
<p><img src="https://cloud.ryanuo.cc/hexo/0/20210822143150.png" alt="Partial Code"></p>
<ul>
<li>Project deployment method has been uploaded to GitHub. Project address: <a href="https://github.com/ryanuo/hot_search">GitHub Address</a></li>
</ul>
<h2>Technologies Used</h2>
<ul>
<li>Python regular expressions</li>
<li>BeautifulSoup4 library</li>
<li>XPath parsing</li>
</ul>
<h2>Regular Expressions</h2>
<ul>
<li>Similar to JavaScript matching methods</li>
<li>Import the <code>re</code> package before use</li>
<li>Several matching methods: match, search, compile, findall, finditer</li>
</ul>
<h3>re.match(a, b, c)</h3>
<ul>
<li>Three parameters: matching rule, string to match, matching mode</li>
<li>Matches from the first position of the string. If it satisfies, <code>.span()</code> returns its index position; otherwise, returns <code>None</code></li>
<li><code>result.groups()</code> returns a tuple containing all groups of strings. Use <code>group(num)</code> to return a tuple with the corresponding value (starting from 1)</li>
</ul>
<h3>re.search(a, b, c)</h3>
<ul>
<li>Also three parameters, same as above</li>
<li>The method to get tuples is the same. The only difference is that <code>search</code> does not match from the beginning but returns the first successful match if the string contains the content to match</li>
<li>Note: Only returns one match, not multiple</li>
</ul>
<h3>re.sub(a, b, c, d, e)</h3>
<ul>
<li>Performs <code>replacement</code> operations</li>
<li><code>a</code>: Pattern string in the regular expression</li>
<li><code>b</code>: String to replace, can also be a function</li>
<li><code>c</code>: Original string</li>
<li><code>d</code>: Maximum number of replacements after matching, default is 0 (replace all matches)</li>
<li><code>e</code>: Matching mode, numeric form</li>
</ul>
<h3>re.compile(a, b)</h3>
<ul>
<li>Compiles a regular expression for use by the match and search functions</li>
<li>If using the match method, the group method can omit the parameter or use 0</li>
<li>The group method's parameter value depends on the number of tuples in your regular expression</li>
<li><code>start</code>, <code>end</code>, and <code>span</code> methods return the index position of the matched character in the original string</li>
</ul>
<h3>findall(a, b, c, d)</h3>
<ul>
<li>Parameters: regular expression, string to match, start position, end position</li>
<li>Returns all substrings that meet the conditions in a list. If none, returns an empty list</li>
<li>If there are tuples, returns characters that meet the tuple rules, which can be iterated</li>
</ul>
<h3>finditer(a, b, c)</h3>
<ul>
<li>Parameters: matching rule, string to match, matching mode</li>
<li>Similar to <code>findall</code>, but returns an iterator that can be used with <code>for in</code></li>
</ul>
<h3>re.split(a, b, c, d)</h3>
<ul>
<li>Splits the matching string into a list based on the matching rule</li>
<li>Parameters: matching rule, string to split, <code>number of splits (default is 0, unlimited)</code>, matching mode</li>
</ul>
<h3>Regular Expression Modifiers</h3>
<ul>
<li><code>re.I</code>: Case-insensitive matching</li>
<li><code>re.L</code>: Locale-aware matching</li>
<li><code>re.S</code>: Makes <code>.</code> match all characters, including newlines</li>
<li><code>re.M</code>: Multi-line matching, affects <code>^</code> and <code>$</code></li>
<li><code>re.U</code>: Parses characters based on the Unicode character set. Affects <code>\w</code>, <code>\W</code>, <code>\b</code>, <code>\B</code></li>
<li><code>re.X</code>: Allows more flexible formatting for better readability of regular expressions</li>
</ul>
<p><a href="https://blog.csdn.net/weixin_43347550/article/details/105158003">Python Regular Expression Detailed Explanation (Super Detailed, Must Learn!)</a></p>
<h2>XPath Method</h2>
<ul>
<li>Install the <code>lxml</code> library first: <code>pip install lxml</code></li>
<li>XPath uses path expressions to navigate XML documents</li>
<li>Can parse local HTML files or directly parse HTML strings</li>
</ul>
<h3>Common XPath Rules</h3>
<ul>
<li><code>nodename</code>: Selects all child nodes</li>
<li><code>/</code>: Selects child nodes of the current node</li>
<li><code>//</code>: Selects descendants of the current node</li>
<li><code>.</code>: Selects the current node</li>
<li><code>..</code>: Selects the parent node of the current node</li>
<li><code>@</code>: Selects attributes</li>
</ul>
<h3>Local Display</h3>
<p><img src="https://cloud.ryanuo.cc/hexo/0/20210822191052.png" alt="The following code uses this example"></p>
<!-- Two methods to parse local files -->
<ul>
<li>First method: Use <code>etree.parse</code> to parse locally</li>
</ul>
<pre><code class="language-python"># coding= utf-8
from lxml import etree
html = etree.parse('./index.html', etree.HTMLParser())
print(etree.tostring(html))
</code></pre>
<ul>
<li>Second method: Use <code>etree.HTML</code></li>
</ul>
<pre><code class="language-python"># coding= utf-8
from lxml import etree
fp = open('./index.html', 'rb')
html = fp.read().decode('utf-8')
selector = etree.HTML(html)  # etree.HTML(source) recognizes it as an object that can be parsed by XPath
print(selector)
</code></pre>
<ul>
<li>Match all nodes using the <code>//*</code> rule</li>
<li>Match all specified nodes using <code>//node_name</code></li>
<li>Match all child nodes by replacing <code>//</code> with <code>/</code></li>
<li>Get parent node attribute values using <code>../@attribute_name</code></li>
<li>Attribute matching can use the <code>@attribute_name</code> method</li>
<li>Text retrieval has two methods: <code>/text()</code> and <code>//text()</code>. The first directly retrieves text, while the second retrieves special characters generated by line breaks</li>
<li>Retrieve attributes using <code>/@href</code></li>
<li>For attributes containing multiple values, use the <code>contains()</code> method</li>
<li>Multi-attribute matching uses the <code>and</code> operator with the <code>contains()</code> method</li>
</ul>
<h3>XPath Operators</h3>
<ul>
<li>Division and modulo are special; others are the same as basic operators</li>
<li>Division uses <code>div</code>, e.g., <code>8 div 4</code></li>
<li>Modulo uses <code>mod</code>, e.g., <code>1 mod 2</code></li>
<li>Also, <code>and</code> and <code>or</code> represent conjunction and disjunction</li>
</ul>
<h3>Sequential Selection</h3>
<ul>
<li>XPath has over 100 built-in functions. Refer to <a href="http://www.w3school.com.cn/xpath/xpath_functions.asp">XPath Functions</a> for details</li>
</ul>
<p><img src="https://cloud.ryanuo.cc/hexo/0/20210822182351.png" alt="Sequential"></p>
<h3>Node Axis Selection</h3>
<ul>
<li>Get the <code>href</code> attribute value of all <code>a</code> nodes under the current node: <code>child::a/@href</code></li>
<li>Get the attribute value of the specified element of the current node: <code>attribute::attribute_name</code></li>
<li>Get all child elements of the current node: <code>child::*</code></li>
<li>Get the attribute values of all attributes of the current node: <code>attribute::*</code></li>
<li>Get all child nodes of the current node: <code>child::node()</code></li>
<li>Get all text child nodes of the current element: <code>child::text()</code></li>
<li>Get all ancestor <code>li</code> elements of the current element (including the current element): <code>ancestor-or-self::element</code></li>
</ul>
<p><a href="https://www.w3school.com.cn/xpath/xpath_axes.asp">XPath Axes</a><br>
<a href="https://blog.csdn.net/Ryan_lee9410/article/details/107144213">XPath Pitfalls Guide</a></p>
<p><img src="https://cloud.ryanuo.cc/hexo/0/20210822185037.png" alt="XPath Axes"></p>
<h3>Demo Code</h3>
<pre><code class="language-python"># coding= utf-8
from lxml import etree
# fp = open('./index.html', 'rb')
# html = fp.read().decode('utf-8')   #.decode('gbk')
# selector = etree.HTML(html)   #etree.HTML(source) recognizes it as an object that can be parsed by XPath
# print(selector)

html = etree.parse('./index.html',etree.HTMLParser())
# print(etree.tostring(html).decode('utf-8'))

all_node = html.xpath('//*')  # Get all nodes: //*
part_node = html.xpath('//li')  # Get part of the nodes: //node_name
child_node = html.xpath('//li/a')  # Match child nodes
parent_node = html.xpath('//a[@href=&quot;//ryanuo.cc&quot;]/../@class')  # Get parent node attribute value: ../@attribute_name
attrs_node = html.xpath('//a[contains(@class,&quot;a&quot;)]/text()')   # Match attributes containing multiple values: contains() method
# Sequential retrieval
first_node = html.xpath('//li[1]/a/text()')  # Get the first node
last_node = html.xpath('//li[last()]//text()')   # Get the last node
front_node = html.xpath('//li[position()&lt;3]//text()')    # Get the first two nodes
end_ndoe = html.xpath('//li[last()-2]//text()')   # Get the third-to-last node

# Axis nodes
child_node_z = html.xpath('//li[position()&lt;2]/child::a/@href')  # Get the `href` attribute value of all `a` nodes under the current node
attribute_node = html.xpath('//li[2]//attribute::lang')  # Get the attribute value of the specified element of the current node
all_child_node = html.xpath('//ul/li[last()-1]//child::*')  # Get all child elements of the current node
all_attrs_node = html.xpath('//li[1]/a/attribute::*')  # Get the attribute values of all attributes of the current node
all_child_text_node = html.xpath('//li[1]//child::text()')  # Get all text child nodes of the current element
all_child_node_node = html.xpath('//li[1]/a/child::node()')  # Get all child nodes of the current node
ancestor_self = html.xpath('//a[@title=&quot;1&quot;]/../ancestor-or-self::li') # Get all ancestor `li` elements of the current element (including the current element)
print(ancestor_self)
</code></pre>
<h2>BeautifulSoup4 Usage</h2>
<ul>
<li><code>Beautiful Soup</code> automatically converts input documents to Unicode and output documents to UTF-8</li>
<li>Install using <code>pip install beautifulsoup4</code></li>
<li>Import using <code>from bs4 import BeautifulSoup</code></li>
</ul>
<h3>Getting Content</h3>
<ul>
<li>Tags have two important attributes: <code>name</code> and <code>attrs</code></li>
<li>There are three methods to get text content:</li>
<li><code>.string</code> method returns an iterator</li>
<li><code>.text</code> method returns node text</li>
<li><code>.get_text()</code> method returns node text</li>
</ul>
<pre><code class="language-python">
## Get title object

print(soup.title)  # &lt;title&gt;XPath Method&lt;/title&gt;
# Get title content
print(soup.title.string)  # Returns an iterator
print(soup.title.text)
print(soup.title.get_text())
print(soup.find('title').get_text())
</code></pre>
<ul>
<li>Get objects through parent-child relationships</li>
</ul>
<pre><code class="language-python"># print(soup.title.parent)   # Returns parent node including content
print(soup.li.child)  # Node
print(soup.li.children)  # Returns an iterator
</code></pre>
<h3>Get the first <code>li</code> tag</h3>
<pre><code class="language-python">print(soup.li.get_text())  # Matches the first one, returns all node text information
print(soup.find('li').text)
# Get `ul` child tags (empty lines are also considered children)
print(soup.ul.children)
for index, item in enumerate(soup.ul.children):
    print(index, item)
</code></pre>
<h3>Get element attributes</h3>
<ul>
<li>Use <code>.attribute_name</code> method, but can only get one</li>
<li>Use <code>element.attrs['attribute_name']</code> method to return a list</li>
<li>If using <code>soup.element</code> twice, the first time gets the first matched element, the second time gets the second matched element</li>
</ul>
<h3>Get multiple elements</h3>
<ul>
<li><code>find</code> method gets one element</li>
<li><code>find_all</code> gets multiple elements, can use <code>limit</code> to limit the number, <code>recursive = True</code> to find descendants; <code>recursive = False</code> to find children</li>
<li>Multi-level search: <code>find_all</code> returns a list that can be iterated and used with <code>find</code> or <code>find_all</code> again to get elements</li>
</ul>
<h3>Get objects by specified attributes</h3>
<ul>
<li><code>id</code> and <code>class</code> selectors. <code>class</code> is special because it's a keyword. Use <code>class_</code> instead</li>
</ul>
<pre><code class="language-python">print(soup.find(id='a'))
print(soup.find('a', id='a'))
print(soup.find_all('a', id='a'))  # Can use index to query

# `class` is a keyword, use `class_`

print('class1', soup.find_all('a', class_='a'))
print('class2', soup.find_all('a', attrs={'class': 'item'}))  # More general
print('class3', soup.find_all('a', attrs={'class': 'item', 'id': 'a'}))  # Multiple conditions
</code></pre>
<h3>Use functions as parameters to return elements</h3>
<pre><code class="language-python">def judgeTitle1(t):
    if t == 'a':
        return True

print(soup.find_all(class_=judgeTitle1))
</code></pre>
<ul>
<li>Judge by length</li>
</ul>
<pre><code class="language-python"># Judge by length
import re  # Regular expression
reg = re.compile(&quot;item&quot;)
def judgeTitle2(t):
    # Return `t` parameter with length 5 and containing 'item'
    return len(str(t)) == 5 and bool(re.search(reg, t))
print(soup.find_all(class_=judgeTitle2))
</code></pre>
<h3>Use CSS selectors</h3>
<ul>
<li><code>select</code> method returns a list</li>
<li>Can search by tag name, attribute, tag + class + id, combination</li>
</ul>
<p><a href="https://blog.csdn.net/qq_21933615/article/details/81171951">Usage of BeautifulSoup Library in Python</a><br>
<a href="https://blog.csdn.net/love666666shen/article/details/77512353">Detailed Usage of BeautifulSoup Library in Python</a><br>
<a href="https://blog.csdn.net/IT_arookie/article/details/82824620">Python Web Scraping: Extract Text with BeautifulSoup</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Python爬虫：XPath, BeautifulSoup, re, Selenium]]></title>
            <link>https://ryanuo.cc/zh/posts/re-soup</link>
            <guid>https://ryanuo.cc/zh/posts/re-soup</guid>
            <pubDate>Sun, 22 Aug 2021 10:34:06 GMT</pubDate>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>项目代码展示</h2>
<p><img src="https://cloud.ryanuo.cc/hexo/0/20210822143150.png" alt="部分代码"></p>
<ul>
<li>项目部署方法, 已发GitHub, 项目地址<a href="https://github.com/ryanuo/hot_search">github地址</a></li>
</ul>
<h2>使用技术</h2>
<ul>
<li>python正则匹配</li>
<li>Beautifulsoup4库</li>
<li>xpath解析</li>
</ul>
<h2>正则匹配</h2>
<ul>
<li>和JavaScript语言匹配方式类似</li>
<li>使用前需导入<code>re</code>包</li>
<li>有几种正则匹配的方法:match, search, compile, findall, finditer</li>
</ul>
<h3>re.match(a, b, c)</h3>
<ul>
<li>三个参数: 匹配的规则, 要匹配的字符串, 匹配方式</li>
<li>它是从字符串的第一个位置进行匹配如果满足使用<code>.span()</code>方法可以返回它所在的索引位置, 如果不满足则返回<code>None</code></li>
<li><code>返回结果.groups()</code>返回一个包含所有小组的字符串的元组, 使用<code>group(num)</code>方法可以返回一个包含对应值的元组(从1开始)</li>
</ul>
<h3>re.search(a, b, c)</h3>
<ul>
<li>也是三个参数, 与上面相同</li>
<li>获取元组方法也相同, 唯一的不同点就是, search不是从一开始进行匹配, 而是如果字符串中包含所要匹配的内容, 则返回第一个匹配成功的</li>
<li>注意只返回一个不是多个</li>
</ul>
<h3>re.sub(a, b, c, d, e)</h3>
<ul>
<li>执行<code>替换</code>操作</li>
<li><code>a</code>正则中的模式字符串</li>
<li><code>b</code>要替换的字符串, 也可以是一个函数</li>
<li><code>c</code>原始字符串</li>
<li><code>d</code>匹配后替换的最大次数, 默认是0表示全部匹配替换</li>
<li><code>e</code>匹配模式, 数字形式</li>
</ul>
<h3>re.compile(a, b)</h3>
<ul>
<li>用来编译正则表达式, 供match和search这两个函数使用</li>
<li>如果使用的是match方法 在获取匹配的字符时使用group方法获取参数可以省略不写也可以写0</li>
<li>group方法参数的数值与你所写的正则表达式元组数有关</li>
<li>start, end, span方法都是返回匹配字符在原字符串中所在的索引位置</li>
</ul>
<h3>findall(a, b, c, d)</h3>
<ul>
<li>参数分别表示: 正则表达式, 匹配的字符串, 指定匹配的起始位置, 结束位置</li>
<li>返回满足条件的所有子串, 列表的形式, 如不则返回空列表</li>
<li>如果由有元组则返回满足元组规则的字符 可进行遍历</li>
</ul>
<h3>finditer(a, b, c)</h3>
<ul>
<li>参数分别为: 匹配规则, 匹配的字符串, 匹配模式</li>
<li>和findall方法类似, 返回值使用迭代器方式返回使用<code>for in</code>方法</li>
</ul>
<h3>re.split(a, b, c, d)</h3>
<ul>
<li>按照匹配规则将匹配的字符串进行分隔以列表的形式返回</li>
<li>参数分别为: 匹配规则, 匹配字符, <code>切割次数默认为0, 不限制次数</code>, 匹配模式</li>
</ul>
<h3>正则表达式修饰符</h3>
<ul>
<li><code>re.I</code> 是匹配时大小写不敏感</li>
<li><code>re.L</code> 做本地化识别</li>
<li><code>re.S</code> 使. 匹配包括换行在内的所有字符</li>
<li><code>re.M</code> 多行匹配影响^$</li>
<li><code>re.U</code> 根据Unicode字符集解析字符. 这个标志影响 \w, \W, \b, \B.</li>
<li><code>re.X</code> 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解.</li>
</ul>
<p>【<a href="https://blog.csdn.net/weixin_43347550/article/details/105158003">Python正则表达式详解 (超详细, 看完必会!)</a>】</p>
<h2>xPath方法</h2>
<ul>
<li>使用之前先安装好lxml库<code>pip i lxml</code></li>
<li>xPath使用路径表达式在XML文档中进行导航</li>
<li>可以对本地的html文件进行解析也可以直接对html字符串进行解析</li>
</ul>
<h3>Xpath常用的规则</h3>
<ul>
<li><code>nodename</code>选取所有的子节点</li>
<li><code>/</code>选取当前节点下的子节点</li>
<li><code>//</code>选取当前节点的子孙节点</li>
<li><code>.</code> 选取当前节点</li>
<li><code>..</code> 选取当前节点的父节点</li>
<li><code>@</code>选取属性</li>
</ul>
<h3>本地展示</h3>
<p><img src="https://cloud.ryanuo.cc/hexo/0/20210822191052.png" alt="以下代码中用到该实例"></p>
<!-- 两种方法解析本地文件 -->
<ul>
<li>第一种使用<code>etree.parse</code>方法解析本地</li>
</ul>
<pre><code class="language-python"># coding= utf-8
from lxml import etree
html = etree.parse('./index.html',etree.HTMLParser())
print(etree.tostring(html))
</code></pre>
<ul>
<li>第二种方法使用 <code>etree.HTML</code> 方法</li>
</ul>
<pre><code class="language-python"># coding= utf-8
from lxml import etree
fp = open('./index.html', 'rb')
html = fp.read().decode('utf-8')
selector = etree.HTML(html)   #etree.HTML(源码) 识别为可被xpath解析的对象
print(selector)
</code></pre>
<ul>
<li>匹配所有的节点 使用<code>//*</code>规则匹配</li>
<li>匹配所有指定的节点使用<code>//节点名称</code></li>
<li>匹配所有的子节点将//换成<code>/</code></li>
<li>获取父节点属性值的方法 <code>../@属性名</code></li>
<li>属性匹配可以使用<code>@属性名</code>的方法</li>
<li>文本获取两种方法 <code>/text()</code> 和 <code>//text()</code>, 区别第一种直接获取文本, 第二种要获取换行时产生的特殊字符</li>
<li>属性获取使用<code>/@href</code>获取</li>
<li>获取属性中包含多个值的情况 属性多值匹配 <code>contains()</code>方法</li>
<li>多属性匹配, 使用and运算符 和contains方法搭配使用</li>
</ul>
<h3>xPath运算符</h3>
<ul>
<li>除号和取余特殊, 其他的都与基本运算符一直</li>
<li>除号使用<code>div</code> 比如<code>8 div 4</code></li>
<li>取余则是<code>mod</code> 比如<code>1 mod 2</code></li>
<li>还有表示并列和或者的 <code>and</code> 和 <code>or</code></li>
</ul>
<h3>按序选择</h3>
<ul>
<li>xPath内置了100多种函数方法, 具体参考【<a href="http://www.w3school.com.cn/xpath/xpath_functions.asp">xPath函数</a>】</li>
</ul>
<p><img src="https://cloud.ryanuo.cc/hexo/0/20210822182351.png" alt="按序"></p>
<h3>节点轴选择</h3>
<ul>
<li>获取当前节点所有子元素的<code>a</code>节点的<code>href</code>属性值 <code>child::a/@href</code></li>
<li>获取当前节点的指定元素的属性值 <code>attribute:: 属性名</code></li>
<li>获取当前节点的所有的子元素 <code>child::*</code></li>
<li>获取当前节点 的所有属性的属性值 <code>attribute::*</code></li>
<li>获取当前节点所有子节点 <code>child::node()</code></li>
<li>获取当前元素所有文本子节点 <code>child::text()</code></li>
<li>获取当前元素的所有父辈为li元素的节点(包括当前元素) <code>ancestor-or-self:: 元素</code></li>
</ul>
<p><a href="https://www.w3school.com.cn/xpath/xpath_axes.asp">xPath轴</a><br>
<a href="https://blog.csdn.net/Ryan_lee9410/article/details/107144213">xPath避坑指南</a></p>
<p><img src="https://cloud.ryanuo.cc/hexo/0/20210822185037.png" alt="xPath轴"></p>
<h3>演示代码</h3>
<pre><code class="language-python"># coding= utf-8
from lxml import etree
# fp = open('./index.html', 'rb')
# html = fp.read().decode('utf-8')   #.decode('gbk')
# selector = etree.HTML(html)   #etree.HTML(源码) 识别为可被xpath解析的对象
# print(selector)

html = etree.parse('./index.html',etree.HTMLParser())
# print(etree.tostring(html).decode('utf-8'))

all_node = html.xpath('//*')  # 所有节点的获取 //*
part_node = html.xpath('//li')  # 部分节点 格式：//节点名
child_node = html.xpath('//li/a')  # 匹配子节点
parent_node = html.xpath('//a[@href=&quot;//ryanuo.cc&quot;]/../@class')  # 获取父节点属性值的方法 ../@属性名
attrs_node = html.xpath('//a[contains(@class,&quot;a&quot;)]/text()')   # 获取属性中包含多个值的情况 属性多值匹配 contains()方法
# 按序获取
first_node = html.xpath('//li[1]/a/text()')  # 获取第一个
last_node = html.xpath('//li[last()]//text()')   # 获取最后一个节点
front_node = html.xpath('//li[position()&lt;3]//text()')    # 获取前两个节点
end_ndoe = html.xpath('//li[last()-2]//text()')   # 获取到数第三个节点

# 轴节点
child_node_z = html.xpath('//li[position()&lt;2]/child::a/@href')  # 获取当前节点所有子元素的a节点的href属性值
attribute_node = html.xpath('//li[2]//attribute::lang')  # 获取当前节点的指定元素的属性值
all_child_node = html.xpath('//ul/li[last()-1]//child::*')  # 获取当前节点的所有的文本节点
all_attrs_node = html.xpath('//li[1]/a/attribute::*')  # 获取当前节点 的所有属性的属性值
all_child_text_node = html.xpath('//li[1]//child::text()')  # 获取当前节点所有文本子节点
all_child_node_node = html.xpath('//li[1]/a/child::node()')  # 获取当前节点所有子元素
ancestor_self = html.xpath('//a[@title=&quot;1&quot;]/../ancestor-or-self::li') # 获取当前元素的所有父辈为li元素的节点（包括当前元素）
print(ancestor_self)
</code></pre>
<h2>Beautifulsoup4使用</h2>
<ul>
<li><code>Beautiful Soup</code>自动将输入文档转换为Unicode编码, 输出文档转换为utf-8编码</li>
<li>使用前安装 <code>pip install beautifulsoup4</code></li>
<li>引入<code>from bs4 import Beautifulsoup4</code></li>
</ul>
<h3>获取内容</h3>
<ul>
<li>标签有两个重要的属性name, attrs</li>
<li>文本内容的获取有三种方法</li>
<li><code>.string</code>方法返回一个迭代器</li>
<li><code>.text</code>方法返回节点文本</li>
<li><code>.get_text()</code>方法返回节点文本</li>
</ul>
<pre><code class="language-python">
## 获取标题对象

print(soup.title)  # &lt;title&gt;xPath方法&lt;/title&gt;
# 获取标题内容
print(soup.title.string)  # 返回迭代器
print(soup.title.text)
print(soup.title.get_text())
print(soup.find('title').get_text())
</code></pre>
<ul>
<li>通过上下级获得对象</li>
</ul>
<pre><code class="language-python"># print(soup.title.parent)   # 返回父节点包括父节点中的内容
print(soup.li.child)  # Node
print(soup.li.children)  # 返回一个迭代器
</code></pre>
<h3>获取第一个li标签</h3>
<pre><code class="language-python">print(soup.li.get_text())  # 匹配到第一个，返回所有节点的文本信息
print(soup.find('li').text)
# 获取ul的子标签们   (空行也看成了一个children)
print(soup.ul.children)
for index, item in enumerate(soup.ul.children):
    print(index, item)
</code></pre>
<h3>获取元素的属性</h3>
<ul>
<li>使用 <code>.属性名</code> 的方法, 但是只能获取到一个</li>
<li>使用<code>元素.attrs['属性名']</code>的方法返回的时一个列表</li>
<li>如果使用两次 <code>soup.元素</code> 第一次获取的是匹配到的第一个元素, 第二次是匹配到的第二个元素</li>
</ul>
<h3>获取多个元素</h3>
<ul>
<li>find方法获取一个元素</li>
<li>find_all获取多个元素, 可以加上<code>limit</code>来达到限制个数的问题, <code>recursive = True</code> 寻找子孙 ; <code>recursive = False</code>只找子</li>
<li>多层级查找 find_all返回的是一个列表 可以遍历该列表再次使用find方法或者find_all方法 进行元素的获取</li>
</ul>
<h3>通过指定的属性, 获取对象</h3>
<ul>
<li>id和class选择器, class比较特殊, 因为是关键字 在使用class时改成<code>class_</code></li>
</ul>
<pre><code class="language-python">print(soup.find(id='a'))
print(soup.find('a', id='a'))
print(soup.find_all('a', id='a'))  # 可以使用下标查询

# class是关键字 要这么写class_

print('class1', soup.find_all('a', class_='a'))
print('class2', soup.find_all('a', attrs={'class': 'item'}))  # 更通用
print('class3', soup.find_all('a', attrs={'class': 'item', 'id': 'a'}))  # 多条件
</code></pre>
<h3>使用函数作为参数, 返回元素</h3>
<pre><code class="language-python">def judgeTilte1(t):
    if t == 'a':
        return True

print(soup.find_all(class_=judgeTilte1))
</code></pre>
<ul>
<li>根据长度来判断</li>
</ul>
<pre><code class="language-python"># 判断长度
import re  # 正则表达式
reg = re.compile(&quot;item&quot;)
def judgeTilte2(t):
    # 返回长度为5，且包含'item'的t参数
    return len(str(t)) == 5 and bool(re.search(reg, t))
print(soup.find_all(class_=judgeTilte2))
</code></pre>
<h3>可以使用css选择器</h3>
<ul>
<li>select方法返回的都是一个列表</li>
<li>可以通过标签名查找, 属性查找, 标签+类名+id, 组合查找</li>
</ul>
<p><a href="https://blog.csdn.net/qq_21933615/article/details/81171951">Python中BeautifulSoup库的用法</a><br>
<a href="https://blog.csdn.net/love666666shen/article/details/77512353">python beautiful soup库的超详细用法</a><br>
<a href="https://blog.csdn.net/IT_arookie/article/details/82824620">python 爬虫 提取文本之BeautifulSoup详细用法</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Sass CSS Preprocessor]]></title>
            <link>https://ryanuo.cc/posts/sass</link>
            <guid>https://ryanuo.cc/posts/sass</guid>
            <pubDate>Tue, 17 Aug 2021 10:34:06 GMT</pubDate>
            <description><![CDATA[sass css preprocessor scss extension npm installation dynamic variables nesting mixin mixing extend inheritance import data type judgment math string color functions list operations interpolation conditional loops custom functions warning errors]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>认识sass</h2>
<ul>
<li>它是一个css的预处理器, 文件的后缀为<code>.scss</code></li>
</ul>
<h2>安装</h2>
<pre><code class="language-shell">npm i -g sass
# 使用
$ sass &lt;input.scss&gt; [output.css]
</code></pre>
<h2>语法规则</h2>
<h3>动态变量</h3>
<pre><code>// 使用变量,可以在变量中使用变量
$primary-color: #1269b5;

p {
  width: 20px;
  color: $primary-color;
}

</code></pre>
<h3>嵌套</h3>
<ul>
<li>使用嵌入式, 使用<code>&amp;</code>符号 连接伪元素</li>
<li>属性的嵌套, 如果有多个相同前缀的属性, 使用<code>前缀名:{后缀属性: 属性值}</code></li>
</ul>
<pre><code>.nav {
  ul {
    li {
      color: $primary-color;
    }

    a {
      text-align: center;

      &amp;:hover {
        font-size: 20px;
      }
    }
  }
}

</code></pre>
<pre><code>/* // 属性的嵌套 前缀有-可以使用  前缀名：{} */
body {
  font: {
    size: 20px;
    weight: normal;
    family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
  }
}

</code></pre>
<h3>mixin alert函数</h3>
<ul>
<li>可以给alert函数加参数也可以不加, 调用时使用include方法调用alert函数</li>
<li>也可以给alert函数参数定义默认值</li>
</ul>
<pre><code>/* // mixin */

@mixin alert($text-color, $background) {
  color: $text-color;
  background-color: $background;

  a {
    color: $primary-color;
    background-color: darken($text-color, 10%);
  }
}

.alert-warning {
  @include alert(#1269b5, #fff);
}

</code></pre>
<h3>继承extend</h3>
<pre><code>/* // 继承 */

a {
  text-decoration: none;
}

.info {
  @extend a;
}

</code></pre>
<h3>@import方法</h3>
<ul>
<li>导入时不需要加入后缀</li>
<li>多行注释在不压缩编译后也能显示</li>
</ul>
<pre><code>@import 'base';
</code></pre>
<h3>data-type</h3>
<ul>
<li>使用type-of方法来判断数据类型</li>
<li>在终端输入<code>sass -i</code></li>
</ul>
<h3>数字函数</h3>
<ul>
<li><code>abs()</code> 绝对值</li>
<li><code>round()</code> 方法 四舍五入</li>
<li><code>ceil()</code> 方法 向上取整 取大</li>
<li><code>floor()</code> 方法 向下取整 取小</li>
<li><code>min() max()</code> 判断大小</li>
</ul>
<h4>字符串处理方法</h4>
<ul>
<li><code>to-upper-case()</code>方法 将字符串全部改为大写</li>
<li><code>to-lower-case()</code>方法 全部改为小写</li>
<li><code>str-length(变量)</code> 输出字符串的长度</li>
<li><code>str-index(变量, 检测的字符串)</code> 输出当前检测字符串在变量字符串中第一次出现的索引号</li>
<li><code>str-insert(变量, 输入插入的值, 索引位置)</code> 在字符串的指定位置插入值</li>
</ul>
<h3>颜色函数</h3>
<ul>
<li><code>adjust-hue</code> 对颜色值进行调整, 对hsl颜色进行调整</li>
<li><code>darken</code>函数可以改变颜色的深度, 更暗 <code>lignten</code>函数更亮, 第一个参数是要转换的值, 第二个参数是改变的程度百分比</li>
<li><code>saturate函</code>数 颜色的饱和度 <code>desaturate</code>函数 减少颜色的饱和度 对hsl颜色进行处理</li>
<li>opacify函数 设置不透明度</li>
<li>transparentize函数 设置透明度</li>
</ul>
<h3>列表 List</h3>
<ul>
<li>length方法, index方法 nth方法 append方法</li>
<li>join方法组合 使用comma 会使用', '分隔</li>
<li>map-get()方法获取 列表对应的属性值 map-keys 返回所有的key值, map-value 返回列表中的值, 判断是否存在某个属性, 使用map-has-key方法 map-merge()方法将新创建的属性加入到列表中使用map-remove方法来移除列表中的指定属性</li>
</ul>
<h3>使用interpolation方法</h3>
<ul>
<li><code>#{}</code> 格式</li>
</ul>
<pre><code>// 使用变量,可以在变量中使用变量
$version: '0.0.1';
/* 当前的版本号#{$version} */
$name: 'info';
$attr: 'border';

.border-#{name} {
  #{$attr}-radius: 10px;
}

</code></pre>
<h3>控制指令</h3>
<ul>
<li>@if</li>
</ul>
<pre><code>$primary-color: #1269b5;
$state: true;

.info-warp {
  @if $state {
    color: $primary-color;
  }
}

</code></pre>
<ul>
<li>@for, through包括最后一个值; to不包括</li>
</ul>
<pre><code>$colums: 4;

@for $i from 1 through $colums {
  .col-#{$i} {
    width: 100% / $colums * $i;
  }
}

</code></pre>
<ul>
<li>@each 遍历数组</li>
</ul>
<pre><code>$icons: error success info;

@each $i in $icons {
  .col-#{$i} {
    background: url('image/#{$i}.png');
  }
}

</code></pre>
<ul>
<li>@while 计算字符要空格</li>
</ul>
<pre><code>$i: 6;

@while $i&gt;0 {
  .item-#{$i} {
    width: 5px * $i;
    color: $primary-color;
  }

  $i: $i - 2;
}

</code></pre>
<h2>自定义函数</h2>
<ul>
<li>格式<code>@function(传入的参数){@return 属性值}</code>如果是对象可以使用map方法获取第一个参数是构造的对象属性, 第二个参数是要调用的对象参数</li>
</ul>
<pre><code>$color: (
  light: #fff,
  dark: #000,
);

@function color($key) {
  @return map-get($color, $key);
}

.oi {
  color: color(dark);
}

</code></pre>
<h3>警告和错误</h3>
<ul>
<li>@warn和@error 打印输入 在终端输出</li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Sass-CSS预处理器]]></title>
            <link>https://ryanuo.cc/zh/posts/sass</link>
            <guid>https://ryanuo.cc/zh/posts/sass</guid>
            <pubDate>Tue, 17 Aug 2021 10:34:06 GMT</pubDate>
            <description><![CDATA[Sass-CSS预处理器：.scss扩展名，npm安装，动态变量、嵌套、mixin混入、extend继承、@import、数据类型判断、数学字符串颜色函数、列表操作、插值、条件循环、自定义函数、警告错误。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>认识sass</h2>
<ul>
<li>它是一个css的预处理器, 文件的后缀为<code>.scss</code></li>
</ul>
<h2>安装</h2>
<pre><code class="language-shell">npm i -g sass
# 使用
$ sass &lt;input.scss&gt; [output.css]
</code></pre>
<h2>语法规则</h2>
<h3>动态变量</h3>
<pre><code>// 使用变量,可以在变量中使用变量
$primary-color: #1269b5;

p {
  width: 20px;
  color: $primary-color;
}
</code></pre>
<h3>嵌套</h3>
<ul>
<li>使用嵌入式, 使用<code>&amp;</code>符号 连接伪元素</li>
<li>属性的嵌套, 如果有多个相同前缀的属性, 使用<code>前缀名:{后缀属性: 属性值}</code></li>
</ul>
<pre><code>.nav {
  ul {
    li {
      color: $primary-color;
    }

    a {
      text-align: center;

      &amp;:hover {
        font-size: 20px;
      }
    }
  }
}

</code></pre>
<pre><code>/* // 属性的嵌套 前缀有-可以使用  前缀名：{} */
body {
  font: {
    size: 20px;
    weight: normal;
    family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
  }
}

</code></pre>
<h3>mixin alert函数</h3>
<ul>
<li>可以给alert函数加参数也可以不加, 调用时使用include方法调用alert函数</li>
<li>也可以给alert函数参数定义默认值</li>
</ul>
<pre><code>/* // mixin */

@mixin alert($text-color, $background) {
  color: $text-color;
  background-color: $background;

  a {
    color: $primary-color;
    background-color: darken($text-color, 10%);
  }
}

.alert-warning {
  @include alert(#1269b5, #fff);
}

</code></pre>
<h3>继承extend</h3>
<pre><code>/* // 继承 */

a {
  text-decoration: none;
}

.info {
  @extend a;
}

</code></pre>
<h3>@import方法</h3>
<ul>
<li>导入时不需要加入后缀</li>
<li>多行注释在不压缩编译后也能显示</li>
</ul>
<pre><code>@import 'base';
</code></pre>
<h3>data-type</h3>
<ul>
<li>使用type-of方法来判断数据类型</li>
<li>在终端输入<code>sass -i</code></li>
</ul>
<h3>数字函数</h3>
<ul>
<li><code>abs()</code> 绝对值</li>
<li><code>round()</code> 方法 四舍五入</li>
<li><code>ceil()</code> 方法 向上取整 取大</li>
<li><code>floor()</code> 方法 向下取整 取小</li>
<li><code>min() max()</code> 判断大小</li>
</ul>
<h4>字符串处理方法</h4>
<ul>
<li><code>to-upper-case()</code>方法 将字符串全部改为大写</li>
<li><code>to-lower-case()</code>方法 全部改为小写</li>
<li><code>str-length(变量)</code> 输出字符串的长度</li>
<li><code>str-index(变量, 检测的字符串)</code> 输出当前检测字符串在变量字符串中第一次出现的索引号</li>
<li><code>str-insert(变量, 输入插入的值, 索引位置)</code> 在字符串的指定位置插入值</li>
</ul>
<h3>颜色函数</h3>
<ul>
<li><code>adjust-hue</code> 对颜色值进行调整, 对hsl颜色进行调整</li>
<li><code>darken</code>函数可以改变颜色的深度, 更暗 <code>lignten</code>函数更亮, 第一个参数是要转换的值, 第二个参数是改变的程度百分比</li>
<li><code>saturate函</code>数 颜色的饱和度 <code>desaturate</code>函数 减少颜色的饱和度 对hsl颜色进行处理</li>
<li>opacify函数 设置不透明度</li>
<li>transparentize函数 设置透明度</li>
</ul>
<h3>列表 List</h3>
<ul>
<li>length方法, index方法 nth方法 append方法</li>
<li>join方法组合 使用comma 会使用', '分隔</li>
<li>map-get()方法获取 列表对应的属性值 map-keys 返回所有的key值, map-value 返回列表中的值, 判断是否存在某个属性, 使用map-has-key方法 map-merge()方法将新创建的属性加入到列表中使用map-remove方法来移除列表中的指定属性</li>
</ul>
<h3>使用interpolation方法</h3>
<ul>
<li><code>#{}</code> 格式</li>
</ul>
<pre><code>// 使用变量,可以在变量中使用变量
$version: '0.0.1';
/* 当前的版本号#{$version} */
$name: 'info';
$attr: 'border';

.border-#{name} {
  #{$attr}-radius: 10px;
}

</code></pre>
<h3>控制指令</h3>
<ul>
<li>@if</li>
</ul>
<pre><code>$primary-color: #1269b5;
$state: true;

.info-warp {
  @if $state {
    color: $primary-color;
  }
}

</code></pre>
<ul>
<li>@for, through包括最后一个值; to不包括</li>
</ul>
<pre><code>$colums: 4;

@for $i from 1 through $colums {
  .col-#{$i} {
    width: 100% / $colums * $i;
  }
}

</code></pre>
<ul>
<li>@each 遍历数组</li>
</ul>
<pre><code>$icons: error success info;

@each $i in $icons {
  .col-#{$i} {
    background: url('image/#{$i}.png');
  }
}

</code></pre>
<ul>
<li>@while 计算字符要空格</li>
</ul>
<pre><code>$i: 6;

@while $i&gt;0 {
  .item-#{$i} {
    width: 5px * $i;
    color: $primary-color;
  }

  $i: $i - 2;
}

</code></pre>
<h2>自定义函数</h2>
<ul>
<li>格式<code>@function(传入的参数){@return 属性值}</code>如果是对象可以使用map方法获取第一个参数是构造的对象属性, 第二个参数是要调用的对象参数</li>
</ul>
<pre><code>$color: (
  light: #fff,
  dark: #000,
);

@function color($key) {
  @return map-get($color, $key);
}

.oi {
  color: color(dark);
}

</code></pre>
<h3>警告和错误</h3>
<ul>
<li>@warn和@error 打印输入 在终端输出</li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Regular Expressions Js]]></title>
            <link>https://ryanuo.cc/posts/js-re</link>
            <guid>https://ryanuo.cc/posts/js-re</guid>
            <pubDate>Thu, 29 Jul 2021 08:50:00 GMT</pubDate>
            <description><![CDATA[java script regular expressions number extraction variable application substitution rules boundary delimiting character classes global search]]></description>
            <content:encoded><![CDATA[<h2>正则字符串提取数字</h2>
<ol>
<li>使用\d将数字匹配出来 /g全局匹配</li>
</ol>
<pre><code>const hd = 'daw21321dasda23123123'
// 不使用正则表达式写一个方法
// console.log(Number.isNaN(parseInt('1')));
// ...将字符串转换成数组形式 Array.from()方法也可以 使用isNaN判断是否为非数字
// let news = [...hd].filter(v=&gt;!Number.isNaN(parseInt(v)));
// 使用join方法连接
// 使用正则表达式
const news = hd.match(/\d/g).join('')
console.log(news)
</code></pre>
<h2>正则字变量</h2>
<ol>
<li>使用eval()方法</li>
<li>使用test()方法返回的是布尔值</li>
</ol>
<pre><code>const hd = 'daw21321dasda23123123'
const a = '1'
const news = eval(`/${a}/`).test(hd) // true
console.log(news)
</code></pre>
<h2>使用对象创建</h2>
<ol>
<li>替换字符串中匹配到的字符</li>
</ol>
<pre><code>&lt;div class=&quot;content&quot;&gt;ryanuo.cc&lt;/div&gt;
&lt;script&gt;
  let div = document.querySelector('.content')
  let a = prompt('输入字符')
  let regs = new RegExp(a, 'g')
  div.innerHTML = div.innerHTML.replace(regs, (search) =&gt; {
    return `&lt;span style=&quot;color:red&quot;&gt;${search}&lt;/span&gt;`
  })
&lt;/script&gt;

</code></pre>
<h2>选择符 |</h2>
<ol>
<li>匹配左右两侧的条件</li>
<li>原子组() 原子表[]</li>
</ol>
<pre><code>const tel = '023-2312322'
console.log(/(023|020)-\d{7,8}/.test(tel)) // true
// 重复的条件使用原子组的方法{7，8}表示 7-8位的数字 \- 表示转义
const reg = /(12|34)/
const hd = '31w231'
console.log(hd.match(reg)) // null
</code></pre>
<h2>转义</h2>
<ol>
<li>在字面量里面 使用<code>\d \.</code> 将字符可实现转义</li>
<li>在构造函数中<code>\d \.</code> 表示的是该字符 所以 必须再加一个转义符 <code>\\d \\.</code></li>
</ol>
<pre><code>const price = '12@21'
// . 除换行外的任何字符
console.log(/\d+.\d/.test(price)) // true
let reg = new RegExp('\\d+\\.\\d+')
// \\ 双转义
console.log(reg.test(price)) // false

// 网址
const url = 'https://u.ryanuo.cc'
let reg = new RegExp(/https?:\/\/\w+\.\w+\.\w+/)
console.log(reg.test(url)) // true
console.log(/https?:\/\/\w+\.\w+/.test(url)) // true
</code></pre>
<h2>字符边界符</h2>
<ol>
<li>边界符表示开始和结尾必须满足条件 <code>/^[a-z]$/</code></li>
<li><code>{3,6}</code> 加入边界字符 才能控制其数量范围</li>
</ol>
<pre><code>&lt;input type=&quot;text&quot; value=&quot;&quot; name=&quot;user&quot; /&gt;
&lt;script&gt;
  document
    .querySelector(&quot;[name='user']&quot;)
    .addEventListener('keyup', function () {
      let flag = this.value.match(/^[a-z]{3,6}$/)
      console.log(flag)
    })
&lt;/script&gt;

</code></pre>
<h2>数值与空白元字符</h2>
<ol>
<li><code>\d</code> 匹配数字 <code>\D</code> 匹配非数字</li>
<li>在原子表中首页<code>^</code>表示非的意思<code>/[^]/</code></li>
<li><code>\s</code> 表示匹配空白和换行 <code>\S</code> 非空白</li>
</ol>
<pre><code>const hd = '张三:021-9999299,李四:022-2122122'
// 匹配所有的数字号码
console.log(hd.match(/\d{3}-\d{7,8}/g)) // [&quot;021-9999299&quot;, &quot;022-2122122&quot;]
// 匹配姓名 + 表示匹配相邻的符合条件的
console.log(hd.match(/[^:\d,-\s]+/g)) // [&quot;张三&quot;, &quot;李四&quot;]
</code></pre>
<h2>w和W元字符</h2>
<ol>
<li><code>\w</code> 匹配字母和数字 下划线</li>
<li><code>\W</code>与之相反</li>
</ol>
<h2>点字符的使用</h2>
<ol>
<li>匹配所有字符默认模式下，它匹配除了换行符以外的任意字符。</li>
</ol>
<pre><code>const hd = `wad\nawd\n`
console.log(hd.match(/.+/g)) // [wad,awd]
</code></pre>
<h2>匹配所有字符</h2>
<ol>
<li>在原子组中<code>[\s\S]</code> 或者<code>[\d\D]</code></li>
<li>匹配标签使用 <code>/&lt;span&gt;[\s\S]+&lt;/span&gt;/</code></li>
</ol>
<h2>i和g 模式修正</h2>
<ol>
<li><code>g</code>全局匹配 <code>i</code>不区分大小写</li>
</ol>
<pre><code>const hd = `
#1 js,200元 #
#2 php,300元 #
#9 ryanuo.cc # 你好
#3 node.js,180元 #
`
const lessons = hd.match(/^\s*#\d+\s+(?:\S.*|[\t\v\f \xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF])\s+#$/gm).map((v) =&gt; {
  v = v.replace(/\s*#\d+\s*/, '').replace(/\s+#/, '')
  const [name, price] = v.split(',')
  return { name, price }
})
console.log(lessons)
/*
0: {name: &quot;js&quot;, price: &quot;200元&quot;}
1: {name: &quot;php&quot;, price: &quot;300元&quot;}
2: {name: &quot;node.js&quot;, price: &quot;180元&quot;}
length: 3 */
</code></pre>
<h2>汉字与字符属性</h2>
<ol>
<li><code>\p</code>表示匹配字符</li>
<li><code>\P</code> 相反</li>
</ol>
<h2>lastIndex</h2>
<ol>
<li>lastIndex 属性用于规定下次匹配的起始位置</li>
<li><code>exec()</code> 方法用于检索字符串中的正则表达式的匹配。</li>
</ol>
<pre><code>const hd = 'dawdaw31'
const reg = /\w/g
console.log(reg.exec(hd))
console.log(reg.lastIndex) // lastIndex 属性用于规定下次匹配的起始位置
// 将匹配到的值赋值给res 有值打印数据 否则返回null 不满足循环条件 循环
while (res = reg.exec(hd))
  console.log(res)

</code></pre>
<h2>y模式</h2>
<ol>
<li>后一次匹配都是从上一次匹配成功的下一个位置开始</li>
</ol>
<pre><code>const hd = `你好世界大王大大:11111111,1111231,213123212,
达瓦达,网站:u.ryanuo.cc`
const reg = /(\d+),?/y
reg.lastIndex = 9
const qq = []
// console.log(reg.exec(hd));
while (res = reg.exec(hd)) qq.push(res[1])
console.log(qq)
</code></pre>
<h2>原子表</h2>
<ol>
<li><code>[]</code> 匹配中括号内的所有条件</li>
<li>原子组和原子表搭配使用</li>
</ol>
<pre><code>const times = '2020-02/21'
console.log(times.match(/^\d{4}[-/]\d{2}[-/]\d{2}$/)) // 2020/02-21
console.log(times.match(/^\d{4}([-/])\d{2}\1\d{2}$/)) // null
console.log(times.match(/^\d{4}([-/])\d{2}\1\d{2}$/)) // 2020/02/21
</code></pre>
<h2>区间匹配</h2>
<ol>
<li>数字区间<code>[0-9]+</code> 加号表示贪婪模式</li>
<li>字母区间<code>[a-z]</code></li>
</ol>
<h2>排除匹配</h2>
<ol>
<li><code>[^]</code> 表示排除匹配</li>
<li><code>\p</code>表示匹配满足<code>{}</code>内的字符</li>
</ol>
<pre><code>// let hd = 'u.ryanuo.cc'
// console.log(hd.match(/[^u]/gi)); //[&quot;.&quot;, &quot;m&quot;, &quot;r&quot;, &quot;9&quot;, &quot;0&quot;, &quot;.&quot;, &quot;t&quot;, &quot;o&quot;, &quot;p&quot;]
const hd = `张三:231-23123123,李四#:123-312313;`
console.log(hd.match(/[^:\w-,#;]+/g)) // 贪婪模式下的排除匹配
console.log(hd.match(/\p{sc=Han}+/gu)) // [&quot;张三&quot;, &quot;李四&quot;]
</code></pre>
<h2>原子表字符不解析</h2>
<ol>
<li><code>[.+]</code> <code>[()]</code> 仅表示原来字符的意思</li>
</ol>
<h2>原子表匹配</h2>
<ol>
<li><code>s</code>忽略换行符 <code>*</code>表示0个或者多个</li>
<li><code>[\d\D] [\s\S]</code> 匹配所有字符</li>
</ol>
<pre><code>&lt;body&gt;
  &lt;h1&gt;ddawd dawd&lt;/h1&gt;
  &lt;h2&gt;dwad&lt;/h2&gt;
  &lt;span&gt;dasdwa adaw &lt;/span&gt;
&lt;/body&gt;
&lt;script&gt;
  let body = document.body
  // let reg = /&lt;(\w+)&gt;[^]+&lt;\/\1&gt;/gi // 注意贪婪模式
  let reg = /&lt;(h[1-6])&gt;[^]*&lt;\/\1&gt;/gi // 注意贪婪模式 *表示0个或者多个
  body.innerHTML = body.innerHTML.replace(reg, '')
&lt;/script&gt;

</code></pre>
<h2>原子组</h2>
<ol>
<li><code>()</code> 表示一个整体</li>
<li>可进行别名的编号</li>
<li>使用原子组实现标签的替换</li>
<li>在原子组回调函数中,各个参数表示的是 第一个是满足条件的所有内容 其他参数 依次为定义的满足原子组条件的内容</li>
</ol>
<pre><code>&lt;body&gt;
  &lt;h1&gt;ddawd dawd&lt;/h1&gt;
  &lt;h5&gt;&lt;/h5&gt;
  &lt;h2&gt;dwad&lt;/h2&gt;
  &lt;span&gt;dasdwa adaw &lt;/span&gt;
&lt;/body&gt;
&lt;script&gt;
  // 原子组替换
  let body = document.body
  // body.innerHTML = body.innerHTML.replace(/&lt;(h[1-6]+)&gt;([^]*)&lt;\/\1&gt;/g,`&lt;p&gt;$2&lt;/p&gt;`) // 直接表示
  body.innerHTML = body.innerHTML.replace(
    /&lt;(h[1-6]+)&gt;([^]*)&lt;\/\1&gt;/g,
    (f0, f1, f2) =&gt; {
      // console.log(f0);  // 输入满足条件的所有
      // console.log(f1); // 输出第一个原子组匹配的内容
      // console.log(f2);// 输出第二个原子组匹配的内容
      return `&lt;p&gt;${f2}&lt;/p&gt;`
    },
  ) // 函数表示
&lt;/script&gt;

</code></pre>
<h2>不记录分组</h2>
<ol>
<li>使用不记录分组,就是使用原子组后 无法再去使用函数参数去调用</li>
</ol>
<pre><code>const url = `https://u.ryanuo.cc
    http://github.com
    httpS://ryanuo.cc`
// 使用不记录分组,就是使用原子组后 无法再去使用函数参数去调用
const reg = /https?:\/\/((?:\w+\.)?\w+\.(?:com|top))/gi
console.log(reg.test(url)) // true // 有满足条件的内容
console.log(url.match(reg)) // [&quot;https://u.ryanuo.cc&quot;, &quot;http://github.com&quot;, &quot;httpS://ryanuo.cc&quot;]
//    console.log(reg.exec(url)); // 返回满足条件的第一个内容
//    console.log(reg.lastIndex); // 记录下次开始时的索引位置
const urlA = []
while (res = reg.exec(url)) urlA.push(res[1])
console.log(urlA)
</code></pre>
<h2>重复匹配</h2>
<ol>
<li><code>+</code> 一个或者多个 <code>{1,}</code> 类似与加号</li>
<li><code>*</code> 0个或者多个 <code>{0,}</code> 类似与*号</li>
<li><code>?</code> 0个或者1个</li>
</ol>
<h2>批量使用正则完成密码的验证</h2>
<pre><code>// 对表单输入的值进行验证
document.querySelector('[name=pwd')
  .addEventListener('keyup', (e) =&gt; {
    // console.log();
    const value = e.target.value
    const reg = [
      /^[a-z0-9]{5,7}$/i,
      /[A-Z]/,
      /\d/
    ]
    const state = reg.every(v =&gt; v.test(value))
    console.log(state ? '正确' : '错误')
  })
</code></pre>
<h2>禁止贪婪</h2>
<ol>
<li><code>{2,}?</code> 限制贪婪</li>
<li><code>*?</code> 禁止贪婪</li>
</ol>
<pre><code>&lt;body&gt;
  &lt;main&gt;
    &lt;span&gt;u.ryanuo.cc1&lt;/span&gt;
    &lt;span&gt;u.ryanuo.cc2&lt;/span&gt;
    &lt;span&gt;u.ryanuo.cc3&lt;/span&gt;
  &lt;/main&gt;
&lt;/body&gt;
&lt;script&gt;
  let main = document.querySelector('main')
  let reg = /&lt;span&gt;([^]+?)&lt;\/span&gt;/g // ?加上问号 并且写到原子组中为了将贪婪的范围缩小到每一行上
  main.innerHTML = main.innerHTML.replace(reg, (p0, p1) =&gt; {
    console.log(p0)
    return `&lt;h2 style=&quot;color:red&quot;&gt;${p1}&lt;/h2&gt;`
  })
&lt;/script&gt;

</code></pre>
<h2>matchAll 全局匹配</h2>
<pre><code>&lt;body&gt;
  &lt;main&gt;
    &lt;span&gt;u.ryanuo.cc1&lt;/span&gt;
    &lt;span&gt;u.ryanuo.cc2&lt;/span&gt;
    &lt;span&gt;u.ryanuo.cc3&lt;/span&gt;
  &lt;/main&gt;
&lt;/body&gt;
&lt;script&gt;
  // 不使用matchAll方法
  let main = document.querySelector('main')
  let reg = /&lt;span&gt;([^]+?)&lt;\/span&gt;/g // ?加上问号 并且写到原子组中为了将贪婪的范围缩小到每一行上
  let content = []
  // main.innerHTML = main.innerHTML.replace(reg,(p0,p1)=&gt;{
  //     content.push(p1)
  //     return `&lt;h2 style=&quot;color:red&quot;&gt;${p1}&lt;/h2&gt;`
  // })
  // console.table(content)

  // 使用matchAll方法
  let hd = main.innerHTML.matchAll(reg)
  // console.log(main.innerHTML.matchAll()); // 遍历迭代
  for (const i of hd) {
    content.push(i[1])
    // console.log(i);
  }
  console.table(content)
&lt;/script&gt;

</code></pre>
<h2>exec 全局匹配</h2>
<ol>
<li>匹配时 <code>g</code>不能缺少</li>
<li><code>search</code> 返回匹配的索引值</li>
</ol>
<pre><code>// exec方法 匹配全局
let main = document.querySelector('main')
let reg = /&lt;span&gt;([\s\S]+?)&lt;\/span&gt;/g // ?加上问号 并且写到原子组中为了将贪婪的范围缩小到每一行上
function fn(string, reg) {
  const content = []
  while (res = reg.exec(string))
    content.push(res)
  return content
}
console.log(fn(main.innerHTML, reg))
// exec方法 匹配全局
let main = document.querySelector('main')
let reg = /&lt;span&gt;([\s\S]+?)&lt;\/span&gt;/g // ?加上问号 并且写到原子组中为了将贪婪的范围缩小到每一行上
const content = []
function fn(string, reg) {
  while (res = reg.exec(string)) content.push(res)
}
fn(main.innerHTML, reg)
console.log(content)
</code></pre>
<h2>$符</h2>
<ol>
<li><code>$`</code> 匹配开头的第一个字符</li>
<li><code>$'</code> 匹配结尾字符</li>
<li><code>$&amp;</code> 匹配自身</li>
</ol>
<pre><code>// let tel = '2020/12/23'
// console.log(tel.replace(/\//g,'-')); // 2020-12-23
// let tel = '(021)9999999 (023)4444444'
// console.log(tel.replace(/[(]+(\d{3})[)](\d{7})/g,'$1-$2')); //021-9999999 023-4444444
const tel = '%Harry='
console.log(tel.replace(/\w+/g, '$`')) // $` 匹配开头的第一个字符 %%=
console.log(tel.replace(/\w+/g, '$\'')) // $' 匹配结尾字符 %==
console.log(tel.replace(/\w+/g, '$&amp;')) // $&amp; 匹配自身
console.log(tel.replace(/\w+/g, '$`$`$&amp;$\'$\'')) // $&amp; 匹配自身
</code></pre>
<h2>原子组的别名</h2>
<ol>
<li>格式 <code>?&lt;别名&gt;</code></li>
<li>调用 <code>&lt;别名&gt;</code></li>
</ol>
<pre><code>&lt;body&gt;
  &lt;main&gt;
    &lt;a href=&quot;http://u.ryanuo.cc&quot;&gt;博客&lt;/a&gt;
    &lt;a href=&quot;https://baidu.com&quot;&gt;百度&lt;/a&gt;
    &lt;a href=&quot;https://github.com&quot;&gt;Github&lt;/a&gt;
  &lt;/main&gt;
&lt;/body&gt;
&lt;script&gt;
  let main = document.querySelector('main')
  let reg = /&lt;a.*?href=(['&quot;])(?&lt;link&gt;.*?)\1&gt;(?&lt;title&gt;.*?)&lt;\/a&gt;/gi
  // 进行迭代遍历
  for (const i of main.innerHTML.matchAll(reg)) {
    // console.log(i);
    console.table(i.groups)
  }
&lt;/script&gt;

</code></pre>
<h2>断言匹配 ?=</h2>
<ol>
<li><code>(?=)</code> 表示在之前条件的后面满足该断言匹配的内容</li>
<li><code>(?&lt;=)</code> 表示前面是条件的匹配内容</li>
<li><code>(?!)</code> 表示后面不是某个条件的内容</li>
<li><code>(?&lt;!)</code> 表示前面不是某个条件的内容</li>
</ol>
<pre><code>&lt;body&gt;
  &lt;main&gt;
    &lt;a href=&quot;http://u.ryanuo.cc&quot;&gt;博客&lt;/a&gt;
    &lt;a href=&quot;https://baidu.com&quot;&gt;百度&lt;/a&gt;
    &lt;a href=&quot;https://github.com&quot;&gt;Github&lt;/a&gt;
  &lt;/main&gt;
&lt;/body&gt;
&lt;script&gt;
  let main = document.querySelector('main')
  let reg = /(?&lt;=href=(['&quot;]))(.*?)(?=\1&gt;)/gi
  main.innerHTML = main.innerHTML.replace(reg, 'https://ryanuo.cc')
&lt;/script&gt;

</code></pre>
<h3>手机号码断言隐藏</h3>
<pre><code>const users = `
李四：12323212232
张三：12322131122
`
const reg = /(?&lt;=\d{7})\d{4}/g
console.log(users.replace(reg, '*'.repeat(4)))
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[正则表达式（js）]]></title>
            <link>https://ryanuo.cc/zh/posts/js-re</link>
            <guid>https://ryanuo.cc/zh/posts/js-re</guid>
            <pubDate>Thu, 29 Jul 2021 08:50:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>正则字符串提取数字</h2>
<ol>
<li>使用\d将数字匹配出来 /g全局匹配</li>
</ol>
<pre><code>const hd = 'daw21321dasda23123123'
// 不使用正则表达式写一个方法
// console.log(Number.isNaN(parseInt('1')));
// ...将字符串转换成数组形式 Array.from()方法也可以 使用isNaN判断是否为非数字
// let news = [...hd].filter(v=&gt;!Number.isNaN(parseInt(v)));
// 使用join方法连接
// 使用正则表达式
const news = hd.match(/\d/g).join('')
console.log(news)
</code></pre>
<h2>正则字变量</h2>
<ol>
<li>使用eval()方法</li>
<li>使用test()方法返回的是布尔值</li>
</ol>
<pre><code>const hd = 'daw21321dasda23123123'
const a = '1'
const news = eval(`/${a}/`).test(hd) // true
console.log(news)
</code></pre>
<h2>使用对象创建</h2>
<ol>
<li>替换字符串中匹配到的字符</li>
</ol>
<pre><code>&lt;div class=&quot;content&quot;&gt;ryanuo.cc&lt;/div&gt;
&lt;script&gt;
  let div = document.querySelector('.content')
  let a = prompt('输入字符')
  let regs = new RegExp(a, 'g')
  div.innerHTML = div.innerHTML.replace(regs, (search) =&gt; {
    return `&lt;span style=&quot;color:red&quot;&gt;${search}&lt;/span&gt;`
  })
&lt;/script&gt;

</code></pre>
<h2>选择符 |</h2>
<ol>
<li>匹配左右两侧的条件</li>
<li>原子组() 原子表[]</li>
</ol>
<pre><code>const tel = '023-2312322'
console.log(/(023|020)-\d{7,8}/.test(tel)) // true
// 重复的条件使用原子组的方法{7，8}表示 7-8位的数字 \- 表示转义
const reg = /(12|34)/
const hd = '31w231'
console.log(hd.match(reg)) // null
</code></pre>
<h2>转义</h2>
<ol>
<li>在字面量里面 使用<code>\d \.</code> 将字符可实现转义</li>
<li>在构造函数中<code>\d \.</code> 表示的是该字符 所以 必须再加一个转义符 <code>\\d \\.</code></li>
</ol>
<pre><code>const price = '12@21'
// . 除换行外的任何字符
console.log(/\d+.\d/.test(price)) // true
let reg = new RegExp('\\d+\\.\\d+')
// \\ 双转义
console.log(reg.test(price)) // false

// 网址
const url = 'https://u.ryanuo.cc'
let reg = new RegExp(/https?:\/\/\w+\.\w+\.\w+/)
console.log(reg.test(url)) // true
console.log(/https?:\/\/\w+\.\w+/.test(url)) // true
</code></pre>
<h2>字符边界符</h2>
<ol>
<li>边界符表示开始和结尾必须满足条件 <code>/^[a-z]$/</code></li>
<li><code>{3,6}</code> 加入边界字符 才能控制其数量范围</li>
</ol>
<pre><code>&lt;input type=&quot;text&quot; value=&quot;&quot; name=&quot;user&quot; /&gt;
&lt;script&gt;
  document
    .querySelector(&quot;[name='user']&quot;)
    .addEventListener('keyup', function () {
      let flag = this.value.match(/^[a-z]{3,6}$/)
      console.log(flag)
    })
&lt;/script&gt;

</code></pre>
<h2>数值与空白元字符</h2>
<ol>
<li><code>\d</code> 匹配数字 <code>\D</code> 匹配非数字</li>
<li>在原子表中首页<code>^</code>表示非的意思<code>/[^]/</code></li>
<li><code>\s</code> 表示匹配空白和换行 <code>\S</code> 非空白</li>
</ol>
<pre><code>const hd = '张三:021-9999299,李四:022-2122122'
// 匹配所有的数字号码
console.log(hd.match(/\d{3}-\d{7,8}/g)) // [&quot;021-9999299&quot;, &quot;022-2122122&quot;]
// 匹配姓名 + 表示匹配相邻的符合条件的
console.log(hd.match(/[^:\d,-\s]+/g)) // [&quot;张三&quot;, &quot;李四&quot;]
</code></pre>
<h2>w和W元字符</h2>
<ol>
<li><code>\w</code> 匹配字母和数字 下划线</li>
<li><code>\W</code>与之相反</li>
</ol>
<h2>点字符的使用</h2>
<ol>
<li>匹配所有字符默认模式下，它匹配除了换行符以外的任意字符。</li>
</ol>
<pre><code>const hd = `wad\nawd\n`
console.log(hd.match(/.+/g)) // [wad,awd]
</code></pre>
<h2>匹配所有字符</h2>
<ol>
<li>在原子组中<code>[\s\S]</code> 或者<code>[\d\D]</code></li>
<li>匹配标签使用 <code>/&lt;span&gt;[\s\S]+&lt;/span&gt;/</code></li>
</ol>
<h2>i和g 模式修正</h2>
<ol>
<li><code>g</code>全局匹配 <code>i</code>不区分大小写</li>
</ol>
<pre><code>const hd = `
#1 js,200元 #
#2 php,300元 #
#9 ryanuo.cc # 你好
#3 node.js,180元 #
`
const lessons = hd.match(/^\s*#\d+\s+(?:\S.*|[\t\v\f \xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFEFF])\s+#$/gm).map((v) =&gt; {
  v = v.replace(/\s*#\d+\s*/, '').replace(/\s+#/, '')
  const [name, price] = v.split(',')
  return { name, price }
})
console.log(lessons)
/*
0: {name: &quot;js&quot;, price: &quot;200元&quot;}
1: {name: &quot;php&quot;, price: &quot;300元&quot;}
2: {name: &quot;node.js&quot;, price: &quot;180元&quot;}
length: 3 */
</code></pre>
<h2>汉字与字符属性</h2>
<ol>
<li><code>\p</code>表示匹配字符</li>
<li><code>\P</code> 相反</li>
</ol>
<h2>lastIndex</h2>
<ol>
<li>lastIndex 属性用于规定下次匹配的起始位置</li>
<li><code>exec()</code> 方法用于检索字符串中的正则表达式的匹配。</li>
</ol>
<pre><code>const hd = 'dawdaw31'
const reg = /\w/g
console.log(reg.exec(hd))
console.log(reg.lastIndex) // lastIndex 属性用于规定下次匹配的起始位置
// 将匹配到的值赋值给res 有值打印数据 否则返回null 不满足循环条件 循环
while (res = reg.exec(hd))
  console.log(res)

</code></pre>
<h2>y模式</h2>
<ol>
<li>后一次匹配都是从上一次匹配成功的下一个位置开始</li>
</ol>
<pre><code>const hd = `你好世界大王大大:11111111,1111231,213123212,
达瓦达,网站:u.ryanuo.cc`
const reg = /(\d+),?/y
reg.lastIndex = 9
const qq = []
// console.log(reg.exec(hd));
while (res = reg.exec(hd)) qq.push(res[1])
console.log(qq)
</code></pre>
<h2>原子表</h2>
<ol>
<li><code>[]</code> 匹配中括号内的所有条件</li>
<li>原子组和原子表搭配使用</li>
</ol>
<pre><code>const times = '2020-02/21'
console.log(times.match(/^\d{4}[-/]\d{2}[-/]\d{2}$/)) // 2020/02-21
console.log(times.match(/^\d{4}([-/])\d{2}\1\d{2}$/)) // null
console.log(times.match(/^\d{4}([-/])\d{2}\1\d{2}$/)) // 2020/02/21
</code></pre>
<h2>区间匹配</h2>
<ol>
<li>数字区间<code>[0-9]+</code> 加号表示贪婪模式</li>
<li>字母区间<code>[a-z]</code></li>
</ol>
<h2>排除匹配</h2>
<ol>
<li><code>[^]</code> 表示排除匹配</li>
<li><code>\p</code>表示匹配满足<code>{}</code>内的字符</li>
</ol>
<pre><code>// let hd = 'u.ryanuo.cc'
// console.log(hd.match(/[^u]/gi)); //[&quot;.&quot;, &quot;m&quot;, &quot;r&quot;, &quot;9&quot;, &quot;0&quot;, &quot;.&quot;, &quot;t&quot;, &quot;o&quot;, &quot;p&quot;]
const hd = `张三:231-23123123,李四#:123-312313;`
console.log(hd.match(/[^:\w-,#;]+/g)) // 贪婪模式下的排除匹配
console.log(hd.match(/\p{sc=Han}+/gu)) // [&quot;张三&quot;, &quot;李四&quot;]
</code></pre>
<h2>原子表字符不解析</h2>
<ol>
<li><code>[.+]</code> <code>[()]</code> 仅表示原来字符的意思</li>
</ol>
<h2>原子表匹配</h2>
<ol>
<li><code>s</code>忽略换行符 <code>*</code>表示0个或者多个</li>
<li><code>[\d\D] [\s\S]</code> 匹配所有字符</li>
</ol>
<pre><code>&lt;body&gt;
  &lt;h1&gt;ddawd dawd&lt;/h1&gt;
  &lt;h2&gt;dwad&lt;/h2&gt;
  &lt;span&gt;dasdwa adaw &lt;/span&gt;
&lt;/body&gt;
&lt;script&gt;
  let body = document.body
  // let reg = /&lt;(\w+)&gt;[^]+&lt;\/\1&gt;/gi // 注意贪婪模式
  let reg = /&lt;(h[1-6])&gt;[^]*&lt;\/\1&gt;/gi // 注意贪婪模式 *表示0个或者多个
  body.innerHTML = body.innerHTML.replace(reg, '')
&lt;/script&gt;

</code></pre>
<h2>原子组</h2>
<ol>
<li><code>()</code> 表示一个整体</li>
<li>可进行别名的编号</li>
<li>使用原子组实现标签的替换</li>
<li>在原子组回调函数中,各个参数表示的是 第一个是满足条件的所有内容 其他参数 依次为定义的满足原子组条件的内容</li>
</ol>
<pre><code>&lt;body&gt;
  &lt;h1&gt;ddawd dawd&lt;/h1&gt;
  &lt;h5&gt;&lt;/h5&gt;
  &lt;h2&gt;dwad&lt;/h2&gt;
  &lt;span&gt;dasdwa adaw &lt;/span&gt;
&lt;/body&gt;
&lt;script&gt;
  // 原子组替换
  let body = document.body
  // body.innerHTML = body.innerHTML.replace(/&lt;(h[1-6]+)&gt;([^]*)&lt;\/\1&gt;/g,`&lt;p&gt;$2&lt;/p&gt;`) // 直接表示
  body.innerHTML = body.innerHTML.replace(
    /&lt;(h[1-6]+)&gt;([^]*)&lt;\/\1&gt;/g,
    (f0, f1, f2) =&gt; {
      // console.log(f0);  // 输入满足条件的所有
      // console.log(f1); // 输出第一个原子组匹配的内容
      // console.log(f2);// 输出第二个原子组匹配的内容
      return `&lt;p&gt;${f2}&lt;/p&gt;`
    },
  ) // 函数表示
&lt;/script&gt;

</code></pre>
<h2>不记录分组</h2>
<ol>
<li>使用不记录分组,就是使用原子组后 无法再去使用函数参数去调用</li>
</ol>
<pre><code>const url = `https://u.ryanuo.cc
    http://github.com
    httpS://ryanuo.cc`
// 使用不记录分组,就是使用原子组后 无法再去使用函数参数去调用
const reg = /https?:\/\/((?:\w+\.)?\w+\.(?:com|top))/gi
console.log(reg.test(url)) // true // 有满足条件的内容
console.log(url.match(reg)) // [&quot;https://u.ryanuo.cc&quot;, &quot;http://github.com&quot;, &quot;httpS://ryanuo.cc&quot;]
//    console.log(reg.exec(url)); // 返回满足条件的第一个内容
//    console.log(reg.lastIndex); // 记录下次开始时的索引位置
const urlA = []
while (res = reg.exec(url)) urlA.push(res[1])
console.log(urlA)
</code></pre>
<h2>重复匹配</h2>
<ol>
<li><code>+</code> 一个或者多个 <code>{1,}</code> 类似与加号</li>
<li><code>*</code> 0个或者多个 <code>{0,}</code> 类似与*号</li>
<li><code>?</code> 0个或者1个</li>
</ol>
<h2>批量使用正则完成密码的验证</h2>
<pre><code>// 对表单输入的值进行验证
document.querySelector('[name=pwd')
  .addEventListener('keyup', (e) =&gt; {
    // console.log();
    const value = e.target.value
    const reg = [
      /^[a-z0-9]{5,7}$/i,
      /[A-Z]/,
      /\d/
    ]
    const state = reg.every(v =&gt; v.test(value))
    console.log(state ? '正确' : '错误')
  })
</code></pre>
<h2>禁止贪婪</h2>
<ol>
<li><code>{2,}?</code> 限制贪婪</li>
<li><code>*?</code> 禁止贪婪</li>
</ol>
<pre><code>&lt;body&gt;
  &lt;main&gt;
    &lt;span&gt;u.ryanuo.cc1&lt;/span&gt;
    &lt;span&gt;u.ryanuo.cc2&lt;/span&gt;
    &lt;span&gt;u.ryanuo.cc3&lt;/span&gt;
  &lt;/main&gt;
&lt;/body&gt;
&lt;script&gt;
  let main = document.querySelector('main')
  let reg = /&lt;span&gt;([^]+?)&lt;\/span&gt;/g // ?加上问号 并且写到原子组中为了将贪婪的范围缩小到每一行上
  main.innerHTML = main.innerHTML.replace(reg, (p0, p1) =&gt; {
    console.log(p0)
    return `&lt;h2 style=&quot;color:red&quot;&gt;${p1}&lt;/h2&gt;`
  })
&lt;/script&gt;

</code></pre>
<h2>matchAll 全局匹配</h2>
<pre><code>&lt;body&gt;
  &lt;main&gt;
    &lt;span&gt;u.ryanuo.cc1&lt;/span&gt;
    &lt;span&gt;u.ryanuo.cc2&lt;/span&gt;
    &lt;span&gt;u.ryanuo.cc3&lt;/span&gt;
  &lt;/main&gt;
&lt;/body&gt;
&lt;script&gt;
  // 不使用matchAll方法
  let main = document.querySelector('main')
  let reg = /&lt;span&gt;([^]+?)&lt;\/span&gt;/g // ?加上问号 并且写到原子组中为了将贪婪的范围缩小到每一行上
  let content = []
  // main.innerHTML = main.innerHTML.replace(reg,(p0,p1)=&gt;{
  //     content.push(p1)
  //     return `&lt;h2 style=&quot;color:red&quot;&gt;${p1}&lt;/h2&gt;`
  // })
  // console.table(content)

  // 使用matchAll方法
  let hd = main.innerHTML.matchAll(reg)
  // console.log(main.innerHTML.matchAll()); // 遍历迭代
  for (const i of hd) {
    content.push(i[1])
    // console.log(i);
  }
  console.table(content)
&lt;/script&gt;

</code></pre>
<h2>exec 全局匹配</h2>
<ol>
<li>匹配时 <code>g</code>不能缺少</li>
<li><code>search</code> 返回匹配的索引值</li>
</ol>
<pre><code>// exec方法 匹配全局
let main = document.querySelector('main')
let reg = /&lt;span&gt;([\s\S]+?)&lt;\/span&gt;/g // ?加上问号 并且写到原子组中为了将贪婪的范围缩小到每一行上
function fn(string, reg) {
  const content = []
  while (res = reg.exec(string))
    content.push(res)
  return content
}
console.log(fn(main.innerHTML, reg))
// exec方法 匹配全局
let main = document.querySelector('main')
let reg = /&lt;span&gt;([\s\S]+?)&lt;\/span&gt;/g // ?加上问号 并且写到原子组中为了将贪婪的范围缩小到每一行上
const content = []
function fn(string, reg) {
  while (res = reg.exec(string)) content.push(res)
}
fn(main.innerHTML, reg)
console.log(content)
</code></pre>
<h2>$符</h2>
<ol>
<li><code>$`</code> 匹配开头的第一个字符</li>
<li><code>$'</code> 匹配结尾字符</li>
<li><code>$&amp;</code> 匹配自身</li>
</ol>
<pre><code>// let tel = '2020/12/23'
// console.log(tel.replace(/\//g,'-')); // 2020-12-23
// let tel = '(021)9999999 (023)4444444'
// console.log(tel.replace(/[(]+(\d{3})[)](\d{7})/g,'$1-$2')); //021-9999999 023-4444444
const tel = '%Harry='
console.log(tel.replace(/\w+/g, '$`')) // $` 匹配开头的第一个字符 %%=
console.log(tel.replace(/\w+/g, '$\'')) // $' 匹配结尾字符 %==
console.log(tel.replace(/\w+/g, '$&amp;')) // $&amp; 匹配自身
console.log(tel.replace(/\w+/g, '$`$`$&amp;$\'$\'')) // $&amp; 匹配自身
</code></pre>
<h2>原子组的别名</h2>
<ol>
<li>格式 <code>?&lt;别名&gt;</code></li>
<li>调用 <code>&lt;别名&gt;</code></li>
</ol>
<pre><code>&lt;body&gt;
  &lt;main&gt;
    &lt;a href=&quot;http://u.ryanuo.cc&quot;&gt;博客&lt;/a&gt;
    &lt;a href=&quot;https://baidu.com&quot;&gt;百度&lt;/a&gt;
    &lt;a href=&quot;https://github.com&quot;&gt;Github&lt;/a&gt;
  &lt;/main&gt;
&lt;/body&gt;
&lt;script&gt;
  let main = document.querySelector('main')
  let reg = /&lt;a.*?href=(['&quot;])(?&lt;link&gt;.*?)\1&gt;(?&lt;title&gt;.*?)&lt;\/a&gt;/gi
  // 进行迭代遍历
  for (const i of main.innerHTML.matchAll(reg)) {
    // console.log(i);
    console.table(i.groups)
  }
&lt;/script&gt;

</code></pre>
<h2>断言匹配 ?=</h2>
<ol>
<li><code>(?=)</code> 表示在之前条件的后面满足该断言匹配的内容</li>
<li><code>(?&lt;=)</code> 表示前面是条件的匹配内容</li>
<li><code>(?!)</code> 表示后面不是某个条件的内容</li>
<li><code>(?&lt;!)</code> 表示前面不是某个条件的内容</li>
</ol>
<pre><code>&lt;body&gt;
  &lt;main&gt;
    &lt;a href=&quot;http://u.ryanuo.cc&quot;&gt;博客&lt;/a&gt;
    &lt;a href=&quot;https://baidu.com&quot;&gt;百度&lt;/a&gt;
    &lt;a href=&quot;https://github.com&quot;&gt;Github&lt;/a&gt;
  &lt;/main&gt;
&lt;/body&gt;
&lt;script&gt;
  let main = document.querySelector('main')
  let reg = /(?&lt;=href=(['&quot;]))(.*?)(?=\1&gt;)/gi
  main.innerHTML = main.innerHTML.replace(reg, 'https://ryanuo.cc')
&lt;/script&gt;

</code></pre>
<h3>手机号码断言隐藏</h3>
<pre><code>const users = `
李四：12323212232
张三：12322131122
`
const reg = /(?&lt;=\d{7})\d{4}/g
console.log(users.replace(reg, '*'.repeat(4)))
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Java Knowledge Collation Method Overloading Recursion]]></title>
            <link>https://ryanuo.cc/posts/java</link>
            <guid>https://ryanuo.cc/posts/java</guid>
            <pubDate>Tue, 20 Jul 2021 10:18:18 GMT</pubDate>
            <description><![CDATA[java method overloading and recursion usage methods and examples]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>方法的重载</h2>
<h3>好处</h3>
<ul>
<li>减少方法名称的记忆</li>
<li>可以忽略参数的自动类型转换</li>
</ul>
<h3>定义</h3>
<blockquote>
<p>方法的重载是指一个类中定义了多个相同名字的方法，需要每个方法具有不同的参数类型或者参数类型的相同个数不同，调用的时候可以根据该方法的参数类型或者个数类型区分对应的方法;方法的重载和返回值类型，修饰词没有关系</p>
</blockquote>
<h3>适用场景</h3>
<blockquote>
<p>当一个类中出现相同的功能时,但是需要的参数不同 参数 类型不同 个数不同</p>
</blockquote>
<h3>要求</h3>
<ol>
<li>必须在同一个类中</li>
<li>方法名的相同</li>
<li>参数列表不同 (不同型 同型不同个 顺序)</li>
<li>返回值不同</li>
</ol>
<h2>递归</h2>
<ol>
<li>在调用过程中不断的调用自己</li>
</ol>
<pre><code class="language-java">public class text5 {
    public static void main(String[] args){
        int a = 1;
        System.out.println(add(a));
    }
    static int add(int a){
        return a==5?5:a+add(a+1);
    }
}
</code></pre>
<h2>斐波那契</h2>
<p>{% folding red, java %}</p>
<pre><code class="language-java">public class text7 {
    public static void main(String[] args) {
        System.out.println(&quot;请月份数：&quot;);
        Scanner s = new Scanner(System.in);
        int n = s.nextInt();
        System.out.println(&quot;第&quot; + n + &quot;个数：&quot; + f(n));
//        for (int n=1;n&lt;50;n++){
//            System.out.println(&quot;第&quot;+n+&quot;个数：&quot; + f(n));
//        }
    }
    public static long f(int n) {
        return n==1 || n==2 ? 1 : f(n - 1) + f(n - 2);
    }
}
</code></pre>
<p>{% folding red, python %}</p>
<pre><code class="language-python"># -*- coding:utf-8 -*-
def f(n):
    return n if n &lt; 2 else f(n - 1) + f(n - 2)
if __name__ == '__main__':
    num = input('请输入:')
    print(f(int(num)))
</code></pre>
<h3>进制转换</h3>
<ol>
<li>思路: 将输入数除2判断 商是否等于0 如果等于0则终止返回，不等于0 返回余数+方法*10 每次执行方法返回的值都要乘10 最后将每次计算的都加起来</li>
</ol>
<pre><code class="language-java">//十进制准换二进制
import java.util.Scanner;
public class text9 {
    public static void main(String[] args) {
        System.out.println(&quot;请输入十进制数：&quot;);
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        System.out.println(toBin(num));
    }
    static int toBin(int num) {
        return num == 0 ? 0 : num % 2 + toBin(num / 2) * 10;
    }
}
</code></pre>
<h2>面向对象</h2>
<ol>
<li>封装 权限 访问接口</li>
<li>继承 原有的基础之上扩展性 兼容 单继承</li>
<li>多态 符合规则的继续大范围扩展 向上造型 向下造型</li>
<li>抽象</li>
</ol>
<h2>类和对象的概念</h2>
<ol>
<li>对象: 实际存在的事物</li>
<li>类: 实际存在的事物进行抽象形成的模板,描述对象的共同特征</li>
</ol>
<h3>对象的创建和使用</h3>
<ol>
<li>类定义之后，就有了模板 模板可以创建对象 了可以创建任意多个对象</li>
<li>创建类 new 构造方法()</li>
<li>如果想使用创建的对象 需要使用变量来承接</li>
<li>不赋值有默认值，主动赋值默认值消失</li>
</ol>
<h2>JVM内存管理</h2>
<ol>
<li>栈: 先进后出 （在程序运行的过程中存放局部变量和运行中的方法）线性地址连续</li>
<li>方法区：已被加载的类信息，常量，静态变量;</li>
<li>堆: 虚拟机启动的时候,创建堆;存放对象;地址可以不连续</li>
<li>c语言中指针是操作存储空间 java不可以操作堆地址 只能使用堆中的信息，栈中引用是指是对象的地址;打印出来的不是地址，这个变量是基本类型局部变量</li>
</ol>
<p><img src="https://cloud.ryanuo.cc/hexo/api/zhan.png" alt=""></p>
<h2>构造方法Constructor</h2>
<ol>
<li>定义: 构造方法是类中一种特殊的方法，通过构造方法可以完成对象的创建，以及对象属性的初始化操作</li>
<li>修饰词 类名(参数列表){方法体}<code>构造方法没有返回值类型</code>，<code>构造方法的方法名和类名相同</code>, <code>构造方法同来创建对象，并且为对象初始化实例变量</code>,<code>一个类中可以定义多个构造方法，需要符合方法的重载</code>，<code>实质是有返回值，返回值类型是类</code>，<code>类中不写构造方法，默认系统会自动生成一个无参构造方法</code></li>
</ol>
<h3>作用</h3>
<ol>
<li>创建对象</li>
<li>初始化对象（方法中必须赋值）</li>
</ol>
<pre><code class="language-java">public class text5 {
  int year;
  int month;
  int day;
  public text5(int y, int m, int d) {
    // TODO 自动生成的构造函数存根
    year = y;
    month = m;
    day = d;  // 就近原则this
  }
  void show() {
    System.out.println(year + &quot;-&quot; + month + &quot;-&quot; + day);
  }
}

// 构造方法
public class text6 {
  public static void main(String[] args) {
    // TODO 自动生成的方法存根
//    创建日期对象
    text5 d5 = new text5(2021,7,22);
    d5.show();
    System.out.print(&quot;------------&quot;);
  }
}

</code></pre>
<h3>方法的重載</h3>
<p>构造方法也有方法的重载</p>
<h2>空指针异常</h2>
<ol>
<li>引用为空</li>
<li>引用对象=&gt; 空指针错误 <code>java.lang.NullPointerException</code></li>
</ol>
<pre><code class="language-java">package demo1;
public class text10 {
  public static void main(String[] args) {
    text8 d = new text8(2000,10,2);
    text9 t9 = new text9();
    text8 w = new text8();
    w.year = 1999;
    w.month = 5;
    w.day = 4;
    t9.name = &quot;张三&quot;;
    t9.id = &quot;001&quot;;
    t9.birth = d;
    Wife n = new Wife(&quot;李四&quot;,&quot;002&quot;,w);
    System.out.println(n.birth.ShowDate());
    System.out.println(t9.birth.ShowDate());
  }
}
//
package demo1;

public class text8 {
  int year;
  int month;
  int day;

  public text8() {
    // TODO 自动生成的构造函数存根

  }
  text8(int y,int m, int d){
    year=y;
    month=m;
    day=d;
//    System.out.println(ShowDate());
  }
  String ShowDate(){
    return year+&quot;-&quot;+month+&quot;-&quot;+day;
  }
}
//
package demo1;

public class text9 {
  String name;
  String id;
  text8 birth;
  public text9() {
    // TODO 自动生成的构造函数存根
  }
}
//
package demo1;

public class Wife {
  String name;
  String id;
  text8 birth;
  public Wife() {
    // TODO 自动生成的构造函数存根
  }
  public Wife(String string, String string2, text8 d) {
    // TODO 自动生成的构造函数存根
    name = string;
    id = string2;
    birth = d;
  }
}

</code></pre>
<h2>封装</h2>
<p>在面向对象程式设计方法中，封装（英语：Encapsulation）是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法</p>
<ul>
<li>封装的优点</li>
</ul>
<ol>
<li>
<p>良好的封装能够减少耦合。</p>
</li>
<li>
<p>类内部的结构可以自由修改。</p>
</li>
<li>
<p>可以对成员变量进行更精确的控制。</p>
</li>
<li>
<p>隐藏信息，实现细节。</p>
</li>
<li>
<p>提供公共的入口 使用<code>set</code>修改 和 <code>get</code>访问 方法</p>
</li>
</ol>
<pre><code class="language-java">public class Product {
  private int pro_id;
  private String pro_name;
  private int pro_price;
  public Product() {
    // TODO 自动生成的构造函数存根
  }
//  设置
  public void setId(int i) {
    pro_id = i;
  }
  public void setName(String i) {
    pro_name = i;
  }
  public void setPrice(int i) {
    pro_price = i;
  }
//  查看
  public int getId() {
    return pro_id;
  }
  public String getName() {
    return pro_name;
  }
  public int getPrice() {
    return pro_price;
  }
//  有参封装
  Product(int i,String n,int p){
    pro_id = i;
    pro_name = n;
    pro_price = p;
  }
//  返回所有的情况
  String ShowProduct(){
    return &quot;商品id为：&quot;+pro_id+&quot;\n商品名称为：&quot;+pro_name+&quot;\n商品价格为&quot;+pro_price;
  }
}

// 调用
public class Build {
  public static void main(String[] args) {
    Product p1 = new Product();
    p1.setId(1);
    p1.setName(&quot;手机&quot;);
    p1.setPrice(12000);
    Product p2 = new Product(2,&quot;电脑&quot;,22222);
    System.out.println(p2.ShowProduct());
    System.out.println(p1.getPrice());  // 获取单个商品的信息 使用get方法
    System.out.println(p2.ShowProduct());
  }
}

</code></pre>
<h2>this 关键字</h2>
<ol>
<li>this本质是对象的地址，this关键字指向的是当前对象的引用</li>
<li>this 内容相同 每个this指向对象的地址 都在堆中</li>
<li>本质就是一个变量或者引用，存储在对象的内部</li>
<li>this本身的内容就是该对象的地址值</li>
<li>this关键字来区分局部变量和实例变量。</li>
<li>在构造方法中 使用<code>this() = new 构造方法</code> 只能在构造方法中的第一行使用</li>
</ol>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Java知识整理-方法重载,递归]]></title>
            <link>https://ryanuo.cc/zh/posts/java</link>
            <guid>https://ryanuo.cc/zh/posts/java</guid>
            <pubDate>Tue, 20 Jul 2021 10:18:18 GMT</pubDate>
            <description><![CDATA[Java方法重载和递归的使用方法和示例。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>方法的重载</h2>
<h3>好处</h3>
<ul>
<li>减少方法名称的记忆</li>
<li>可以忽略参数的自动类型转换</li>
</ul>
<h3>定义</h3>
<blockquote>
<p>方法的重载是指一个类中定义了多个相同名字的方法，需要每个方法具有不同的参数类型或者参数类型的相同个数不同，调用的时候可以根据该方法的参数类型或者个数类型区分对应的方法;方法的重载和返回值类型，修饰词没有关系</p>
</blockquote>
<h3>适用场景</h3>
<blockquote>
<p>当一个类中出现相同的功能时,但是需要的参数不同 参数 类型不同 个数不同</p>
</blockquote>
<h3>要求</h3>
<ol>
<li>必须在同一个类中</li>
<li>方法名的相同</li>
<li>参数列表不同 (不同型 同型不同个 顺序)</li>
<li>返回值不同</li>
</ol>
<h2>递归</h2>
<ol>
<li>在调用过程中不断的调用自己</li>
</ol>
<pre><code class="language-java">public class text5 {
    public static void main(String[] args){
        int a = 1;
        System.out.println(add(a));
    }
    static int add(int a){
        return a==5?5:a+add(a+1);
    }
}
</code></pre>
<h2>斐波那契</h2>
<p>{% folding red, java %}</p>
<pre><code class="language-java">public class text7 {
    public static void main(String[] args) {
        System.out.println(&quot;请月份数：&quot;);
        Scanner s = new Scanner(System.in);
        int n = s.nextInt();
        System.out.println(&quot;第&quot; + n + &quot;个数：&quot; + f(n));
//        for (int n=1;n&lt;50;n++){
//            System.out.println(&quot;第&quot;+n+&quot;个数：&quot; + f(n));
//        }
    }
    public static long f(int n) {
        return n==1 || n==2 ? 1 : f(n - 1) + f(n - 2);
    }
}
</code></pre>
<p>{% folding red, python %}</p>
<pre><code class="language-python"># -*- coding:utf-8 -*-
def f(n):
    return n if n &lt; 2 else f(n - 1) + f(n - 2)
if __name__ == '__main__':
    num = input('请输入:')
    print(f(int(num)))
</code></pre>
<h3>进制转换</h3>
<ol>
<li>思路: 将输入数除2判断 商是否等于0 如果等于0则终止返回，不等于0 返回余数+方法*10 每次执行方法返回的值都要乘10 最后将每次计算的都加起来</li>
</ol>
<pre><code class="language-java">//十进制准换二进制
import java.util.Scanner;
public class text9 {
    public static void main(String[] args) {
        System.out.println(&quot;请输入十进制数：&quot;);
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        System.out.println(toBin(num));
    }
    static int toBin(int num) {
        return num == 0 ? 0 : num % 2 + toBin(num / 2) * 10;
    }
}
</code></pre>
<h2>面向对象</h2>
<ol>
<li>封装 权限 访问接口</li>
<li>继承 原有的基础之上扩展性 兼容 单继承</li>
<li>多态 符合规则的继续大范围扩展 向上造型 向下造型</li>
<li>抽象</li>
</ol>
<h2>类和对象的概念</h2>
<ol>
<li>对象: 实际存在的事物</li>
<li>类: 实际存在的事物进行抽象形成的模板,描述对象的共同特征</li>
</ol>
<h3>对象的创建和使用</h3>
<ol>
<li>类定义之后，就有了模板 模板可以创建对象 了可以创建任意多个对象</li>
<li>创建类 new 构造方法()</li>
<li>如果想使用创建的对象 需要使用变量来承接</li>
<li>不赋值有默认值，主动赋值默认值消失</li>
</ol>
<h2>JVM内存管理</h2>
<ol>
<li>栈: 先进后出 （在程序运行的过程中存放局部变量和运行中的方法）线性地址连续</li>
<li>方法区：已被加载的类信息，常量，静态变量;</li>
<li>堆: 虚拟机启动的时候,创建堆;存放对象;地址可以不连续</li>
<li>c语言中指针是操作存储空间 java不可以操作堆地址 只能使用堆中的信息，栈中引用是指是对象的地址;打印出来的不是地址，这个变量是基本类型局部变量</li>
</ol>
<p><img src="https://cloud.ryanuo.cc/hexo/api/zhan.png" alt=""></p>
<h2>构造方法Constructor</h2>
<ol>
<li>定义: 构造方法是类中一种特殊的方法，通过构造方法可以完成对象的创建，以及对象属性的初始化操作</li>
<li>修饰词 类名(参数列表){方法体}<code>构造方法没有返回值类型</code>，<code>构造方法的方法名和类名相同</code>, <code>构造方法同来创建对象，并且为对象初始化实例变量</code>,<code>一个类中可以定义多个构造方法，需要符合方法的重载</code>，<code>实质是有返回值，返回值类型是类</code>，<code>类中不写构造方法，默认系统会自动生成一个无参构造方法</code></li>
</ol>
<h3>作用</h3>
<ol>
<li>创建对象</li>
<li>初始化对象（方法中必须赋值）</li>
</ol>
<pre><code class="language-java">public class text5 {
  int year;
  int month;
  int day;
  public text5(int y, int m, int d) {
    // TODO 自动生成的构造函数存根
    year = y;
    month = m;
    day = d;  // 就近原则this
  }
  void show() {
    System.out.println(year + &quot;-&quot; + month + &quot;-&quot; + day);
  }
}

// 构造方法
public class text6 {
  public static void main(String[] args) {
    // TODO 自动生成的方法存根
    text5 d5 = new text5(2021,7,22);
    d5.show();
    System.out.print(&quot;------------&quot;);
  }
}
</code></pre>
<h3>方法的重載</h3>
<p>构造方法也有方法的重载</p>
<h2>空指针异常</h2>
<ol>
<li>引用为空</li>
<li>引用对象=&gt; 空指针错误 <code>java.lang.NullPointerException</code></li>
</ol>
<pre><code class="language-java">package demo1;
public class text10 {
  public static void main(String[] args) {
  text8 d = new text8(2000,10,2);
  text9 t9 = new text9();
  text8 w = new text8();
  w.year = 1999;
  w.month = 5;
  w.day = 4;
  t9.name = &quot;张三&quot;;
  t9.id = &quot;001&quot;;
  t9.birth = d;
  Wife n = new Wife(&quot;李四&quot;,&quot;002&quot;,w);
  System.out.println(n.birth.ShowDate());
  System.out.println(t9.birth.ShowDate());
  }
}
//
package demo1;

public class text8 {
  int year;
  int month;
  int day;

  public text8() {
    // TODO 自动生成的构造函数存根

  }
  text8(int y,int m, int d){
    year=y;
    month=m;
    day=d;
//    System.out.println(ShowDate());
  }
  String ShowDate(){
    return year+&quot;-&quot;+month+&quot;-&quot;+day;
  }
}
//
package demo1;

public class text9 {
  String name;
  String id;
  text8 birth;
  public text9() {
    // TODO 自动生成的构造函数存根
  }
}
//
package demo1;

public class Wife {
  String name;
  String id;
  text8 birth;
  public Wife() {
    // TODO 自动生成的构造函数存根
  }
  public Wife(String string, String string2, text8 d) {
    // TODO 自动生成的构造函数存根
    name = string;
    id = string2;
    birth = d;
  }
}

</code></pre>
<h2>封装</h2>
<p>在面向对象程式设计方法中，封装（英语：Encapsulation）是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法</p>
<ul>
<li>封装的优点</li>
</ul>
<ol>
<li>
<p>良好的封装能够减少耦合。</p>
</li>
<li>
<p>类内部的结构可以自由修改。</p>
</li>
<li>
<p>可以对成员变量进行更精确的控制。</p>
</li>
<li>
<p>隐藏信息，实现细节。</p>
</li>
<li>
<p>提供公共的入口 使用<code>set</code>修改 和 <code>get</code>访问 方法</p>
</li>
</ol>
<pre><code class="language-java">public class Product {
  private int pro_id;
  private String pro_name;
  private int pro_price;
  public Product() {
    // TODO 自动生成的构造函数存根
  }
//  设置
  public void setId(int i) {
    pro_id = i;
  }
  public void setName(String i) {
    pro_name = i;
  }
  public void setPrice(int i) {
    pro_price = i;
  }
//  查看
  public int getId() {
    return pro_id;
  }
  public String getName() {
    return pro_name;
  }
  public int getPrice() {
    return pro_price;
  }
//  有参封装
  Product(int i,String n,int p){
    pro_id = i;
    pro_name = n;
    pro_price = p;
  }
//  返回所有的情况
  String ShowProduct(){
    return &quot;商品id为：&quot;+pro_id+&quot;\n商品名称为：&quot;+pro_name+&quot;\n商品价格为&quot;+pro_price;
  }
}

// 调用
public class Build {
  public static void main(String[] args) {
    Product p1 = new Product();
    p1.setId(1);
    p1.setName(&quot;手机&quot;);
    p1.setPrice(12000);
    Product p2 = new Product(2,&quot;电脑&quot;,22222);
    System.out.println(p2.ShowProduct());
    System.out.println(p1.getPrice());  // 获取单个商品的信息 使用get方法
    System.out.println(p2.ShowProduct());
  }
}

</code></pre>
<h2>this 关键字</h2>
<ol>
<li>this本质是对象的地址，this关键字指向的是当前对象的引用</li>
<li>this 内容相同 每个this指向对象的地址 都在堆中</li>
<li>本质就是一个变量或者引用，存储在对象的内部</li>
<li>this本身的内容就是该对象的地址值</li>
<li>this关键字来区分局部变量和实例变量。</li>
<li>在构造方法中 使用<code>this() = new 构造方法</code> 只能在构造方法中的第一行使用</li>
</ol>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Python Gui Tkinter]]></title>
            <link>https://ryanuo.cc/posts/tkinter</link>
            <guid>https://ryanuo.cc/posts/tkinter</guid>
            <pubDate>Sat, 10 Apr 2021 10:42:32 GMT</pubDate>
            <description><![CDATA[Tkinter 是使用 python 进行窗口视窗设计的模块。Tkinter模块("Tk 接口")是Python的标准Tk GUI工具包的接口。作为 python 特定的GUI界面，是一个图像的窗口，tkinter是python 自带的，可以编辑的GUI界面，我们可以用GUI 实现很多直观的功能，比如想开发一个计算器，如果只是一个程序输入，输出窗口的话，是没用用户体验的。所有开发一个图像化的小窗口，就是必要的。]]></description>
            <content:encoded><![CDATA[<h2>tkinter是什么</h2>
<ol>
<li>Tkinter 是使用 python 进行窗口视窗设计的模块。Tkinter模块(&quot;Tk 接口&quot;)是Python的标准Tk GUI工具包的接口。作为 python 特定的GUI界面，是一个图像的窗口，tkinter是python 自带的，可以编辑的GUI界面，我们可以用GUI 实现很多直观的功能，比如想开发一个计算器，如果只是一个程序输入，输出窗口的话，是没用用户体验的。所有开发一个图像化的小窗口，就是必要的。</li>
</ol>
<h2>Label标签</h2>
<ol>
<li>创建好主窗口才能在上面放置各种控件元素</li>
<li><code>tk.Lable(window,text=&quot;文本内容&quot;，bg='背景颜色',font=('字体family',字体大小)),width=标签宽度,height=标签高度 以字符的大小为单位</code></li>
<li><code>window.title('窗口名称')</code></li>
<li>window.geometry('窗口的大小')</li>
<li>放置标签x.pack()或者x.place()</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上设定标签
l = tk.Label(window, text='你好！this is Tkinter', bg='green', font=('Arial', 12), width=30, height=2)
# 说明： bg为背景，font为字体，width为长，height为高，这里的长和高是字符的长和高，比如height=2,就是标签有2个字符这么高

# 第5步，放置标签
l.pack()    # Label内容content区域放置位置，自动调节尺寸
# 放置lable的方法有：1）l.pack(); 2)l.place();

# 第6步，主窗口循环显示
window.mainloop()
# 注意，loop因为是循环的意思，window.mainloop就会让window不断的刷新，如果没有mainloop,就是一个静态的window,传入进去的值就不会有循环，mainloop就相当于一个很大的while循环，有个while，每点击一次就会更新一次，所以我们必须要有循环
# 所有的窗口文件都必须有类似的mainloop函数，mainloop是窗口文件的关键的关键。
</code></pre>
<h2>Button窗口部件</h2>
<ol>
<li>定义一个函数功能（内容自己自由编写），供点击Button按键时调用，调用命令参数command=函数名</li>
</ol>
<pre><code class="language-python">
import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x
# 第4步，在图形界面上设定标签
# 说明： bg为背景，font为字体，width为长，height为高，这里的长和高是字符的长和高，比如height=2,就是标签有2个字符这么高
var = tk.StringVar()  # 将label标签的内容设置为字符类型，用var来接收hit_me函数的传出内容用以显示在标签上
l = tk.Label(window, textvariable=var, bg='green', fg='white', font=('Arial', 12), width=30, height=2)
# 说明： bg为背景，fg为字体颜色，font为字体，width为长，height为高，这里的长和高是字符的长和高，比如height=2,就是标签有2个字符这么高
l.pack()
# 定义一个函数功能（内容自己自由编写），供点击Button按键时调用，调用命令参数command=函数名
on_hit = False
def hit_me():
    global on_hit
    if on_hit == False:
        on_hit = True
        var.set('you hit me')
    else:
        on_hit = False
        var.set('')
# 第5步，在窗口界面设置放置Button按键
b = tk.Button(window, text='hit me', font=('Arial', 12), width=10, height=1, command=hit_me)
# 第5步，放置标签
# x.pack()  # Label内容content区域放置位置，自动调节尺寸
b.pack()
# 放置lable的方法有：1）l.pack(); 2)l.place();

# 第6步，主窗口循环显示
window.mainloop()
# 注意，loop因为是循环的意思，window.mainloop就会让window不断的刷新，如果没有mainloop,就是一个静态的window,传入进去的值就不会有循环，mainloop就相当于一个很大的while循环，有个while，每点击一次就会更新一次，所以我们必须要有循环
# 所有的窗口文件都必须有类似的mainloop函数，mainloop是窗口文件的关键的关键。
</code></pre>
<h2>Entry窗口部件</h2>
<ol>
<li>Entry是tkinter类中提供的的一个单行文本输入域，用来输入显示一行文本，收集键盘输入(类似 HTML 中的 text)</li>
</ol>
<pre><code class="language-python">e1 = tk.Entry(window, show='*', font=('Arial', 14))   # 显示成密文形式
e2 = tk.Entry(window, show=None, font=('Arial', 14))  # 显示成明文形式
e2.pack()
</code></pre>
<h2>Text窗口部件</h2>
<ol>
<li>Text是tkinter类中提供的的一个多行文本区域，显示多行文本，可用来收集(或显示)用户输入的文字(类似 HTML 中的 textarea)，格式化文本显示，允许你用不同的样式和属性来显示和编辑文本，同时支持内嵌图象和窗口。</li>
<li>insert 在焦点处插入 ，end 在文本末尾插入数据</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上设定输入框控件entry框并放置
e = tk.Entry(window, show=None)  # 显示成明文形式
e.pack()

# 第5步，定义两个触发事件时的函数insert_point和insert_end（注意：因为Python的执行顺序是从上往下，所以函数一定要放在按钮的上面）
def insert_point():  # 在鼠标焦点处插入输入内容
    var = e.get()
    t.insert('insert', var)

def insert_end():  # 在文本框内容最后接着插入输入内容
    var = e.get()
    t.insert('end', var)

# 第6步，创建并放置两个按钮分别触发两种情况
b1 = tk.Button(window, text='insert point', width=10,
               height=2, command=insert_point)
b1.pack()
b2 = tk.Button(window, text='insert end', width=10,
               height=2, command=insert_end)
b2.pack()

# 第7步，创建并放置一个多行文本框text用以显示，指定height=3为文本框是三个字符高度
t = tk.Text(window, height=3)
t.pack()

# 第8步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Listbox窗口部件</h2>
<ol>
<li>Text是tkinter类中提供的的列表框部件，显示供选方案的一个列表。listbox能够被配置来得到radiobutton或checklist的行为。</li>
<li>lb.get(lb.curselection()) 获取列表选中文本的值</li>
<li>可以使用insert将值遍历到listbox中</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签label用以显示并放置
var1 = tk.StringVar()  # 创建变量，用var1用来接收鼠标点击具体选项的内容
l = tk.Label(window, bg='green', fg='yellow', font=('Arial', 12), width=10, textvariable=var1)
l.pack()

# 第6步，创建一个方法用于按钮的点击事件
def print_selection():
    value = lb.get(lb.curselection())  # 获取当前选中的文本
    var1.set(value)  # 为label设置值

# 第5步，创建一个按钮并放置，点击按钮调用print_selection函数
b1 = tk.Button(window, text='print selection', width=15, height=2, command=print_selection)
b1.pack()

# 第7步，创建Listbox并为其添加内容
var2 = tk.StringVar()
var2.set((1, 2, 3, 4))  # 为变量var2设置值
# 创建Listbox
lb = tk.Listbox(window, listvariable=var2)  # 将var2的值赋给Listbox
# 创建一个list并将值循环添加到Listbox控件中
list_items = [11, 22, 33, 44]
for item in list_items:
    lb.insert('end', item)  # 从最后一个位置开始加入值
lb.insert(1, 'first')  # 在第一个位置加入'first'字符
lb.insert(2, 'second')  # 在第二个位置加入'second'字符
lb.delete(2)  # 删除第二个位置的字符
lb.pack()

# 第8步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Radiobutton窗口部件</h2>
<ol>
<li>代表一个变量，它可以有多个值中的一个。点击它将为这个变量设置值，并且清除与这同一变量相关的其它radiobutton。类似于单选</li>
<li>tk.Radiobutton(window, text='Option A', variable=var, value='A', command=print_selection)</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签label用以显示并放置
var = tk.StringVar()    # 定义一个var用来将radiobutton的值和Label的值联系在一起.
l = tk.Label(window, bg='yellow', width=20, text='empty')
l.pack()

# 第6步，定义选项触发函数功能
def print_selection():
    l.config(text='you have selected ' + var.get())

# 第5步，创建三个radiobutton选项，其中variable=var, value='A'的意思就是，当我们鼠标选中了其中一个选项，把value的值A放到变量var中，然后赋值给variable
r1 = tk.Radiobutton(window, text='Option A', variable=var, value='A', command=print_selection)
r1.pack()
r2 = tk.Radiobutton(window, text='Option B', variable=var, value='B', command=print_selection)
r2.pack()
r3 = tk.Radiobutton(window, text='Option C', variable=var, value='C', command=print_selection)
r3.pack()

# 第7步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Checkbutton窗口部件</h2>
<ol>
<li>代表一个变量，它有两个不同的值。点击这个按钮将会在这两个值间切换，选择和取消选择。</li>
<li><code>l.config</code> 将值显示在标签上</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签label用以显示并放置
l = tk.Label(window, bg='yellow', width=20, text='empty')
l.pack()

# 第6步，定义触发函数功能
def print_selection():
    if (var1.get() == 1) &amp; (var2.get() == 0):  # 如果选中第一个选项，未选中第二个选项
        l.config(text='I love only Python ')
    elif (var1.get() == 0) &amp; (var2.get() == 1):  # 如果选中第二个选项，未选中第一个选项
        l.config(text='I love only C++')
    elif (var1.get() == 0) &amp; (var2.get() == 0):  # 如果两个选项都未选中
        l.config(text='I do not love either')
    else:
        l.config(text='I love both')  # 如果两个选项都选中

# 第5步，定义两个Checkbutton选项并放置
var1 = tk.IntVar()  # 定义var1和var2整型变量用来存放选择行为返回值
var2 = tk.IntVar()
c1 = tk.Checkbutton(window, text='Python', variable=var1, onvalue=1, offvalue=0,
                    command=print_selection)  # 传值原理类似于radiobutton部件
c1.pack()
c2 = tk.Checkbutton(window, text='C++', variable=var2, onvalue=1, offvalue=0, command=print_selection)
c2.pack()

# 第7步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Scale窗口部件</h2>
<ol>
<li>尺度（拉动条），允许你通过滑块来设置一数字值。</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签label用以显示并放置
l = tk.Label(window, bg='green', fg='white', width=20, text='empty')
l.pack()

# 第6步，定义一个触发函数功能
def print_selection(v):
    l.config(text='you have selected ' + v)

# 第5步，创建一个尺度滑条，长度200字符，从0开始10结束，以2为刻度，精度为0.01，触发调用print_selection函数
s = tk.Scale(window, label='try me', from_=0, to=10, orient=tk.HORIZONTAL, length=200, showvalue=0,tickinterval=2, resolution=0.01, command=print_selection)
s.pack()

# 第7步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Canvas窗口部件</h2>
<ol>
<li>画布，提供绘图功能(直线、椭圆、多边形、矩形) 可以包含图形或位图，用来绘制图表和图，创建图形编辑器，实现定制窗口部件。</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建 500 * 200 大小的画布并放置各种元素
canvas = tk.Canvas(window, bg='green', height=200, width=500)
# 说明图片位置，并导入图片到画布上
image_file = tk.PhotoImage(file='pic.gif')  # 图片位置（相对路径，与.py文件同一文件夹下，也可以用绝对路径，需要给定图片具体绝对路径）
image = canvas.create_image(250, 0, anchor='n',image=image_file)        # 图片锚定点（n图片顶端的中间点位置）放在画布（250,0）坐标处
# 定义多边形参数，然后在画布上画出指定图形
x0, y0, x1, y1 = 100, 100, 150, 150
line = canvas.create_line(x0-50, y0-50, x1-50, y1-50)                   # 画直线
oval = canvas.create_oval(x0+120, y0+50, x1+120, y1+50, fill='yellow')  # 画圆 用黄色填充
arc = canvas.create_arc(x0, y0+50, x1, y1+50, start=0, extent=180)      # 画扇形 从0度打开收到180度结束
rect = canvas.create_rectangle(330, 30, 330+20, 30+20)                  # 画矩形正方形
canvas.pack()

# 第6步，触发函数，用来一定指定图形
def moveit():
    canvas.move(rect, 2, 2) # 移动正方形rect（也可以改成其他图形名字用以移动一起图形、元素），按每次（x=2, y=2）步长进行移动

# 第5步，定义一个按钮用来移动指定图形的在画布上的位置
b = tk.Button(window, text='move item',command=moveit).pack()

# 第7步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Menu窗口部件</h2>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签用以显示内容并放置
l = tk.Label(window, text='      ', bg='green')
l.pack()

# 第10步，定义一个函数功能，用来代表菜单选项的功能，这里为了操作简单，定义的功能比较简单
counter = 0

def do_job():
    global counter
    l.config(text='do ' + str(counter))
    counter += 1

# 第5步，创建一个菜单栏，这里我们可以把他理解成一个容器，在窗口的上方
menubar = tk.Menu(window)

# 第6步，创建一个File菜单项（默认不下拉，下拉内容包括New，Open，Save，Exit功能项）
filemenu = tk.Menu(menubar, tearoff=0)
# 将上面定义的空菜单命名为File，放在菜单栏中，就是装入那个容器中
menubar.add_cascade(label='File', menu=filemenu)

# 在File中加入New、Open、Save等小菜单，即我们平时看到的下拉菜单，每一个小菜单对应命令操作。
filemenu.add_command(label='New', command=do_job)
filemenu.add_command(label='Open', command=do_job)
filemenu.add_command(label='Save', command=do_job)
filemenu.add_separator()  # 添加一条分隔线
filemenu.add_command(label='Exit', command=window.quit)  # 用tkinter里面自带的quit()函数

# 第7步，创建一个Edit菜单项（默认不下拉，下拉内容包括Cut，Copy，Paste功能项）
editmenu = tk.Menu(menubar, tearoff=0)
# 将上面定义的空菜单命名为 Edit，放在菜单栏中，就是装入那个容器中
menubar.add_cascade(label='Edit', menu=editmenu)

# 同样的在 Edit 中加入Cut、Copy、Paste等小命令功能单元，如果点击这些单元, 就会触发do_job的功能
editmenu.add_command(label='Cut', command=do_job)
editmenu.add_command(label='Copy', command=do_job)
editmenu.add_command(label='Paste', command=do_job)

# 第8步，创建第二级菜单，即菜单项里面的菜单
submenu = tk.Menu(filemenu)  # 和上面定义菜单一样，不过此处实在File上创建一个空的菜单
filemenu.add_cascade(label='Import', menu=submenu, underline=0)  # 给放入的菜单submenu命名为Import

# 第9步，创建第三级菜单命令，即菜单项里面的菜单项里面的菜单命令（有点拗口，笑~~~）
submenu.add_command(label='Submenu_1', command=do_job)  # 这里和上面创建原理也一样，在Import菜单项中加入一个小菜单命令Submenu_1

# 第11步，创建菜单栏完成后，配置让菜单栏menubar显示出来
window.config(menu=menubar)

# 第12步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Frame 窗口部件</h2>
<ol>
<li>框架，用来承载放置其他GUI元素，就是一个容器，是一个在 Windows 上分离小区域的部件, 它能将 Windows 分成不同的区,然后存放不同的其他部件. 同时一个 Frame 上也能再分成两个 Frame, Frame 可以认为是一种容器.</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签用以显示内容并放置
tk.Label(window, text='on the window', bg='red', font=('Arial', 16)).pack()   # 和前面部件分开创建和放置不同，其实可以创建和放置一步完成

# 第5步，创建一个主frame，长在主window窗口上
frame = tk.Frame(window)
frame.pack()

# 第6步，创建第二层框架frame，长在主框架frame上面
frame_l = tk.Frame(frame)# 第二层frame，左frame，长在主frame上
frame_r = tk.Frame(frame)# 第二层frame，右frame，长在主frame上
frame_l.pack(side='left')
frame_r.pack(side='right')

# 第7步，创建三组标签，为第二层frame上面的内容，分为左区域和右区域，用不同颜色标识
tk.Label(frame_l, text='on the frame_l1', bg='green').pack()
tk.Label(frame_l, text='on the frame_l2', bg='green').pack()
tk.Label(frame_l, text='on the frame_l3', bg='green').pack()
tk.Label(frame_r, text='on the frame_r1', bg='yellow').pack()
tk.Label(frame_r, text='on the frame_r2', bg='yellow').pack()
tk.Label(frame_r, text='on the frame_r3', bg='yellow').pack()

# 第8步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>messageBox窗口部件</h2>
<ol>
<li>类似于弹窗的操作</li>
</ol>
<pre><code class="language-python"> tkinter.messagebox.showinfo(title='Hi', message='你好！')  # 提示信息对话窗
    tkinter.messagebox.showwarning(title='Hi', message='有警告！')       # 提出警告对话窗
    tkinter.messagebox.showerror(title='Hi', message='出错了！')         # 提出错误对话窗
    print(tkinter.messagebox.askquestion(title='Hi', message='你好！'))  # 询问选择对话窗return 'yes', 'no'
    print(tkinter.messagebox.askyesno(title='Hi', message='你好！'))     # return 'True', 'False'
    print(tkinter.messagebox.askokcancel(title='Hi', message='你好！'))  # return 'True', 'False'
</code></pre>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入
import tkinter.messagebox  # 要使用messagebox先要导入模块

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第5步，定义触发函数功能
def hit_me():
    tkinter.messagebox.showinfo(title='Hi', message='你好！')  # 提示信息对话窗
    tkinter.messagebox.showwarning(title='Hi', message='有警告！')       # 提出警告对话窗
    tkinter.messagebox.showerror(title='Hi', message='出错了！')         # 提出错误对话窗
    print(tkinter.messagebox.askquestion(title='Hi', message='你好！'))  # 询问选择对话窗return 'yes', 'no'
    print(tkinter.messagebox.askyesno(title='Hi', message='你好！'))     # return 'True', 'False'
    print(tkinter.messagebox.askokcancel(title='Hi', message='你好！'))  # return 'True', 'False'

# 第4步，在图形界面上创建一个标签用以显示内容并放置
tk.Button(window, text='hit me', bg='green', font=('Arial', 14), command=hit_me).pack()

# 第6步，主窗口循环显示
window.mainloop()
</code></pre>
<ul>
<li><a href="https://blog.csdn.net/low5252/article/details/106366257?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-8&amp;spm=1001.2101.3001.4242">参考文章</a></li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Python GUI之tkinter窗口视窗]]></title>
            <link>https://ryanuo.cc/zh/posts/tkinter</link>
            <guid>https://ryanuo.cc/zh/posts/tkinter</guid>
            <pubDate>Sat, 10 Apr 2021 10:42:32 GMT</pubDate>
            <description><![CDATA[Tkinter 是使用 python 进行窗口视窗设计的模块。Tkinter模块("Tk 接口")是Python的标准Tk GUI工具包的接口。作为 python 特定的GUI界面，是一个图像的窗口，tkinter是python 自带的，可以编辑的GUI界面，我们可以用GUI 实现很多直观的功能，比如想开发一个计算器，如果只是一个程序输入，输出窗口的话，是没用用户体验的。所有开发一个图像化的小窗口，就是必要的。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>tkinter是什么</h2>
<ol>
<li>Tkinter 是使用 python 进行窗口视窗设计的模块。Tkinter模块(&quot;Tk 接口&quot;)是Python的标准Tk GUI工具包的接口。作为 python 特定的GUI界面，是一个图像的窗口，tkinter是python 自带的，可以编辑的GUI界面，我们可以用GUI 实现很多直观的功能，比如想开发一个计算器，如果只是一个程序输入，输出窗口的话，是没用用户体验的。所有开发一个图像化的小窗口，就是必要的。</li>
</ol>
<h2>Label标签</h2>
<ol>
<li>创建好主窗口才能在上面放置各种控件元素</li>
<li><code>tk.Lable(window,text=&quot;文本内容&quot;，bg='背景颜色',font=('字体family',字体大小)),width=标签宽度,height=标签高度 以字符的大小为单位</code></li>
<li><code>window.title('窗口名称')</code></li>
<li>window.geometry('窗口的大小')</li>
<li>放置标签x.pack()或者x.place()</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上设定标签
l = tk.Label(window, text='你好！this is Tkinter', bg='green', font=('Arial', 12), width=30, height=2)
# 说明： bg为背景，font为字体，width为长，height为高，这里的长和高是字符的长和高，比如height=2,就是标签有2个字符这么高

# 第5步，放置标签
l.pack()    # Label内容content区域放置位置，自动调节尺寸
# 放置lable的方法有：1）l.pack(); 2)l.place();

# 第6步，主窗口循环显示
window.mainloop()
# 注意，loop因为是循环的意思，window.mainloop就会让window不断的刷新，如果没有mainloop,就是一个静态的window,传入进去的值就不会有循环，mainloop就相当于一个很大的while循环，有个while，每点击一次就会更新一次，所以我们必须要有循环
# 所有的窗口文件都必须有类似的mainloop函数，mainloop是窗口文件的关键的关键。
</code></pre>
<h2>Button窗口部件</h2>
<ol>
<li>定义一个函数功能（内容自己自由编写），供点击Button按键时调用，调用命令参数command=函数名</li>
</ol>
<pre><code class="language-python">
import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x
# 第4步，在图形界面上设定标签
# 说明： bg为背景，font为字体，width为长，height为高，这里的长和高是字符的长和高，比如height=2,就是标签有2个字符这么高
var = tk.StringVar()  # 将label标签的内容设置为字符类型，用var来接收hit_me函数的传出内容用以显示在标签上
l = tk.Label(window, textvariable=var, bg='green', fg='white', font=('Arial', 12), width=30, height=2)
# 说明： bg为背景，fg为字体颜色，font为字体，width为长，height为高，这里的长和高是字符的长和高，比如height=2,就是标签有2个字符这么高
l.pack()
# 定义一个函数功能（内容自己自由编写），供点击Button按键时调用，调用命令参数command=函数名
on_hit = False
def hit_me():
    global on_hit
    if on_hit == False:
        on_hit = True
        var.set('you hit me')
    else:
        on_hit = False
        var.set('')
# 第5步，在窗口界面设置放置Button按键
b = tk.Button(window, text='hit me', font=('Arial', 12), width=10, height=1, command=hit_me)
# 第5步，放置标签
# x.pack()  # Label内容content区域放置位置，自动调节尺寸
b.pack()
# 放置lable的方法有：1）l.pack(); 2)l.place();

# 第6步，主窗口循环显示
window.mainloop()
# 注意，loop因为是循环的意思，window.mainloop就会让window不断的刷新，如果没有mainloop,就是一个静态的window,传入进去的值就不会有循环，mainloop就相当于一个很大的while循环，有个while，每点击一次就会更新一次，所以我们必须要有循环
# 所有的窗口文件都必须有类似的mainloop函数，mainloop是窗口文件的关键的关键。
</code></pre>
<h2>Entry窗口部件</h2>
<ol>
<li>Entry是tkinter类中提供的的一个单行文本输入域，用来输入显示一行文本，收集键盘输入(类似 HTML 中的 text)</li>
</ol>
<pre><code class="language-python">e1 = tk.Entry(window, show='*', font=('Arial', 14))   # 显示成密文形式
e2 = tk.Entry(window, show=None, font=('Arial', 14))  # 显示成明文形式
e2.pack()
</code></pre>
<h2>Text窗口部件</h2>
<ol>
<li>Text是tkinter类中提供的的一个多行文本区域，显示多行文本，可用来收集(或显示)用户输入的文字(类似 HTML 中的 textarea)，格式化文本显示，允许你用不同的样式和属性来显示和编辑文本，同时支持内嵌图象和窗口。</li>
<li>insert 在焦点处插入 ，end 在文本末尾插入数据</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上设定输入框控件entry框并放置
e = tk.Entry(window, show=None)  # 显示成明文形式
e.pack()

# 第5步，定义两个触发事件时的函数insert_point和insert_end（注意：因为Python的执行顺序是从上往下，所以函数一定要放在按钮的上面）
def insert_point():  # 在鼠标焦点处插入输入内容
    var = e.get()
    t.insert('insert', var)

def insert_end():  # 在文本框内容最后接着插入输入内容
    var = e.get()
    t.insert('end', var)

# 第6步，创建并放置两个按钮分别触发两种情况
b1 = tk.Button(window, text='insert point', width=10,
               height=2, command=insert_point)
b1.pack()
b2 = tk.Button(window, text='insert end', width=10,
               height=2, command=insert_end)
b2.pack()

# 第7步，创建并放置一个多行文本框text用以显示，指定height=3为文本框是三个字符高度
t = tk.Text(window, height=3)
t.pack()

# 第8步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Listbox窗口部件</h2>
<ol>
<li>Text是tkinter类中提供的的列表框部件，显示供选方案的一个列表。listbox能够被配置来得到radiobutton或checklist的行为。</li>
<li>lb.get(lb.curselection()) 获取列表选中文本的值</li>
<li>可以使用insert将值遍历到listbox中</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签label用以显示并放置
var1 = tk.StringVar()  # 创建变量，用var1用来接收鼠标点击具体选项的内容
l = tk.Label(window, bg='green', fg='yellow', font=('Arial', 12), width=10, textvariable=var1)
l.pack()

# 第6步，创建一个方法用于按钮的点击事件
def print_selection():
    value = lb.get(lb.curselection())  # 获取当前选中的文本
    var1.set(value)  # 为label设置值

# 第5步，创建一个按钮并放置，点击按钮调用print_selection函数
b1 = tk.Button(window, text='print selection', width=15, height=2, command=print_selection)
b1.pack()

# 第7步，创建Listbox并为其添加内容
var2 = tk.StringVar()
var2.set((1, 2, 3, 4))  # 为变量var2设置值
# 创建Listbox
lb = tk.Listbox(window, listvariable=var2)  # 将var2的值赋给Listbox
# 创建一个list并将值循环添加到Listbox控件中
list_items = [11, 22, 33, 44]
for item in list_items:
    lb.insert('end', item)  # 从最后一个位置开始加入值
lb.insert(1, 'first')  # 在第一个位置加入'first'字符
lb.insert(2, 'second')  # 在第二个位置加入'second'字符
lb.delete(2)  # 删除第二个位置的字符
lb.pack()

# 第8步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Radiobutton窗口部件</h2>
<ol>
<li>代表一个变量，它可以有多个值中的一个。点击它将为这个变量设置值，并且清除与这同一变量相关的其它radiobutton。类似于单选</li>
<li>tk.Radiobutton(window, text='Option A', variable=var, value='A', command=print_selection)</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签label用以显示并放置
var = tk.StringVar()    # 定义一个var用来将radiobutton的值和Label的值联系在一起.
l = tk.Label(window, bg='yellow', width=20, text='empty')
l.pack()

# 第6步，定义选项触发函数功能
def print_selection():
    l.config(text='you have selected ' + var.get())

# 第5步，创建三个radiobutton选项，其中variable=var, value='A'的意思就是，当我们鼠标选中了其中一个选项，把value的值A放到变量var中，然后赋值给variable
r1 = tk.Radiobutton(window, text='Option A', variable=var, value='A', command=print_selection)
r1.pack()
r2 = tk.Radiobutton(window, text='Option B', variable=var, value='B', command=print_selection)
r2.pack()
r3 = tk.Radiobutton(window, text='Option C', variable=var, value='C', command=print_selection)
r3.pack()

# 第7步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Checkbutton窗口部件</h2>
<ol>
<li>代表一个变量，它有两个不同的值。点击这个按钮将会在这两个值间切换，选择和取消选择。</li>
<li><code>l.config</code> 将值显示在标签上</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签label用以显示并放置
l = tk.Label(window, bg='yellow', width=20, text='empty')
l.pack()

# 第6步，定义触发函数功能
def print_selection():
    if (var1.get() == 1) &amp; (var2.get() == 0):  # 如果选中第一个选项，未选中第二个选项
        l.config(text='I love only Python ')
    elif (var1.get() == 0) &amp; (var2.get() == 1):  # 如果选中第二个选项，未选中第一个选项
        l.config(text='I love only C++')
    elif (var1.get() == 0) &amp; (var2.get() == 0):  # 如果两个选项都未选中
        l.config(text='I do not love either')
    else:
        l.config(text='I love both')  # 如果两个选项都选中

# 第5步，定义两个Checkbutton选项并放置
var1 = tk.IntVar()  # 定义var1和var2整型变量用来存放选择行为返回值
var2 = tk.IntVar()
c1 = tk.Checkbutton(window, text='Python', variable=var1, onvalue=1, offvalue=0,
                    command=print_selection)  # 传值原理类似于radiobutton部件
c1.pack()
c2 = tk.Checkbutton(window, text='C++', variable=var2, onvalue=1, offvalue=0, command=print_selection)
c2.pack()

# 第7步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Scale窗口部件</h2>
<ol>
<li>尺度（拉动条），允许你通过滑块来设置一数字值。</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签label用以显示并放置
l = tk.Label(window, bg='green', fg='white', width=20, text='empty')
l.pack()

# 第6步，定义一个触发函数功能
def print_selection(v):
    l.config(text='you have selected ' + v)

# 第5步，创建一个尺度滑条，长度200字符，从0开始10结束，以2为刻度，精度为0.01，触发调用print_selection函数
s = tk.Scale(window, label='try me', from_=0, to=10, orient=tk.HORIZONTAL, length=200, showvalue=0,tickinterval=2, resolution=0.01, command=print_selection)
s.pack()

# 第7步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Canvas窗口部件</h2>
<ol>
<li>画布，提供绘图功能(直线、椭圆、多边形、矩形) 可以包含图形或位图，用来绘制图表和图，创建图形编辑器，实现定制窗口部件。</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建 500 * 200 大小的画布并放置各种元素
canvas = tk.Canvas(window, bg='green', height=200, width=500)
# 说明图片位置，并导入图片到画布上
image_file = tk.PhotoImage(file='pic.gif')  # 图片位置（相对路径，与.py文件同一文件夹下，也可以用绝对路径，需要给定图片具体绝对路径）
image = canvas.create_image(250, 0, anchor='n',image=image_file)        # 图片锚定点（n图片顶端的中间点位置）放在画布（250,0）坐标处
# 定义多边形参数，然后在画布上画出指定图形
x0, y0, x1, y1 = 100, 100, 150, 150
line = canvas.create_line(x0-50, y0-50, x1-50, y1-50)                   # 画直线
oval = canvas.create_oval(x0+120, y0+50, x1+120, y1+50, fill='yellow')  # 画圆 用黄色填充
arc = canvas.create_arc(x0, y0+50, x1, y1+50, start=0, extent=180)      # 画扇形 从0度打开收到180度结束
rect = canvas.create_rectangle(330, 30, 330+20, 30+20)                  # 画矩形正方形
canvas.pack()

# 第6步，触发函数，用来一定指定图形
def moveit():
    canvas.move(rect, 2, 2) # 移动正方形rect（也可以改成其他图形名字用以移动一起图形、元素），按每次（x=2, y=2）步长进行移动

# 第5步，定义一个按钮用来移动指定图形的在画布上的位置
b = tk.Button(window, text='move item',command=moveit).pack()

# 第7步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Menu窗口部件</h2>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签用以显示内容并放置
l = tk.Label(window, text='      ', bg='green')
l.pack()

# 第10步，定义一个函数功能，用来代表菜单选项的功能，这里为了操作简单，定义的功能比较简单
counter = 0

def do_job():
    global counter
    l.config(text='do ' + str(counter))
    counter += 1

# 第5步，创建一个菜单栏，这里我们可以把他理解成一个容器，在窗口的上方
menubar = tk.Menu(window)

# 第6步，创建一个File菜单项（默认不下拉，下拉内容包括New，Open，Save，Exit功能项）
filemenu = tk.Menu(menubar, tearoff=0)
# 将上面定义的空菜单命名为File，放在菜单栏中，就是装入那个容器中
menubar.add_cascade(label='File', menu=filemenu)

# 在File中加入New、Open、Save等小菜单，即我们平时看到的下拉菜单，每一个小菜单对应命令操作。
filemenu.add_command(label='New', command=do_job)
filemenu.add_command(label='Open', command=do_job)
filemenu.add_command(label='Save', command=do_job)
filemenu.add_separator()  # 添加一条分隔线
filemenu.add_command(label='Exit', command=window.quit)  # 用tkinter里面自带的quit()函数

# 第7步，创建一个Edit菜单项（默认不下拉，下拉内容包括Cut，Copy，Paste功能项）
editmenu = tk.Menu(menubar, tearoff=0)
# 将上面定义的空菜单命名为 Edit，放在菜单栏中，就是装入那个容器中
menubar.add_cascade(label='Edit', menu=editmenu)

# 同样的在 Edit 中加入Cut、Copy、Paste等小命令功能单元，如果点击这些单元, 就会触发do_job的功能
editmenu.add_command(label='Cut', command=do_job)
editmenu.add_command(label='Copy', command=do_job)
editmenu.add_command(label='Paste', command=do_job)

# 第8步，创建第二级菜单，即菜单项里面的菜单
submenu = tk.Menu(filemenu)  # 和上面定义菜单一样，不过此处实在File上创建一个空的菜单
filemenu.add_cascade(label='Import', menu=submenu, underline=0)  # 给放入的菜单submenu命名为Import

# 第9步，创建第三级菜单命令，即菜单项里面的菜单项里面的菜单命令（有点拗口，笑~~~）
submenu.add_command(label='Submenu_1', command=do_job)  # 这里和上面创建原理也一样，在Import菜单项中加入一个小菜单命令Submenu_1

# 第11步，创建菜单栏完成后，配置让菜单栏menubar显示出来
window.config(menu=menubar)

# 第12步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>Frame 窗口部件</h2>
<ol>
<li>框架，用来承载放置其他GUI元素，就是一个容器，是一个在 Windows 上分离小区域的部件, 它能将 Windows 分成不同的区,然后存放不同的其他部件. 同时一个 Frame 上也能再分成两个 Frame, Frame 可以认为是一种容器.</li>
</ol>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第4步，在图形界面上创建一个标签用以显示内容并放置
tk.Label(window, text='on the window', bg='red', font=('Arial', 16)).pack()   # 和前面部件分开创建和放置不同，其实可以创建和放置一步完成

# 第5步，创建一个主frame，长在主window窗口上
frame = tk.Frame(window)
frame.pack()

# 第6步，创建第二层框架frame，长在主框架frame上面
frame_l = tk.Frame(frame)# 第二层frame，左frame，长在主frame上
frame_r = tk.Frame(frame)# 第二层frame，右frame，长在主frame上
frame_l.pack(side='left')
frame_r.pack(side='right')

# 第7步，创建三组标签，为第二层frame上面的内容，分为左区域和右区域，用不同颜色标识
tk.Label(frame_l, text='on the frame_l1', bg='green').pack()
tk.Label(frame_l, text='on the frame_l2', bg='green').pack()
tk.Label(frame_l, text='on the frame_l3', bg='green').pack()
tk.Label(frame_r, text='on the frame_r1', bg='yellow').pack()
tk.Label(frame_r, text='on the frame_r2', bg='yellow').pack()
tk.Label(frame_r, text='on the frame_r3', bg='yellow').pack()

# 第8步，主窗口循环显示
window.mainloop()
</code></pre>
<h2>messageBox窗口部件</h2>
<ol>
<li>类似于弹窗的操作</li>
</ol>
<pre><code class="language-python"> tkinter.messagebox.showinfo(title='Hi', message='你好！')  # 提示信息对话窗
    tkinter.messagebox.showwarning(title='Hi', message='有警告！')       # 提出警告对话窗
    tkinter.messagebox.showerror(title='Hi', message='出错了！')         # 提出错误对话窗
    print(tkinter.messagebox.askquestion(title='Hi', message='你好！'))  # 询问选择对话窗return 'yes', 'no'
    print(tkinter.messagebox.askyesno(title='Hi', message='你好！'))     # return 'True', 'False'
    print(tkinter.messagebox.askokcancel(title='Hi', message='你好！'))  # return 'True', 'False'
</code></pre>
<pre><code class="language-python">#!/usr/bin/env python
# -*- coding: utf-8 -*-

import tkinter as tk  # 使用Tkinter前需要先导入
import tkinter.messagebox  # 要使用messagebox先要导入模块

# 第1步，实例化object，建立窗口window
window = tk.Tk()

# 第2步，给窗口的可视化起名字
window.title('My Window')

# 第3步，设定窗口的大小(长 * 宽)
window.geometry('500x300')  # 这里的乘是小x

# 第5步，定义触发函数功能
def hit_me():
    tkinter.messagebox.showinfo(title='Hi', message='你好！')  # 提示信息对话窗
    tkinter.messagebox.showwarning(title='Hi', message='有警告！')       # 提出警告对话窗
    tkinter.messagebox.showerror(title='Hi', message='出错了！')         # 提出错误对话窗
    print(tkinter.messagebox.askquestion(title='Hi', message='你好！'))  # 询问选择对话窗return 'yes', 'no'
    print(tkinter.messagebox.askyesno(title='Hi', message='你好！'))     # return 'True', 'False'
    print(tkinter.messagebox.askokcancel(title='Hi', message='你好！'))  # return 'True', 'False'

# 第4步，在图形界面上创建一个标签用以显示内容并放置
tk.Button(window, text='hit me', bg='green', font=('Arial', 14), command=hit_me).pack()

# 第6步，主窗口循环显示
window.mainloop()
</code></pre>
<ul>
<li><a href="https://blog.csdn.net/low5252/article/details/106366257?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-8&amp;spm=1001.2101.3001.4242">参考文章</a></li>
</ul>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[My Beloved Car-A Journey of Acquisition]]></title>
            <link>https://ryanuo.cc/posts/buy-car</link>
            <guid>https://ryanuo.cc/posts/buy-car</guid>
            <pubDate>Thu, 01 Apr 2021 20:00:00 GMT</pubDate>
            <description><![CDATA[My Beloved Car-A Journey of Acquisition]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h3>为什么买车？</h3>
<p><code>便利出行</code>：拥有一辆车意味着你可以随时随地自主出行，不受公共交通的限制，节约了时间和精力。<br>
<code>家庭聚会</code>：车子可以让你更轻松地与家人一起外出，去探索新的地方、享受自然风光或者参加各种活动，增进家庭关系。<br>
<code>提升生活质量</code>：拥有一辆心仪的车型可以为你的生活增添色彩，让你在驾驶时感受到愉悦和满足，为日常生活增添乐趣。<br>
<code>自由探索</code>：有了车子，你可以更方便地探索城市的每一个角落，发现更多美好的事物，拓展自己的视野和经历。</p>
<p><code>主要的原因还是家里真的需要一辆车了，刚好工作一年多了，就决定买车了。</code></p>
<h3>选择之初</h3>
<p><code>预算决策</code>：我在15万元以下的预算范围内选择了<a href="https://preface.geely.com/preface">星瑞</a>自动车型。价格合适，而且外观符合我的品味，这是我喜欢的。</p>
<p><code>品牌倾向</code>：我打算支持国产车，因为它们的性价比很高，而且这个品牌最近也备受关注，颇受欢迎。</p>
<p><code>配置选择</code>：我选了<code>2.0T</code>，动力强劲，配置也符合我的需求，包括动力性能和舒适性等方面。</p>
<p><code>油车偏好</code>：我决定选择传统油车而不是电动车，主要是因为我认为15万元以下的电动车性能一般，而且我有<a href="https://baike.baidu.com/item/%E7%94%B5%E9%87%8F%E7%84%A6%E8%99%91%E7%97%87/12507167">电池焦虑症</a>，掉电一点就想冲。</p>
<h3>车型对比</h3>
<p>因为先选中了星瑞，所以我在同价位下选择了多个车型进行对比，以下是我选择的车型和配置的对比。</p>
<table>
<thead>
<tr>
<th>车型</th>
<th>外观和内饰</th>
<th>智能系统</th>
<th>动力性能</th>
<th>价格范围</th>
</tr>
</thead>
<tbody>
<tr>
<td>吉利星瑞</td>
<td>外观成熟</td>
<td>配备了一些</td>
<td>2.0T发动机，动力强劲 95号汽油</td>
<td>12-15w</td>
</tr>
<tr>
<td>艾瑞泽8</td>
<td>-</td>
<td>-</td>
<td>搭载2.0T发动机，动力强劲，可使用92号汽油</td>
<td>12-14w</td>
</tr>
<tr>
<td>速腾</td>
<td>-</td>
<td>-</td>
<td>1.5T</td>
<td>12-17w</td>
</tr>
<tr>
<td>领克03</td>
<td>-</td>
<td>表现出色</td>
<td>1.5T，2.0T，动力强劲</td>
<td>13-42w</td>
</tr>
<tr>
<td>长安UNI-V</td>
<td>-</td>
<td>-</td>
<td>1.5T 2.0T</td>
<td>13-14w</td>
</tr>
</tbody>
</table>
<p><span style="color:gray;font-size:12px;">注：以上对比为个人观点，实际情况可能有所不同。</span></p>
<h3>试驾体验</h3>
<p>试驾了星瑞，感觉还是比较满意的。<br>
首次试驾的是星瑞只此青绿（石青），后来本打算试驾星瑞青云2.0T，但经销店没有试驾车，而且暂时新款也不支持试驾，问了销售说库存很紧张。于是，我又试驾了一次星瑞尊贵版，想要多练练、多感受一下。</p>
<p>试驾两次后的感受是完全不同，第一次不是很敢踩油门，所以第一次试驾的感觉比较不顺手，第二次试驾的感受就比较顺手了，而且第二次试驾试驾员带我体验了下弹射起步和百公里120迅速刹停，还有车在高速的情况下猛打方向盘，该说不说这个车的操控是真的爽。</p>
<!-- ![](/demos/car-shijia.png) -->
<h3>谈价购车</h3>
<!-- ![](/demos/car-price.jpg) -->
<h3>落地成交</h3>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[我的爱车购车经历]]></title>
            <link>https://ryanuo.cc/zh/posts/buy-car</link>
            <guid>https://ryanuo.cc/zh/posts/buy-car</guid>
            <pubDate>Thu, 01 Apr 2021 20:00:00 GMT</pubDate>
            <description><![CDATA[我的爱车购车经历]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h3>为什么买车？</h3>
<p><code>便利出行</code>：拥有一辆车意味着你可以随时随地自主出行，不受公共交通的限制，节约了时间和精力。<br>
<code>家庭聚会</code>：车子可以让你更轻松地与家人一起外出，去探索新的地方、享受自然风光或者参加各种活动，增进家庭关系。<br>
<code>提升生活质量</code>：拥有一辆心仪的车型可以为你的生活增添色彩，让你在驾驶时感受到愉悦和满足，为日常生活增添乐趣。<br>
<code>自由探索</code>：有了车子，你可以更方便地探索城市的每一个角落，发现更多美好的事物，拓展自己的视野和经历。</p>
<p><code>主要的原因还是家里真的需要一辆车了，刚好工作一年多了，就决定买车了。</code></p>
<h3>选择之初</h3>
<p><code>预算决策</code>：我在15万元以下的预算范围内选择了<a href="https://preface.geely.com/preface">星瑞</a>自动车型。价格合适，而且外观符合我的品味，这是我喜欢的。</p>
<p><code>品牌倾向</code>：我打算支持国产车，因为它们的性价比很高，而且这个品牌最近也备受关注，颇受欢迎。</p>
<p><code>配置选择</code>：我选了<code>2.0T</code>，动力强劲，配置也符合我的需求，包括动力性能和舒适性等方面。</p>
<p><code>油车偏好</code>：我决定选择传统油车而不是电动车，主要是因为我认为15万元以下的电动车性能一般，而且我有<a href="https://baike.baidu.com/item/%E7%94%B5%E9%87%8F%E7%84%A6%E8%99%91%E7%97%87/12507167">电池焦虑症</a>，掉电一点就想冲。</p>
<h3>车型对比</h3>
<p>因为先选中了星瑞，所以我在同价位下选择了多个车型进行对比，以下是我选择的车型和配置的对比。</p>
<table>
<thead>
<tr>
<th>车型</th>
<th>外观和内饰</th>
<th>智能系统</th>
<th>动力性能</th>
<th>价格范围</th>
</tr>
</thead>
<tbody>
<tr>
<td>吉利星瑞</td>
<td>外观成熟</td>
<td>配备了一些</td>
<td>2.0T发动机，动力强劲 95号汽油</td>
<td>12-15w</td>
</tr>
<tr>
<td>艾瑞泽8</td>
<td>-</td>
<td>-</td>
<td>搭载2.0T发动机，动力强劲，可使用92号汽油</td>
<td>12-14w</td>
</tr>
<tr>
<td>速腾</td>
<td>-</td>
<td>-</td>
<td>1.5T</td>
<td>12-17w</td>
</tr>
<tr>
<td>领克03</td>
<td>-</td>
<td>表现出色</td>
<td>1.5T，2.0T，动力强劲</td>
<td>13-42w</td>
</tr>
<tr>
<td>长安UNI-V</td>
<td>-</td>
<td>-</td>
<td>1.5T 2.0T</td>
<td>13-14w</td>
</tr>
</tbody>
</table>
<p><span style="color:gray;font-size:12px;">注：以上对比为个人观点，实际情况可能有所不同。</span></p>
<h3>试驾体验</h3>
<p>试驾了星瑞，感觉还是比较满意的。<br>
首次试驾的是星瑞只此青绿（石青），后来本打算试驾星瑞青云2.0T，但经销店没有试驾车，而且暂时新款也不支持试驾，问了销售说库存很紧张。于是，我又试驾了一次星瑞尊贵版，想要多练练、多感受一下。</p>
<p>试驾两次后的感受是完全不同，第一次不是很敢踩油门，所以第一次试驾的感觉比较不顺手，第二次试驾的感受就比较顺手了，而且第二次试驾试驾员带我体验了下弹射起步和百公里120迅速刹停，还有车在高速的情况下猛打方向盘，该说不说这个车的操控是真的爽。</p>
<!-- ![](/demos/car-shijia.png) -->
<h3>谈价购车</h3>
<!-- ![](/demos/car-price.jpg) -->
<h3>落地成交</h3>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[New Starting Point in 2021]]></title>
            <link>https://ryanuo.cc/posts/2021</link>
            <guid>https://ryanuo.cc/posts/2021</guid>
            <pubDate>Fri, 01 Jan 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[New Beginning in 2021 | by RYANUO]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<div class="font5">
<p>This article deeply resonates.</p>
<p>Start planning your life early, strive harder, complain less, and remember opportunities are earned through personal effort. Keep pushing forward!</p>
</div>
<h2>On Anxiety</h2>
<p>We feel anxious because the current version of ourselves is quite distant from our envisioned self. The growing gap between who we are and who we aspire to be largely stems from how we continually let ourselves down.</p>
<p>Regarding anxiety, many of us have fallen into a paradoxical loop where we seem busy but are actually riddled with anxiety. We spend nights awake, but merely scrolling through our phones, giving countless likes; we impulsively decide to learn something new, buy numerous vocabulary books that remain unopened thereafter; we get motivated to work out after being inspired, gather loads of workout plans but never execute them; we vow to read extensively, purchase stacks of books yet never crack one open. We invest significant time on social media, bookmarking what we think is useful, only for our hard drives to fill up with resources we never review. We hoard information but fail to truly engage with what we've carefully selected. We focus on collecting, forgetting that the most crucial step is to take the time to digest and absorb.</p>
<p>A book unread is just printed pages; a vocabulary book not studied is merely a collection of alphabetical combinations; downloaded lectures left unheard are simply worthless data—things you might have saved mindlessly, never revisiting again. Eventually, you find yourself overwhelmed with an unmanageable backlog. As you stare at piles of lectures and untouched textbooks, anxiety intensifies. Procrastination and waiting are the最容易 killers of motivation.</p>
<p>The best way to conquer anxiety? Face it head-on by doing the very things that make you anxious.</p>
<p>Action requires no perfect timing or state of mind; the present moment is eternal and all-encompassing.</p>
<h2>On Dreams and Struggle</h2>
<p>I firmly believe that heroes are always solitary, while minions cluster together. Pareto's Principle holds true universally: 20% of people hold 80% of the wealth, while 80% serve the remaining 20%.</p>
<p>Being outstanding isn't enough; becoming indispensable is key. If excellence is insufficient, strive to become irreplaceable. There are two paths to irreplaceability: doing tasks others are unwilling to do, or performing common tasks so exceptionally well that no one else can match you. Such individuals are genuinely needed in society. This journey is long and fiercely competitive, yet I will continue to strengthen myself, improving bit by bit each day, venturing into fields others avoid and mastering areas where everyone competes, to become truly irreplaceable.</p>
<p>Someone is indeed living the life you desire, and you too can live that life if you possess a little determination, courage, hope, and belief.</p>
<p>Each episode in our lives serves its purpose; there are no meaningless experiences. Therefore, refrain from lamenting why you didn't start earlier or regretting what you could have done differently. The time spent complaining or regretting would be better invested in moving forward. Sometimes, taking a wrong turn clarifies the future path; befriending the wrong people reveals the meaning of true friendship amidst hardship; loving the wrong person teaches us the essence of genuine love.</p>
<h2>On Money</h2>
<p>Have you considered that excessive frugality can actually be wasteful? When you spend hours arguing over cents instead of using that time to study, you're wasting valuable resources. Eating leftover food that's gone bad may lead to hospital bills that exceed the savings. Skipping health checks due to cost may result in larger medical expenses once serious illnesses are diagnosed. Thinking you're saving money by doing these things, you're unknowingly squandering your most precious asset—time—endlessly debating trivial matters.</p>
<h2>On Relationships</h2>
<p>The law of attraction dictates that like attracts like; you attract people similar to yourself.</p>
<p>In terms of dating, finding someone who helps you grow is important; finding someone who inspires mutual growth is even more critical.</p>
<p>As for colleagues, has anyone ever said they must become friends? True friends have no overlapping interests or benefits; they are those willing to act for one another, even sacrificing some of their own interests. The best approach is to maintain good relations with every colleague without confiding in or forming deep friendships with any.</p>
<h2>On Love</h2>
<p>True affection involves two people who can converse heartily; the ideal couple engages in soulful dialogue and warms each other's hearts.</p>
<p>Many relationships end due to misunderstandings that stem from unclear communication. Men may assume women don't want to see them, while women fear seeing each other too often may cool off the relationship. Both parties wish to sustain the bond, yet they misunderstand each other, leaving words unsaid or assuming certain things unnecessary to clarify, causing love to fade away. Misunderstandings deepen until conflicts surface. Remember, before marriage, people choose among options; post-marriage, they make judgments.</p>
<p>Countless romances crumble due to poor communication. Cherishing young love entails keeping lines of communication open, sharing your thoughts and feelings openly to ensure your partner feels cherished and valued.</p>
<p>If a past relationship made you a better person, then you did not love the wrong person. Life is inherently lonely, but we're fortunate when temporary companions walk alongside us during our solitude. While parents may accompany you for a considerable part of your adult life, to navigate life effectively, you must sometimes walk alone, explore unfamiliar places, meet new faces, endure hardships you've never experienced before. All these challenges aren't meant to break you but to fortify you, enabling you to better support those you cherish.</p>
<h2>On Family</h2>
<p>Temporary separations from loved ones are necessary precursors to joyful reunions.</p>
<p>Each individual navigates the world independently and with a sense of loneliness. Parents won't be with us forever, but gratefully, Chinese parents often stay involved in our lives for a longer period. However, to thrive in the world, relying solely on others' support isn't enough; you need to venture forth alone at times, enduring solitude to strengthen your wings, ultimately to protect and cherish those you don't want to leave behind.</p>
<h2>On Reading and Learning</h2>
<p>There's no fixed rhythm for studying; what suits you personally is the best method. Sometimes, getting caught up in the methodology prevents you from taking the first step. Many people spend their lives deliberating over that initial stride, never actually making it.</p>
<p>During study sessions, ask yourself what you aim to achieve and where you want to reach today. Without a goal, learning becomes aimless wandering; without a target, hard work appears diligent but lacks substance.</p>
<p>(1) When reading a book, ask yourself what you want to gain from it and proceed with questions in mind.</p>
<p>(2) If you're one-third into a book and still unsure about its content, it's time to put it aside.</p>
<p>(3) Don't take more than a week to finish a book, as extended periods can lead to forgetfulness and ineffective reading.</p>
<p>(4) After finishing a good book, consider reading it a second time for a more profound understanding.</p>
<h2>Pursue Your Dreams Fearlessly</h2>
<p>Ultimately, only you can determine your destiny. You are the master of your life and youth. Thank you for staying true to yourself.</p>
<p>The real experts are those who &quot;march to their own drum,&quot; not escaping the world, but rather knowing its rules while skillfully operating outside the system. They don't flatter nor do they allow the world to change them. They undertake tasks they'd rather not do, solely to create better opportunities for pursuing their passions later on. Like nomads, they wander, yet they understand societal norms.</p>
<p>To embody such individuals, I believe you should cultivate several traits: acquire a specialized skill set to earn a living anywhere; avoid politics, as it's complex; and above all, have clarity about the lifestyle you desire and steadfastly pursue it.</p>
<p>Regarding dreams, I don't subscribe to the notion that hard work guarantees success since the world can be unpredictable. Nevertheless, this doesn't warrant resignation; people must resiliently strive towards their aspirations. Even if the world sometimes hinders a dedicated individual, holding onto your original dream signifies that you haven't aged in spirit.</p>
<ul>
<li>Source: Jianshu</li>
<li>Author: <a href="https://www.jianshu.com/p/aaa96f279f8f">Qianqian Whisper</a></li>
</ul>
<h2>Choice</h2>
<h2>Planning</h2>
<h2>Effort</h2>
<h2>Future</h2>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[2021年-新的起点]]></title>
            <link>https://ryanuo.cc/zh/posts/2021</link>
            <guid>https://ryanuo.cc/zh/posts/2021</guid>
            <pubDate>Fri, 01 Jan 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[2021年新的起点 - Ryan C.o]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<div class="tip key font5 fas fa-key faa-horizontal animated"><p>看完这篇文章深有感触。</p><p>尽早的做自己的人生规划，多些努力，少些怨天尤人，机会都是靠自己争取的，加油吧！！！</p></div>
<h2>关于焦虑</h2>
<p>我们之所以会觉得焦虑，无非是因为现在的自己和想象中的自己，很有距离。”而我们离想象中的自己越来越远，很大程度上都是因为我们在一点点地辜负自己。</p>
<p>关于焦虑，我发现我们或多或少地都陷入了一个怪圈，这个怪圈叫：我们看似忙碌，实则焦虑。看起来每天熬夜，却只是拿着手机点了无数个赞；我们总是心血来潮想学习，于是买了很多单词书，再也没有翻开过；我们总是备受刺激想健身，于是找了很多攻略，再也没有动过；我们总是信誓旦旦要读书，于是买了很多书，再也没有打开过。 我们总是花很多时间在社交网络上，把认为有用的东西另存为，直到你的硬盘存满了资料，你还是没有看过。 我们忙碌，可我们却没有真的去了解那些自己精挑细选留下的内容。我们花时间收集，却忘了最重要的其实是花时间去消化。</p>
<p>一本书买了不看也不过是印着字的纸而已；单词书买了不背充其量就是26个字母的排列组合；下载的演讲公开课不听也只是一堆无用的影像，可能你只是随手下载了，就再也没有去看过。于是有一天你发现，堆积的东西已经看不完了。 你看着一个个公开课、一本本单词书，无从下手从而越发焦虑。拖延和等待，是世界上最容易压垮一个人斗志的东西。</p>
<p>那么怎么打败焦虑？打败焦虑的最好办法，就是去做那些让你焦虑的事情。</p>
<p>行动这件事，从来不需要等到什么好天气什么好状态，此时此刻就是永远，此时此刻就是一切。</p>
<h2>关于梦想和奋斗</h2>
<p>我一直坚信，英雄，永远是孤独的，只有小喽啰才扎堆。“二八定律”永远适合在地球的每一个角落：百分之二十的人，占有百分之八十的资产；百分之八十的人，为百分之二十的人服务。</p>
<p>做到优秀还不够，一定要卓越，一定要无可替代才是最重要的。既然优秀不够，就让自己无可替代吧。而无可替代的方式有两种：一是做别人不愿意做的事情；二是把别人都能做好的事情做卓越。这样的人，才是这个社会真正需要的。这条路很远，有很多人都在竞争，而我还会继续让自己强大起来，每天进步一点儿，做别人不愿意做的领域，把别人都能做的领域做绝，才能无可替代。</p>
<p>这世上，真的有人过着你想要的生活，这生活，你也能过，你只需要一点点决心、一点点勇敢、一点点希望和一点点的相信。</p>
<p>没有什么路是白走的，没有什么事情是白做的，这些看似无意义的事情，都是成长的基石。在我们的生命中的每个插曲都有着自己该有的意义，所以不要抱怨为什么不早点儿做，更不要后悔要是做了什么就好了。你有后悔、抱怨的时间，不如整装待发前进起来。有时候只有走错了路，才逐渐明白自己要的未来；只有交错了朋友，才逐渐知道什么是患难见真情；只有爱错过人，才逐渐懂得真爱是什么。</p>
<h2>关于金钱</h2>
<p>你是否想过，一些过于节省的生活，其实就是浪费：你每天都在争论那几角钱，浪费了精力和时间去读书学习；把隔夜的剩菜热了吃，吃坏肚子去医院花更多钱；你省钱不去体检，检查出大病后不得不住院花费更多。你以为你在省钱，却不知道，真正省钱的方式，是去赚钱，是让自己变得更强大，而不是为了几角钱花自己最宝贵的时间去永无休止地争论。</p>
<h2>关于人际</h2>
<p>吸引力法则，你是什么样的人，就会吸引什么样的人。</p>
<p>关于恋爱，找一个能让你进步的人，很重要；找一个能让彼此进步的人，更重要。</p>
<p>关于同事，谁说过能成为朋友的？真正的朋友，是没有利益交集，是能为对方去做一些事的，是能为对方放弃一些利益的人。最好的办法，就是不得罪每个同事，也更别和任何同事掏心地做朋友。</p>
<h2>关于爱情</h2>
<p>真正的感情，是要两个人能聊到一起的；最好的情侣，是能用灵魂平等地交流，是能用心去温暖彼此的两个人。</p>
<p>很多分手的原因，都是互相没有把话讲明白。男的以为女的不愿意见到自己；而女的怕见得频繁感情降温了。双方都想维持住这段感情，却互相都不了解，话没有讲明白，甚至有些话觉得没必要讲明白，感情也就逐渐淡了。误解越深，矛盾也就浮出水面。要知道，结婚前，所有人都在做选择题；结婚后，所有人都在做判断题。</p>
<p>多少感情，不是败给了现实，而是败给了交流。</p>
<p>珍惜年轻时恋人的最好方式其实就是交流，就是把心打开，让对方看到，让他明白，这颗心，依然留着对方的爱。</p>
<p>生命中任何走来的人，只要她曾经让自己变得更优秀，你就没有爱错人。人生本来就孤独，任何陪过自己走完一段寂寞时光的人，都应该用心感激。</p>
<p>感谢那些擦肩而过的人，因为时光证明了他们不是对的人，更重要的是，上天会为你安排一个更适合的，就在不久的将来。</p>
<h2>关于亲情</h2>
<p>离开亲人的分别，是为了更好地团聚。</p>
<p>一个人行走在这个世界上，都是孤独的。父母不会陪你一辈子，幸福的是，中国的父母，会在你成年之后依旧陪伴你很长一段时间。可是，想要在这个世界更好地走，光让人搀扶着你是不够的，你总要一个人孤单地走完一些路，去一些陌生的地方，见一些没有见过的人，吃一些从未吃过的苦。这一切，不是你自己作死，只是为了让自己的羽翼丰满，能更好地陪伴那些不想离开的人。</p>
<h2>关于读书和学习</h2>
<p>所谓学习方法，其实没有什么固定的节奏，适合自己的，才是最好的；有效果的，才是最好的。有时候由于太纠结如何走，竟然连第一步都没有迈出去。这世界上，有多少人，为了第一步能走得潇洒霸气，却一辈子没有迈出第一步。</p>
<p>在每次学习的时候，你是否问过自己，今天要达到什么目标，要走到哪里。没有目的的旅行只能叫漫游，没有目标的学习只是叫看起来很努力。形式什么的，根本没必要纠结，适合自己就好。</p>
<p>读书只能增加自己的见识和文化积淀，是不能让你暴富的，但至少能让你拥有一个体面的生活和一份得体的收入。</p>
<p>（1）在看一本书的时候，问问自己，你想要从中得到什么，然后带着问题去翻阅目录和文字。</p>
<p>（2）如果一本书读了三分之一了还不知道在讲什么，放手吧。</p>
<p>（3）一本书不要超过一周读完，否则太长时间容易读了前面忘记后面，无效阅读。</p>
<p>（4）读完一本书后，如果觉得是好作品，第二遍才是真正的阅读。</p>
<h2>勇敢去追求梦想</h2>
<p>这世上，除了自己，没有人能决定你的命运。你才是自己生活和青春的主宰者。感谢你，勇敢地做了自己。</p>
<p>其实真正的高手是“特立独行的猪”，并不是去逃避这个世界；是知道这个世界的规则，但有能力游离于这个体系之外，有智慧地区别自己想要的和不得不做的。他们不阿谀奉承，更不愿意被世界改变；他们会做自己不愿意做的，只是为了以后更好地去做自己愿意做的；他们像浪迹天涯的游子一样，但也知道这个社会的规则。</p>
<p>我的总结是，做这样的人，你需要有这样几个特征：一是拥有一技之长，让自己无论去哪儿都能赚到钱；二是不去搞政治，这玩意太复杂；三是明白自己想要的生活，坚定地去追。</p>
<p>关于梦想，我一直不相信努力就能成功的鬼话，因为这个世界很诡异，时势才造英雄。有时候一个很努力的人，毁掉他的，是这个社会。但是，不代表我们就应该自暴自弃，人总要坚强地为自己的理想奋斗过，只要你还记得当年的梦想，那么你就没有变老。</p>
<ul>
<li>来源：简书</li>
<li>作者：<a href="https://www.jianshu.com/p/aaa96f279f8f">芊芊细语123</a></li>
</ul>
<h2>选择</h2>
<h2>计划</h2>
<h2>努力</h2>
<h2>未来</h2>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[GIT Study Notes]]></title>
            <link>https://ryanuo.cc/posts/git</link>
            <guid>https://ryanuo.cc/posts/git</guid>
            <pubDate>Fri, 18 Dec 2020 14:00:00 GMT</pubDate>
            <description><![CDATA[a git version control learning guide that covers basic instructions such as configuration cloning branching merging tagging and remote repository operations]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<p>Git是一个开源的分布式版本控制系统，可以有效、高速地处理从很小到非常大的项目版本管理Git 是 <a href="https://zh.wikipedia.org/wiki/%E6%9E%97%E7%BA%B3%E6%96%AF%C2%B7%E6%89%98%E7%93%A6%E5%85%B9">Linus Torvalds</a> 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。</p>
<p><span class="inline-tag red">master：默认开发分支</span> <span class="inline-tag green ">orgin：默认远程版本分支 </span> <span class="inline-tag grey"> head：指向当前分支</span></p>
<h2>git配置</h2>
<ul>
<li>设置用户名</li>
</ul>
<pre><code class="language-bash">git config --global user.name &quot;username&quot; //用户名为登录用户名
</code></pre>
<ul>
<li>设置邮箱</li>
</ul>
<pre><code class="language-bash">git config --global user.email &quot;注册时的邮箱&quot;
</code></pre>
<h2>代码指令</h2>
<ul>
<li>克隆github上的仓库源码到本地</li>
</ul>
<pre><code class="language-bash">git clone [url]
</code></pre>
<h2>git仓库的创建</h2>
<p>git初始化</p>
<pre><code class="language-bash">git init
</code></pre>
<p>查看文件是否变更</p>
<pre><code class="language-bash">git status
</code></pre>
<p>添加文件到缓存区</p>
<pre><code class="language-bash">git add .   // 或者git add filename
git rm . //删除缓存文件
</code></pre>
<p><code>注</code>在指定文件夹使用git指令</p>
<p>添加备注</p>
<pre><code class="language-bash">git commit -m &quot;这里写备注内容&quot;
</code></pre>
<p>推送文件到远程仓库master</p>
<pre><code class="language-bash">git push origin master
</code></pre>
<p>查看历史记录</p>
<pre><code class="language-bash">git log
</code></pre>
<p>回溯操作</p>
<pre><code class="language-bash">git reset --head 目标版本号
</code></pre>
<p>查看文件具体更改内容</p>
<pre><code class="language-bash">git diff
</code></pre>
<ul>
<li>可查看修改了哪些内容</li>
</ul>
<h2>分支操作</h2>
<p>查看分支</p>
<pre><code class="language-bash">git branch
</code></pre>
<p>创建分支</p>
<pre><code class="language-bash">git branch 分支名
</code></pre>
<p>删除分支</p>
<pre><code class="language-bash">git branch -d 分支名
</code></pre>
<p>切换分支</p>
<pre><code class="language-bash">git checkout 分支名
</code></pre>
<p>合并分支</p>
<pre><code class="language-bash">git merge 分支名
</code></pre>
<h2>远程仓库</h2>
<p>解除远程仓库链接</p>
<pre><code class="language-bash">git remote rm origin
</code></pre>
<p>链接到远程仓库</p>
<pre><code class="language-bash">git remote add origin [url]
</code></pre>
<p>拉取远程内容</p>
<pre><code class="language-bash">git fetch //或者
git pull
</code></pre>
<p>查看远程仓库与本地仓库关系</p>
<pre><code class="language-bash">git remote show origin
</code></pre>
<h2>标签指令</h2>
<p>查看标签</p>
<pre><code class="language-bash">git tag
</code></pre>
<p>添加标签</p>
<pre><code class="language-bash">git tag [name]
</code></pre>
<p>添加标签tag，比如v1.0等<br>
删除本地标签</p>
<pre><code class="language-bash">git tag -d &lt;tagname&gt;
</code></pre>
<p>删除远程仓库标签</p>
<pre><code class="language-bash">git push origin :refs/tags/&lt;tagname&gt;
</code></pre>
<p>上传单个tag</p>
<pre><code class="language-bash">git push origin [tagname]
</code></pre>
<ul>
<li>上传所有标签</li>
</ul>
<pre><code class="language-bash">git push origin --tags

</code></pre>
<h2>参考</h2>
<p><a href="https://github.com/521xueweihan/git-tips">Git的奇技淫巧</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Git 学习笔记]]></title>
            <link>https://ryanuo.cc/zh/posts/git</link>
            <guid>https://ryanuo.cc/zh/posts/git</guid>
            <pubDate>Fri, 18 Dec 2020 14:00:00 GMT</pubDate>
            <description><![CDATA[Git版本控制学习指南，涵盖配置、克隆、分支、合并、标签、远程仓库操作等基础指令。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<p>Git是一个开源的分布式版本控制系统，可以有效、高速地处理从很小到非常大的项目版本管理Git 是 <a href="https://zh.wikipedia.org/wiki/%E6%9E%97%E7%BA%B3%E6%96%AF%C2%B7%E6%89%98%E7%93%A6%E5%85%B9">Linus Torvalds</a> 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。</p>
<p><span class="inline-tag red">master：默认开发分支</span> <span class="inline-tag green ">orgin：默认远程版本分支 </span> <span class="inline-tag grey"> head：指向当前分支</span></p>
<h2>git配置</h2>
<ul>
<li>设置用户名</li>
</ul>
<pre><code class="language-bash">git config --global user.name &quot;username&quot; //用户名为登录用户名
</code></pre>
<ul>
<li>设置邮箱</li>
</ul>
<pre><code class="language-bash">git config --global user.email &quot;注册时的邮箱&quot;
</code></pre>
<h2>代码指令</h2>
<ul>
<li>克隆github上的仓库源码到本地</li>
</ul>
<pre><code class="language-bash">git clone [url]
</code></pre>
<h2>git仓库的创建</h2>
<p>git初始化</p>
<pre><code class="language-bash">git init
</code></pre>
<p>查看文件是否变更</p>
<pre><code class="language-bash">git status
</code></pre>
<p>添加文件到缓存区</p>
<pre><code class="language-bash">git add .   // 或者git add filename
git rm . //删除缓存文件
</code></pre>
<p><code>注</code>在指定文件夹使用git指令</p>
<p>添加备注</p>
<pre><code class="language-bash">git commit -m &quot;这里写备注内容&quot;
</code></pre>
<p>推送文件到远程仓库master</p>
<pre><code class="language-bash">git push origin master
</code></pre>
<p>查看历史记录</p>
<pre><code class="language-bash">git log
</code></pre>
<p>回溯操作</p>
<pre><code class="language-bash">git reset --head 目标版本号
</code></pre>
<p>查看文件具体更改内容</p>
<pre><code class="language-bash">git diff
</code></pre>
<ul>
<li>可查看修改了哪些内容</li>
</ul>
<h2>分支操作</h2>
<p>查看分支</p>
<pre><code class="language-bash">git branch
</code></pre>
<p>创建分支</p>
<pre><code class="language-bash">git branch 分支名
</code></pre>
<p>删除分支</p>
<pre><code class="language-bash">git branch -d 分支名
</code></pre>
<p>切换分支</p>
<pre><code class="language-bash">git checkout 分支名
</code></pre>
<p>合并分支</p>
<pre><code class="language-bash">git merge 分支名
</code></pre>
<h2>远程仓库</h2>
<p>解除远程仓库链接</p>
<pre><code class="language-bash">git remote rm origin
</code></pre>
<p>链接到远程仓库</p>
<pre><code class="language-bash">git remote add origin [url]
</code></pre>
<p>拉取远程内容</p>
<pre><code class="language-bash">git fetch //或者
git pull
</code></pre>
<p>查看远程仓库与本地仓库关系</p>
<pre><code class="language-bash">git remote show origin
</code></pre>
<h2>标签指令</h2>
<p>查看标签</p>
<pre><code class="language-bash">git tag
</code></pre>
<p>添加标签</p>
<pre><code class="language-bash">git tag [name]
</code></pre>
<p>添加标签tag，比如v1.0等<br>
删除本地标签</p>
<pre><code class="language-bash">git tag -d &lt;tagname&gt;
</code></pre>
<p>删除远程仓库标签</p>
<pre><code class="language-bash">git push origin :refs/tags/&lt;tagname&gt;
</code></pre>
<p>上传单个tag</p>
<pre><code class="language-bash">git push origin [tagname]
</code></pre>
<ul>
<li>上传所有标签</li>
</ul>
<pre><code class="language-bash">git push origin --tags

</code></pre>
<h2>参考</h2>
<p><a href="https://github.com/521xueweihan/git-tips">Git的奇技淫巧</a></p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Deepin Related (Linux)]]></title>
            <link>https://ryanuo.cc/posts/deepin</link>
            <guid>https://ryanuo.cc/posts/deepin</guid>
            <pubDate>Sat, 10 Oct 2020 20:01:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>How to Install</h2>
<h3>Preparation</h3>
<p>Prepare a USB drive to create a Deepin boot disk. Before installing the system with the Deepin image from the official website, check if there is free disk space on the computer, typically requiring about 25 GB. If not, partition the disk.</p>
<p>Partitioning Method/Steps</p>
<p>1.Right-click on &quot;This PC&quot; and select &quot;Manage&quot;.<br>
2.Click on &quot;Disk Management&quot;.<br>
3.Right-click on the disk you want to partition and select &quot;Shrink Volume&quot;.<br>
4.Click &quot;Shrink&quot; (shrink by approximately 102400MB).<br>
5.The available space should show as 100 GB, indicating that the preparation is complete.<br>
Click &quot;Finish&quot;.</p>
<p>For instructions on creating a bootable USB drive, visit the official <a href="https://www.deepin.org/en/download/">Deepin website</a>.</p>
<p>How to Install? <a href="https://www.deepin.org/en/installation/">Click here</a></p>
<h2>Troubleshooting After Installation</h2>
<h3>No Sound</h3>
<p>1.Check if the computer can detect the input and output of the sound card.</p>
<p>Method: Settings &gt; Sound &gt; Advanced Settings - Check for available output and input devices.</p>
<p>2.Verify if the sound card is physically damaged, causing the system to be unable to read physical information. Use the following commands to check for similar feedback related to the sound card:</p>
<pre><code class="language-bash">sudo lspci | grep audio
sudo lspci -v
</code></pre>
<p>3.Modify the grub file to add an instruction:</p>
<pre><code class="language-bash">GRUB_CMDLINE_LINUX_DEFAULT=&quot;${your_existing_configuration_here} snd_hda_intel.dmic_detect=0&quot;
</code></pre>
<p>Instruction:</p>
<pre><code class="language-bash"># Enter root mode
su
cd /etc/default/
vi grub
# Add the code snd_hda_intel.dmic_detect=0 to the grub file
# 1. Press &quot;i&quot; on the keyboard to enter insert mode, modify and add the code
# 2. Press &quot;esc&quot; and then type &quot;:wq!&quot; to save the changes
reboot # Restart the system
</code></pre>
<h3>Wi-Fi Issue</h3>
<p>Problem with the network card driver, unable to connect to Wi-Fi<br>
Delete the file iwlwifi.conf (path: /etc/modprobe.d/iwlwifi.conf)</p>
<pre><code class="language-bash"># First, enter root mode in sequence
su
cd /etc/modprobe.d/
rm -f iwlwifi.conf # Forcefully delete the file
ls # Check if the file is deleted
reboot # Reboot the system
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Deepin 相关（Liunx）]]></title>
            <link>https://ryanuo.cc/zh/posts/deepin</link>
            <guid>https://ryanuo.cc/zh/posts/deepin</guid>
            <pubDate>Sat, 10 Oct 2020 20:01:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>如何安装</h2>
<h3>准备工作</h3>
<p>准备一个u盘，用来制作deepin启动盘，在deepin官网安装镜像文件并制作u盘安装系统前，检查电脑是否有空闲的硬盘空间，大概需要25g ，没有要进行分盘操作</p>
<p>分盘方法/步骤</p>
<p>1.右键点击此电脑，点击管理。2.点击磁盘管理。3.鼠标右键点击你要进行分区的磁盘，点击压缩卷。4.点击压缩（压缩大概102400MB）。5.显示可用空间为100g即可，准备工作就完成了<br>
点击完成即可。<br>
制作U盘操作看官网<a href="https://www.deepin.org/zh/download/">deepin官网</a></p>
<p>如何安装？<a href="https://www.deepin.org/zh/installation/">点击</a></p>
<h2>安装后遇到问题</h2>
<h3>无声音</h3>
<p>1.检查电脑是否能够检测到声卡的输入输出</p>
<p>方法：设置里面-声音-高级设置-看是否有输出输入设备</p>
<p>2.确认声卡是否出现物理损坏导致的物理信息无法被系统读取。代码指令如下,(看是否有类似的声卡反馈)</p>
<pre><code class="language-bash">sudo lspci | grep audio
sudo lspci -v
</code></pre>
<p>3.修改grub文件里面的文件 添加一个指令即可</p>
<pre><code class="language-bash">GRUB_CMDLINE_LINUX_DEFAULT=&quot;${这里你的原有配置不要动} snd_hda_intel.dmic_detect=0&quot;
</code></pre>
<p>指令如下：</p>
<pre><code class="language-bash">#进入root模式
su
cd /etc/default/
vi grub
# 将此代码snd_hda_intel.dmic_detect=0 加入grub即可
# 1.按键盘i 进入插入模式，修改添加代码后
#2.【esc】然后 按【：】输入 【w！】
reboot #重启即可
</code></pre>
<h3>wifi 异常</h3>
<p>1.网卡驱动的问题，无法链接wifi</p>
<p>需要删除 <strong>iwlwifi.conf</strong> 此文件（路径：/etc/modprobe.d/iwlwifi.conf）</p>
<pre><code class="language-bash">#首先进入root模式,按顺序即可
su
cd /etc/modrobe.d/
rm -f iwlwifi.conf #强制删除文件
ls #检查文件是否删除
reboot #重启系统
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Shortcuts Commonly Used By Vscode]]></title>
            <link>https://ryanuo.cc/posts/vscode</link>
            <guid>https://ryanuo.cc/posts/vscode</guid>
            <pubDate>Fri, 18 Sep 2020 22:57:21 GMT</pubDate>
            <description><![CDATA[common shortcut keys for vs code include comment move line copy line delete line and display terminal at the same time some commonly used plug ins are recommended such as auto rename tag auto close tag beautiful etc]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>vs code 的常用快捷键</h2>
<ol>
<li>注释：</li>
</ol>
<ul>
<li>
<p>单行注释：[ctrl+k,ctrl+c] 或 ctrl+/</p>
</li>
<li>
<p>取消单行注释：[ctrl+k,ctrl+u] (按下ctrl不放，再按k + u)</p>
</li>
<li>
<p>多行注释：[alt+shift+A]</p>
</li>
<li>
<p>多行注释：/**</p>
</li>
</ul>
<ol start="2">
<li>
<p>移动行：alt+up/down</p>
</li>
<li>
<p>显示/隐藏左侧目录栏 ctrl + b</p>
</li>
<li>
<p>复制当前行：shift + alt +up/down</p>
</li>
<li>
<p>删除当前行：shift + ctrl + k</p>
</li>
<li>
<p>控制台终端显示与隐藏：ctrl + ~</p>
</li>
<li>
<p>查找文件/安装vs code 插件地址：ctrl + p</p>
</li>
<li>
<p>代码格式化：shift + alt +f</p>
</li>
<li>
<p>新建一个窗口 : ctrl + shift + n</p>
</li>
<li>
<p>行增加缩进: ctrl + [</p>
</li>
<li>
<p>行减少缩进: ctrl + ]</p>
</li>
<li>
<p>裁剪尾随空格(去掉一行的末尾那些没用的空格) : ctrl + shift + x</p>
</li>
<li>
<p>字体放大/缩小: ctrl + ( + 或 - )</p>
</li>
<li>
<p>拆分编辑器 : ctrl + 1/2/3</p>
</li>
<li>
<p>切换窗口 : ctrl + shift + left/right</p>
</li>
<li>
<p>关闭编辑器窗口 : ctrl + w</p>
</li>
<li>
<p>关闭所有窗口 : ctrl + k + w</p>
</li>
<li>
<p>切换全屏 : F11</p>
</li>
<li>
<p>自动换行 : alt + z</p>
</li>
<li>
<p>显示git : ctrl + shift + g</p>
</li>
<li>
<p>全局查找文件：ctrl + shift + f</p>
</li>
<li>
<p>显示相关插件的命令(如：git log)：ctrl + shift + p</p>
</li>
<li>
<p>选中文字：shift + left / right / up / down</p>
</li>
<li>
<p>折叠代码： ctrl + k + 0-9 (0是完全折叠)</p>
</li>
<li>
<p>展开代码： ctrl + k + j (完全展开代码)</p>
</li>
<li>
<p>删除行 ： ctrl + shift + k</p>
</li>
<li>
<p>快速切换主题：ctrl + k / ctrl + t</p>
</li>
<li>
<p>快速回到顶部 ： ctrl + home</p>
</li>
<li>
<p>快速回到底部 : ctrl + end</p>
</li>
<li>
<p>格式化选定代码 ：ctrl + k / ctrl +f</p>
</li>
<li>
<p>选中代码 ： shift + 鼠标左键</p>
</li>
<li>
<p>多行同时添加内容（光标） ：ctrl + alt + up/down</p>
</li>
<li>
<p>全局替换：ctrl + shift + h</p>
</li>
<li>
<p>当前文件替换：ctrl + h</p>
</li>
<li>
<p>打开最近打开的文件：ctrl + r</p>
</li>
<li>
<p>打开新的命令窗：ctrl + shift + c</p>
</li>
</ol>
<h2>vs code 的常用插件</h2>
<ol>
<li>
<p>Auto Rename Tag 修改html标签，自动帮你完成尾部闭合标签的同步修改，和webstorm一样。</p>
</li>
<li>
<p>Auto Close Tag 自动闭合HTML标签</p>
</li>
<li>
<p>Beautiful 格式化代码的工具</p>
</li>
<li>
<p>Dash Dash是MacOS的API文档浏览器和代码段管理器</p>
</li>
<li>
<p>Ejs Snippets ejs 代码提示</p>
</li>
<li>
<p>ESLint 检查javascript语法错误与提示</p>
</li>
<li>
<p>File Navigator 快速查找文件</p>
</li>
<li>
<p>Git History(git log) 查看git log</p>
</li>
<li>
<p>Gulp Snippets 写gulp时用到，gulp语法提示。</p>
</li>
<li>
<p>HTML CSS Support 在HTML标签上写class智能提示当前项目所支持的样式</p>
</li>
<li>
<p>HTML Snippets 超级好用且初级的H5代码片段以及提示</p>
</li>
<li>
<p>Debug for Chrome 让vs code映射chrome的debug功能，静态页面都可以用vscode来打断点调试.配饰稍微复杂一点</p>
</li>
<li>
<p>Document this Js的注释模板</p>
</li>
<li>
<p>jQuery Code Snippets jquery提示工具</p>
</li>
<li>
<p>Html2jade html模板转pug模板</p>
</li>
<li>
<p>JS-CSS-HTML Formatter 格式化</p>
</li>
<li>
<p>Npm intellisense require 时的包提示工具</p>
</li>
<li>
<p>Open in browser 打开默认浏览器</p>
</li>
<li>
<p>One Dark Theme 一个vs code的主题</p>
</li>
<li>
<p>Path Intellisense 自动路径补全.默认不带这个功能</p>
</li>
<li>
<p>Project Manager 多个项目之间快速切换的工具</p>
</li>
<li>
<p>Pug(Jade) snippets pug语法提示</p>
</li>
<li>
<p>React Components 根据文件名创建反应组件代码。</p>
</li>
<li>
<p>React Native Tools reactNative工具类为React Native项目提供了开发环境。</p>
</li>
<li>
<p>Stylelint css/sass代码审查</p>
</li>
<li>
<p>Typings auto installer 安装vscode 的代码提示依赖库，基于typtings的</p>
</li>
<li>
<p>View In Browser 默认浏览器查看HTML文件（快捷键Ctrl+F1可以修改）</p>
</li>
<li>
<p>Vscode-icons 让vscode资源目录加上图标.必备</p>
</li>
<li>
<p>VueHelper Vue2代码段（包括Vue2 api.vue-router2.vuex2）</p>
</li>
<li>
<p>Vue 2 Snippets vue必备vue代码提示</p>
</li>
<li>
<p>Vue-color vue语法高亮主题</p>
</li>
<li>
<p>Auto-Open Markdown Preview markdown文件自动开启预览</p>
</li>
<li>
<p>EverMonkey 印象笔记</p>
</li>
<li>
<p>atom one dark atom的一个高亮主题(个人推荐)</p>
</li>
</ol>
<h2>常用的电脑快捷键</h2>
<ol>
<li>
<p>ctrl + shift + delete 快速清除浏览器缓存</p>
</li>
<li>
<p>ctrl + alt + delete 快速进入任务管理器页面</p>
</li>
<li>
<p>window + L 快速锁定电脑</p>
</li>
<li>
<p>window + d 所有窗口最小化</p>
</li>
<li>
<p>window + e 打开我的资源管理器(我的电脑)</p>
</li>
<li>
<p>window + f 快速打开搜索窗口</p>
</li>
<li>
<p>alt + tab 快速查看打开的应用与窗口</p>
</li>
</ol>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Vscode常用的快捷键]]></title>
            <link>https://ryanuo.cc/zh/posts/vscode</link>
            <guid>https://ryanuo.cc/zh/posts/vscode</guid>
            <pubDate>Fri, 18 Sep 2020 22:57:21 GMT</pubDate>
            <description><![CDATA[VS Code常用快捷键包括注释、移动行、复制行、删除行、终端显示等。同时推荐了一些常用的插件，如Auto Rename Tag、Auto Close Tag、Beautiful等。]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<h2>vs code 的常用快捷键</h2>
<ol>
<li>注释：</li>
</ol>
<ul>
<li>
<p>单行注释：[ctrl+k,ctrl+c] 或 ctrl+/</p>
</li>
<li>
<p>取消单行注释：[ctrl+k,ctrl+u] (按下ctrl不放，再按k + u)</p>
</li>
<li>
<p>多行注释：[alt+shift+A]</p>
</li>
<li>
<p>多行注释：/**</p>
</li>
</ul>
<ol start="2">
<li>
<p>移动行：alt+up/down</p>
</li>
<li>
<p>显示/隐藏左侧目录栏 ctrl + b</p>
</li>
<li>
<p>复制当前行：shift + alt +up/down</p>
</li>
<li>
<p>删除当前行：shift + ctrl + k</p>
</li>
<li>
<p>控制台终端显示与隐藏：ctrl + ~</p>
</li>
<li>
<p>查找文件/安装vs code 插件地址：ctrl + p</p>
</li>
<li>
<p>代码格式化：shift + alt +f</p>
</li>
<li>
<p>新建一个窗口 : ctrl + shift + n</p>
</li>
<li>
<p>行增加缩进: ctrl + [</p>
</li>
<li>
<p>行减少缩进: ctrl + ]</p>
</li>
<li>
<p>裁剪尾随空格(去掉一行的末尾那些没用的空格) : ctrl + shift + x</p>
</li>
<li>
<p>字体放大/缩小: ctrl + ( + 或 - )</p>
</li>
<li>
<p>拆分编辑器 : ctrl + 1/2/3</p>
</li>
<li>
<p>切换窗口 : ctrl + shift + left/right</p>
</li>
<li>
<p>关闭编辑器窗口 : ctrl + w</p>
</li>
<li>
<p>关闭所有窗口 : ctrl + k + w</p>
</li>
<li>
<p>切换全屏 : F11</p>
</li>
<li>
<p>自动换行 : alt + z</p>
</li>
<li>
<p>显示git : ctrl + shift + g</p>
</li>
<li>
<p>全局查找文件：ctrl + shift + f</p>
</li>
<li>
<p>显示相关插件的命令(如：git log)：ctrl + shift + p</p>
</li>
<li>
<p>选中文字：shift + left / right / up / down</p>
</li>
<li>
<p>折叠代码： ctrl + k + 0-9 (0是完全折叠)</p>
</li>
<li>
<p>展开代码： ctrl + k + j (完全展开代码)</p>
</li>
<li>
<p>删除行 ： ctrl + shift + k</p>
</li>
<li>
<p>快速切换主题：ctrl + k / ctrl + t</p>
</li>
<li>
<p>快速回到顶部 ： ctrl + home</p>
</li>
<li>
<p>快速回到底部 : ctrl + end</p>
</li>
<li>
<p>格式化选定代码 ：ctrl + k / ctrl +f</p>
</li>
<li>
<p>选中代码 ： shift + 鼠标左键</p>
</li>
<li>
<p>多行同时添加内容（光标） ：ctrl + alt + up/down</p>
</li>
<li>
<p>全局替换：ctrl + shift + h</p>
</li>
<li>
<p>当前文件替换：ctrl + h</p>
</li>
<li>
<p>打开最近打开的文件：ctrl + r</p>
</li>
<li>
<p>打开新的命令窗：ctrl + shift + c</p>
</li>
</ol>
<h2>vs code 的常用插件</h2>
<ol>
<li>
<p>Auto Rename Tag 修改html标签，自动帮你完成尾部闭合标签的同步修改，和webstorm一样。</p>
</li>
<li>
<p>Auto Close Tag 自动闭合HTML标签</p>
</li>
<li>
<p>Beautiful 格式化代码的工具</p>
</li>
<li>
<p>Dash Dash是MacOS的API文档浏览器和代码段管理器</p>
</li>
<li>
<p>Ejs Snippets ejs 代码提示</p>
</li>
<li>
<p>ESLint 检查javascript语法错误与提示</p>
</li>
<li>
<p>File Navigator 快速查找文件</p>
</li>
<li>
<p>Git History(git log) 查看git log</p>
</li>
<li>
<p>Gulp Snippets 写gulp时用到，gulp语法提示。</p>
</li>
<li>
<p>HTML CSS Support 在HTML标签上写class智能提示当前项目所支持的样式</p>
</li>
<li>
<p>HTML Snippets 超级好用且初级的H5代码片段以及提示</p>
</li>
<li>
<p>Debug for Chrome 让vs code映射chrome的debug功能，静态页面都可以用vscode来打断点调试.配饰稍微复杂一点</p>
</li>
<li>
<p>Document this Js的注释模板</p>
</li>
<li>
<p>jQuery Code Snippets jquery提示工具</p>
</li>
<li>
<p>Html2jade html模板转pug模板</p>
</li>
<li>
<p>JS-CSS-HTML Formatter 格式化</p>
</li>
<li>
<p>Npm intellisense require 时的包提示工具</p>
</li>
<li>
<p>Open in browser 打开默认浏览器</p>
</li>
<li>
<p>One Dark Theme 一个vs code的主题</p>
</li>
<li>
<p>Path Intellisense 自动路径补全.默认不带这个功能</p>
</li>
<li>
<p>Project Manager 多个项目之间快速切换的工具</p>
</li>
<li>
<p>Pug(Jade) snippets pug语法提示</p>
</li>
<li>
<p>React Components 根据文件名创建反应组件代码。</p>
</li>
<li>
<p>React Native Tools reactNative工具类为React Native项目提供了开发环境。</p>
</li>
<li>
<p>Stylelint css/sass代码审查</p>
</li>
<li>
<p>Typings auto installer 安装vscode 的代码提示依赖库，基于typtings的</p>
</li>
<li>
<p>View In Browser 默认浏览器查看HTML文件（快捷键Ctrl+F1可以修改）</p>
</li>
<li>
<p>Vscode-icons 让vscode资源目录加上图标.必备</p>
</li>
<li>
<p>VueHelper Vue2代码段（包括Vue2 api.vue-router2.vuex2）</p>
</li>
<li>
<p>Vue 2 Snippets vue必备vue代码提示</p>
</li>
<li>
<p>Vue-color vue语法高亮主题</p>
</li>
<li>
<p>Auto-Open Markdown Preview markdown文件自动开启预览</p>
</li>
<li>
<p>EverMonkey 印象笔记</p>
</li>
<li>
<p>atom one dark atom的一个高亮主题(个人推荐)</p>
</li>
</ol>
<h2>常用的电脑快捷键</h2>
<ol>
<li>
<p>ctrl + shift + delete 快速清除浏览器缓存</p>
</li>
<li>
<p>ctrl + alt + delete 快速进入任务管理器页面</p>
</li>
<li>
<p>window + L 快速锁定电脑</p>
</li>
<li>
<p>window + d 所有窗口最小化</p>
</li>
<li>
<p>window + e 打开我的资源管理器(我的电脑)</p>
</li>
<li>
<p>window + f 快速打开搜索窗口</p>
</li>
<li>
<p>alt + tab 快速查看打开的应用与窗口</p>
</li>
</ol>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[Travel diary]]></title>
            <link>https://ryanuo.cc/posts/train</link>
            <guid>https://ryanuo.cc/posts/train</guid>
            <pubDate>Tue, 19 May 2020 08:00:00 GMT</pubDate>
            <description><![CDATA[my travel diary]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<GeoMap/>
<h2>2025</h2>
<h3>12.10 - 12.15</h3>
<pre><code class="language-mermaid">flowchart TD
  D1[第一天｜石家庄] --&gt; D1A[河北博物馆&lt;br/&gt;金缕玉衣]
  D1A --&gt; D1B[安徽牛肉板面&lt;br/&gt;熏肉大饼]
  D1B --&gt; D1C[正定夜市&lt;br/&gt;小吃]

  D2[第二天｜正定 → 宿州 → 合肥] --&gt; D2A[正定古城&lt;br/&gt;观感一般]
  D2A --&gt; D2B[下午去宿州&lt;br/&gt;找朋友]
  D2B --&gt; D2C[晚上开车去合肥]
  D2C --&gt; D2D[和朋友吃烧烤]

  D3[第三天｜合肥 → 徐州] --&gt; D3A[合柴1972]
  D3A --&gt; D3B[合肥三件套&lt;br/&gt;地标]
  D3B --&gt; D3C[安徽博物馆&lt;br/&gt;AR体验]
  D3C --&gt; D3D[合肥家常菜]
  D3D --&gt; D3E[去宿州接朋友&lt;br/&gt;前往徐州]
  D3E --&gt; D3F[18夜市]

  D4[第四天｜徐州] --&gt; D4A[富国街]
  D4A --&gt; D4B[宝莲寺]
  D4B --&gt; D4C[云龙山]
  D4C --&gt; D4D[晚上返程]
</code></pre>
<p>这次行程从石家庄开始，第一天参观了河北博物馆，看了金缕玉衣，之后吃了正宗的安徽牛肉板面和熏肉大饼，晚上到正定夜市逛吃；第二天先去了正定古城，但整体感觉一般，下午前往宿州找朋友，晚上直接开车到合肥，和另一位朋友一起吃了烧烤；第三天在合肥游玩，去了合柴1972，打卡了合肥地标“三件套”，下午参观安徽博物馆并体验了 AR 游戏，晚上吃了合肥家常菜后又返回宿州接朋友，一起前往徐州，夜里逛了 18 夜市；第四天在徐州游览了富国街、宝莲寺和云龙山，晚上结束行程返回。</p>
<h3>11.14 - 11.14</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发] --&gt; B[抵达平遥古城]
    B --&gt; C[参观景区]
    C --&gt; D[拍照留念]
    D --&gt; E[品尝山西铜火锅]
    E --&gt; F[自由活动/逛古城]
    F --&gt; G[返程]
</code></pre>
<p>自驾前往平遥古城，抵达后在古城内漫步，参观城墙、日升昌票号等景点，拍摄古城建筑和街景照片。途中品尝山西特色铜火锅，感受当地美食魅力。随后在古城小街自由活动，逛手工艺品店或拍照留念，最后结束游览，自驾返回出发地。</p>
<h3>03.14 - 03.15</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发前往郑州海洋馆] --&gt; B[参观展区]
    B --&gt; C[观看疯狂魔法狮表演 10:00/13:00/15:00]
    C --&gt; D[观看美人鱼水秀表演 9:30/10:30/14:00/15:30]
    D --&gt; E[观看人鲸之恋 10:30/13:30/16:00]
    E --&gt; F[观看海豚魔法传奇 11:00/16:30]
    F --&gt; G[拍照留念]
    G --&gt; H[吃火锅/用餐]
    H --&gt; I[结束行程返回]
</code></pre>
<p>当天前往郑州海洋馆，先参观各类海洋生物展区，体验互动展览。根据表演时间安排观看精彩节目：疯狂魔法狮、美人鱼水秀、人鲸之恋和海豚魔法传奇，并拍摄精彩瞬间留念。游览结束后，在馆内或附近餐馆享用火锅美食，结束愉快的一日海洋馆之行。</p>
<h2>2024</h2>
<h3>12.28 - 12.28</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发前往郑州动物园] --&gt; B[入园/购票]
    B --&gt; C[观赏熊猫、老虎、长颈鹿等动物]
    C --&gt; D[参与喂食或互动体验]
    D --&gt; E[拍照留念]
    E --&gt; F[休息区/休息]
    F --&gt; G[结束游览返回出发地]
</code></pre>
<p>当天前往郑州动物园，沿园区步行游览，观赏熊猫、老虎、长颈鹿、大象等各类动物。途中在园内休息区或附近餐馆小憩，品尝简餐或零食。游览结束后，带着拍摄的照片和美好回忆返回出发地，结束愉快的动物园之行。</p>
<h3>12.22 - 12.22</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发前往郑州植物园] --&gt; B[入园/购票]
    B --&gt; C[漫步花卉区/温室/森林区]
    C --&gt; D[拍照留念]
    D --&gt; E[继续游览其他景区]
    E --&gt; F[结束游览返回出发地]
</code></pre>
<p>当天前往郑州植物园，漫步在花卉区、温室和森林区，欣赏各类植物和花卉景观。拍摄绿植和自然风光留念，感受园内宁静和自然氛围。</p>
<h3>11.24 - 11.25</h3>
<pre><code class="language-mermaid">flowchart TD
  A[公司小组游洛阳] --&gt; B[前往老君山]
  B --&gt; C[遇到雨雪天气]
  C --&gt; D[乘坐缆车上山]
  D --&gt; E[沿山道徒步游览景点]
  E --&gt; F[欣赏山间雪景]
  F --&gt; G[品尝洛阳特色美食&lt;br/&gt;牛肉汤]
</code></pre>
<p>公司小组前往洛阳游览老君山，途中遇到雨雪天气，为了安全大家选择乘坐缆车上山。在缆车上和沿山道徒步游览过程中，大家欣赏到了山间的雪景和各个景点。游览结束后，品尝了洛阳的特色美食——牛肉汤。</p>
<h3>08.02 - 08.02</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发前往只有河南·戏剧幻城] --&gt; B[幻城剧场]
    B --&gt; C[火车站展区]
    C --&gt; D[候车大厅展区]
    D --&gt; E[下沉岁月展区]
    E --&gt; F[天子驾六遗址坑]
    F --&gt; G[第七机车车辆厂礼堂]
    G --&gt; H[团队晚餐：烧烤]
    H --&gt; I[结束行程返回出发地]
</code></pre>
<p>小组团建当天前往 只有河南·戏剧幻城，团队依次参观沉浸式剧场和展区，包括 幻城剧场、火车站、候车大厅、下沉岁月、天子驾六遗址坑、以及 第七机车车辆厂礼堂。在各场景中观看精彩表演、体验互动展览，并拍摄团队合照留念。游览结束后，全体成员在附近享用烧烤晚餐，边用餐边分享当天趣事，增进团队感情。行程结束后返回出发地。</p>
<h3>05.18 - 05.19</h3>
<pre><code class="language-mermaid">graph TB;
    A(第一天) --&gt; B(西安);
    B --&gt; C(到达西安站);
    C --&gt; D(去回民街喝肉丸糊辣汤);
    D --&gt; E(与伙伴汇合);
    E --&gt; F(骑行西安城墙);
    A --&gt; G[参观陕西历史博物馆];
    G --&gt; I[参观大雁塔];
    I --&gt; J[参观大唐不夜城];
    J --&gt; K[去广济街附近吃贾三包子凉皮];
    K --&gt; L[去搓麻将];
    L --&gt; M(结束第一天);

    N(第二天) --&gt; O(去永兴坊);
    O --&gt; P(中午吃\nbiangbiang面、肉夹馍、\n凉皮、米皮、臊子面);
    P --&gt; Q[参观西安世博园];
    Q --&gt; R(去广济街商圈);
    R --&gt; S(吃泡馍);
    S --&gt; T(返程);
</code></pre>
<p>第一天到达西安站后，先去了回民街喝肉丸糊辣汤，随后与伙伴汇合，一起骑行了西安城墙，接着参观了陕西历史博物馆、大雁塔和大唐不夜城，晚上在广济街附近吃了贾三包子和凉皮，之后去搓麻将，结束第一天行程；第二天前往永兴坊，中午吃了 biangbiang 面、肉夹馍、凉皮、米皮和臊子面，下午参观西安世博园，傍晚到广济街商圈吃泡馍，随后返程。</p>
<h2>2023</h2>
<h3>03.24 - 03.25</h3>
<pre><code class="language-mermaid">flowchart TD
  D1[第一天｜夜爬泰山] --&gt; A[开始夜爬]
  A --&gt; B[经过十八盘&lt;br/&gt;最累路段]
  B --&gt; C[傍晚到达南天门]
  C --&gt; D[途中结识一起爬山的朋友]
  D --&gt; E[晚上一起吃饭]
  E --&gt; F[等待清晨日出]
  F --&gt; G[拍摄日出照片]
  G --&gt; H[离开泰山]
  H --&gt; I[泰山脚下夜市吃东西]
  I --&gt; J[各自分别]
</code></pre>
<p>第一天夜爬泰山，途中经过最累的十八盘，傍晚到达南天门，途中结识了几位一起爬山的朋友，晚上大家一起吃了饭，随后等待清晨的日出，并拍下了日出的照片。离开泰山后，大家在泰山脚下的夜市吃东西，最后各自分别。</p>
<h2>2020</h2>
<h3>05.19 - 05.20</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发去绵山] --&gt; B[沿途聊天/欣赏风景]
    B --&gt; C[到达绵山景区]
    C --&gt; D[登山拍照留念]
    D --&gt; E[品尝当地美食/休息]
    E --&gt; F[参观寺庙或文化景点]
    F --&gt; G[下山返回学校/住宿地]
</code></pre>
<p>大学期间与室友乘车前往绵山，沿途聊着校园趣事，气氛轻松愉快。到达绵山后，登山欣赏自然风光，拍摄山景和同伴合照，感受山间清新的空气。途中在山脚或山中小餐馆品尝当地美食，补充体力。参观寺庙或文化景点，感受绵山的历史与文化底蕴。傍晚下山，结束愉快的绵山之旅，返回学校或住宿地，途中回味旅途趣事和拍摄的照片。</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[我的旅行日记]]></title>
            <link>https://ryanuo.cc/zh/posts/train</link>
            <guid>https://ryanuo.cc/zh/posts/train</guid>
            <pubDate>Tue, 19 May 2020 08:00:00 GMT</pubDate>
            <description><![CDATA[my travel diary]]></description>
            <content:encoded><![CDATA[<p>[[toc]]</p>
<GeoMap/>
<h2>2025</h2>
<h3>12.10 - 12.15</h3>
<pre><code class="language-mermaid">flowchart TD
  D1[第一天｜石家庄] --&gt; D1A[河北博物馆&lt;br/&gt;金缕玉衣]
  D1A --&gt; D1B[安徽牛肉板面&lt;br/&gt;熏肉大饼]
  D1B --&gt; D1C[正定夜市&lt;br/&gt;小吃]

  D2[第二天｜正定 → 宿州 → 合肥] --&gt; D2A[正定古城&lt;br/&gt;观感一般]
  D2A --&gt; D2B[下午去宿州&lt;br/&gt;找朋友]
  D2B --&gt; D2C[晚上开车去合肥]
  D2C --&gt; D2D[和朋友吃烧烤]

  D3[第三天｜合肥 → 徐州] --&gt; D3A[合柴1972]
  D3A --&gt; D3B[合肥三件套&lt;br/&gt;地标]
  D3B --&gt; D3C[安徽博物馆&lt;br/&gt;AR体验]
  D3C --&gt; D3D[合肥家常菜]
  D3D --&gt; D3E[去宿州接朋友&lt;br/&gt;前往徐州]
  D3E --&gt; D3F[18夜市]

  D4[第四天｜徐州] --&gt; D4A[富国街]
  D4A --&gt; D4B[宝莲寺]
  D4B --&gt; D4C[云龙山]
  D4C --&gt; D4D[晚上返程]
</code></pre>
<p>这次行程从石家庄开始，第一天参观了河北博物馆，看了金缕玉衣，之后吃了正宗的安徽牛肉板面和熏肉大饼，晚上到正定夜市逛吃；第二天先去了正定古城，但整体感觉一般，下午前往宿州找朋友，晚上直接开车到合肥，和另一位朋友一起吃了烧烤；第三天在合肥游玩，去了合柴1972，打卡了合肥地标“三件套”，下午参观安徽博物馆并体验了 AR 游戏，晚上吃了合肥家常菜后又返回宿州接朋友，一起前往徐州，夜里逛了 18 夜市；第四天在徐州游览了富国街、宝莲寺和云龙山，晚上结束行程返回。</p>
<h3>11.14 - 11.14</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发] --&gt; B[抵达平遥古城]
    B --&gt; C[参观景区]
    C --&gt; D[拍照留念]
    D --&gt; E[品尝山西铜火锅]
    E --&gt; F[自由活动/逛古城]
    F --&gt; G[返程]
</code></pre>
<p>自驾前往平遥古城，抵达后在古城内漫步，参观城墙、日升昌票号等景点，拍摄古城建筑和街景照片。途中品尝山西特色铜火锅，感受当地美食魅力。随后在古城小街自由活动，逛手工艺品店或拍照留念，最后结束游览，自驾返回出发地。</p>
<h3>03.14 - 03.15</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发前往郑州海洋馆] --&gt; B[参观展区]
    B --&gt; C[观看疯狂魔法狮表演 10:00/13:00/15:00]
    C --&gt; D[观看美人鱼水秀表演 9:30/10:30/14:00/15:30]
    D --&gt; E[观看人鲸之恋 10:30/13:30/16:00]
    E --&gt; F[观看海豚魔法传奇 11:00/16:30]
    F --&gt; G[拍照留念]
    G --&gt; H[吃火锅/用餐]
    H --&gt; I[结束行程返回]
</code></pre>
<p>当天前往郑州海洋馆，先参观各类海洋生物展区，体验互动展览。根据表演时间安排观看精彩节目：疯狂魔法狮、美人鱼水秀、人鲸之恋和海豚魔法传奇，并拍摄精彩瞬间留念。游览结束后，在馆内或附近餐馆享用火锅美食，结束愉快的一日海洋馆之行。</p>
<h2>2024</h2>
<h3>12.28 - 12.28</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发前往郑州动物园] --&gt; B[入园/购票]
    B --&gt; C[观赏熊猫、老虎、长颈鹿等动物]
    C --&gt; D[参与喂食或互动体验]
    D --&gt; E[拍照留念]
    E --&gt; F[休息区/休息]
    F --&gt; G[结束游览返回出发地]
</code></pre>
<p>当天前往郑州动物园，沿园区步行游览，观赏熊猫、老虎、长颈鹿、大象等各类动物。途中在园内休息区或附近餐馆小憩，品尝简餐或零食。游览结束后，带着拍摄的照片和美好回忆返回出发地，结束愉快的动物园之行。</p>
<h3>12.22 - 12.22</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发前往郑州植物园] --&gt; B[入园/购票]
    B --&gt; C[漫步花卉区/温室/森林区]
    C --&gt; D[拍照留念]
    D --&gt; E[继续游览其他景区]
    E --&gt; F[结束游览返回出发地]
</code></pre>
<p>当天前往郑州植物园，漫步在花卉区、温室和森林区，欣赏各类植物和花卉景观。拍摄绿植和自然风光留念，感受园内宁静和自然氛围。</p>
<h3>11.24 - 11.25</h3>
<pre><code class="language-mermaid">flowchart TD
  A[公司小组游洛阳] --&gt; B[前往老君山]
  B --&gt; C[遇到雨雪天气]
  C --&gt; D[乘坐缆车上山]
  D --&gt; E[沿山道徒步游览景点]
  E --&gt; F[欣赏山间雪景]
  F --&gt; G[品尝洛阳特色美食&lt;br/&gt;牛肉汤]
</code></pre>
<p>公司小组前往洛阳游览老君山，途中遇到雨雪天气，为了安全大家选择乘坐缆车上山。在缆车上和沿山道徒步游览过程中，大家欣赏到了山间的雪景和各个景点。游览结束后，品尝了洛阳的特色美食——牛肉汤。</p>
<h3>08.02 - 08.02</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发前往只有河南·戏剧幻城] --&gt; B[幻城剧场]
    B --&gt; C[火车站展区]
    C --&gt; D[候车大厅展区]
    D --&gt; E[下沉岁月展区]
    E --&gt; F[天子驾六遗址坑]
    F --&gt; G[第七机车车辆厂礼堂]
    G --&gt; H[团队晚餐：烧烤]
    H --&gt; I[结束行程返回出发地]
</code></pre>
<p>小组团建当天前往 只有河南·戏剧幻城，团队依次参观沉浸式剧场和展区，包括 幻城剧场、火车站、候车大厅、下沉岁月、天子驾六遗址坑、以及 第七机车车辆厂礼堂。在各场景中观看精彩表演、体验互动展览，并拍摄团队合照留念。游览结束后，全体成员在附近享用烧烤晚餐，边用餐边分享当天趣事，增进团队感情。行程结束后返回出发地。</p>
<h3>05.18 - 05.19</h3>
<pre><code class="language-mermaid">graph TB;
    A(第一天) --&gt; B(西安);
    B --&gt; C(到达西安站);
    C --&gt; D(去回民街喝肉丸糊辣汤);
    D --&gt; E(与伙伴汇合);
    E --&gt; F(骑行西安城墙);
    A --&gt; G[参观陕西历史博物馆];
    G --&gt; I[参观大雁塔];
    I --&gt; J[参观大唐不夜城];
    J --&gt; K[去广济街附近吃贾三包子凉皮];
    K --&gt; L[去搓麻将];
    L --&gt; M(结束第一天);

    N(第二天) --&gt; O(去永兴坊);
    O --&gt; P(中午吃\nbiangbiang面、肉夹馍、\n凉皮、米皮、臊子面);
    P --&gt; Q[参观西安世博园];
    Q --&gt; R(去广济街商圈);
    R --&gt; S(吃泡馍);
    S --&gt; T(返程);
</code></pre>
<p>第一天到达西安站后，先去了回民街喝肉丸糊辣汤，随后与伙伴汇合，一起骑行了西安城墙，接着参观了陕西历史博物馆、大雁塔和大唐不夜城，晚上在广济街附近吃了贾三包子和凉皮，之后去搓麻将，结束第一天行程；第二天前往永兴坊，中午吃了 biangbiang 面、肉夹馍、凉皮、米皮和臊子面，下午参观西安世博园，傍晚到广济街商圈吃泡馍，随后返程。</p>
<h2>2023</h2>
<h3>03.24 - 03.25</h3>
<pre><code class="language-mermaid">flowchart TD
  D1[第一天｜夜爬泰山] --&gt; A[开始夜爬]
  A --&gt; B[经过十八盘&lt;br/&gt;最累路段]
  B --&gt; C[傍晚到达南天门]
  C --&gt; D[途中结识一起爬山的朋友]
  D --&gt; E[晚上一起吃饭]
  E --&gt; F[等待清晨日出]
  F --&gt; G[拍摄日出照片]
  G --&gt; H[离开泰山]
  H --&gt; I[泰山脚下夜市吃东西]
  I --&gt; J[各自分别]
</code></pre>
<p>第一天夜爬泰山，途中经过最累的十八盘，傍晚到达南天门，途中结识了几位一起爬山的朋友，晚上大家一起吃了饭，随后等待清晨的日出，并拍下了日出的照片。离开泰山后，大家在泰山脚下的夜市吃东西，最后各自分别。</p>
<h2>2020</h2>
<h3>05.19 - 05.20</h3>
<pre><code class="language-mermaid">flowchart TD
    A[出发去绵山] --&gt; B[沿途聊天/欣赏风景]
    B --&gt; C[到达绵山景区]
    C --&gt; D[登山拍照留念]
    D --&gt; E[品尝当地美食/休息]
    E --&gt; F[参观寺庙或文化景点]
    F --&gt; G[下山返回学校/住宿地]
</code></pre>
<p>大学期间与室友乘车前往绵山，沿途聊着校园趣事，气氛轻松愉快。到达绵山后，登山欣赏自然风光，拍摄山景和同伴合照，感受山间清新的空气。途中在山脚或山中小餐馆品尝当地美食，补充体力。参观寺庙或文化景点，感受绵山的历史与文化底蕴。傍晚下山，结束愉快的绵山之旅，返回学校或住宿地，途中回味旅途趣事和拍摄的照片。</p>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[debug test page]]></title>
            <link>https://ryanuo.cc/posts/debug_test</link>
            <guid>https://ryanuo.cc/posts/debug_test</guid>
            <pubDate>Tue, 01 Jan 2019 21:31:00 GMT</pubDate>
            <description><![CDATA[debug test page]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>test page ✌</p>
</blockquote>
<pre><code class="language-dot">digraph example1 {
    1 -&gt; 2 -&gt; { 4, 5 };
    1 -&gt; 3 -&gt; { 6, 7 };
}
</code></pre>
<pre><code class="language-mermaid">graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;
</code></pre>
<pre><code class="language-ditaa">+--------+   +-------+    +-------+
    |        | --+ ditaa +--&gt; |       |
    |  Text  |   +-------+    |diagram|
    |Document|   |!magic!|    |       |
    |     {d}|   |       |    |       |
    +---+----+   +-------+    +-------+
        :                         ^
        |       Lots of work      |
        +-------------------------+
</code></pre>
<pre><code class="language-plantuml">@startgantt
[Prototype design] requires 15 days
[Test prototype] requires 10 days
-- All example --
[Task 1 (1 day)] requires 1 day
[T2 (5 days)] requires 5 days
[T3 (1 week)] requires 1 week
[T4 (1 week and 4 days)] requires 1 week and 4 days
[T5 (2 weeks)] requires 2 weeks
@endgantt
</code></pre>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
        <item>
            <title><![CDATA[debug test page]]></title>
            <link>https://ryanuo.cc/zh/posts/debug_test</link>
            <guid>https://ryanuo.cc/zh/posts/debug_test</guid>
            <pubDate>Tue, 01 Jan 2019 21:31:00 GMT</pubDate>
            <description><![CDATA[debug test page]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>test page ✌</p>
</blockquote>
]]></content:encoded>
            <author>ryanuo@aliyun.com (ryanuo)</author>
        </item>
    </channel>
</rss>