LOGO
首页 网站广场 站长动态 活跃度榜 审核查询 逛逛好站 留言交流 提交申请 关于本站

站长动态

站长动态所展示的是已加入好站网成员站长所撰写的文章
共同步 634 篇博文
(每2小时更新一次)
小十
入驻不足1年
成都两日纪行
10月20日,读研期间的导师在群里发布活动通知,计划于11月8日在成都望江宾馆举办“电力系统稳定与高压直流输电团队校友大家庭欢聚雅集”。自2017年毕业至今,虽然我曾两次到过成都——一次是因参加QC发布会,一次是假期来游玩——但都未能与导师见上一面。正好借此次活动,利用周末两天时间,与导师见面叙旧。 购票后被周基佬告知,“天府机场”到哪儿都要两个小时(包括成都市区)。打开地图才意识到,上学时天府机场尚未启用,如今它已承接大部分航线,距离本次活动场地56公里,而双流机场仅19公里。往返路程漫长,不免有些无奈。 活动通知 天府机场距离市区非常远 早晨5点起床,最终抵达成都市区已是中午。办理入住后,14点赶到会场,领取伴手礼、签名后入座。 学院吉祥物(到家后补拍) 25-11-11 · 85mm · f/1.4 · 1/320s · ISO320 · ILCE-7CM2 签到墙 25-11-8 · 0mm · f/0.0 · 1/160s · ISO2000 · Canon EOS R6m2 签到的我 会场 25-11-8 · 22mm · f/4.0 · 1/160s · ISO1600 · Canon EOS R6m2 李老师的字画 25-11-8 · 40mm · f/4.0 · 1/160s · ISO6400 · Canon EOS R6m2 多数师兄师姐、师弟师妹都已八年未见,还有曹师兄十多年没见。不过,与我同级的同学在会场并未见到。
小十
入驻不足1年
通过Hugo短代码功能实现图片及其EXIF信息展示
我有一个习惯就是在网络上分享图片时不管是调整尺寸还是压缩,都习惯保留元数据,当然也希望在分析图片时能够展示元数据。本文介绍如何在 Hugo 静态博客中借助短代码实现图片展示功能,包括单张图片和图片组的短代码实现,以及自动获取并格式化显示图片 EXIF 信息的方法。 具体效果如下图所示: 也可见 万圣节和亳都·新象的一些照片 和 天津两日纪行 等文章。 需实现的功能 支持单张图片和图片组两种展示方式 自动从图片 EXIF 数据中提取拍摄信息 格式化显示焦距、光圈、快门、ISO、相机品牌和镜头型号 支持自定义标题和多种参数传递方式 短代码实现 1. 单张图片短代码 (figure.html) 创建 layouts/shortcodes/figure.html 文件: <!-- layouts/shortcodes/figure.html --> {{ $page := .Page }} <!-- 支持两种语法:位置参数和命名参数 --> {{ $imageParam := .Get 0 }} {{ $src := .Get "src" }} {{ $title := .Get "title" }} {{ $alt := .Get "alt" }} {{ $class := .Get "class" }} <!-- 如果使用了位置参数(新语法) --> {{ if $imageParam }} {{ $imagePath := $imageParam }} {{ $customTitle := "" }} <!-- 检查参数是否包含自定义标题(使用冒号分隔) --> {{ if in $imageParam ":" }} {{ $parts := split $imageParam ":" }} {{ $imagePath = index $parts 0 }} {{ $customTitle = index $parts 1 }} {{ end }} {{ $src = $imagePath }} {{ $title = $customTitle }} {{ end }} {{ $imageResource := $page.Resources.GetMatch $src }} {{ if $imageResource }} {{ $exif := $imageResource.Exif }} {{ $autoTitle := $title | default $exif.Tags.ImageDescription | default (humanize (path.Base $src | replaceRE "\\..*$" "" | replaceRE "-|_" " ")) }} {{ $autoAlt := $alt | default $autoTitle }} <figure class="{{ $class }}"> <a href="{{ $imageResource.RelPermalink }}" class="no-a-style" data-fancybox="global-gallery" data-caption="{{ $autoTitle }}"> <img title="{{ $autoTitle }}" alt="{{ $autoAlt }}" src="{{ $imageResource.RelPermalink }}" loading="lazy" > </a> {{ if or $autoTitle $exif }} <figcaption> {{ if $autoTitle }}<span class="title">{{ $autoTitle }}</span>{{ end }} {{ with $exif }} {{ $exifParts := slice }} <!-- 1. 焦距 --> {{ with .Tags.FocalLength }} {{ $focalLength := . }} {{ if findRE "^[0-9]+/[0-9]+$" $focalLength }} {{ $parts := split $focalLength "/" }} {{ $numerator := float (index $parts 0) }} {{ $denominator := float (index $parts 1) }} {{ if gt $denominator 0 }} {{ $value := div $numerator $denominator }} {{ if eq (mod $value 1) 0 }} {{ $focalLength = printf "%.0fmm" $value }} {{ else }} {{ $focalLength = printf "%.1fmm" $value }} {{ end }} {{ end }} {{ else }} {{ $focalLength = printf "%smm" $focalLength }} {{ end }} {{ $exifParts = $exifParts | append $focalLength }} {{ end }} <!-- 2. 光圈 --> {{ with .Tags.FNumber }} {{ $fnumber := . }} {{ if findRE "^[0-9]+/[0-9]+$" $fnumber }} {{ $parts := split $fnumber "/" }} {{ $numerator := float (index $parts 0) }} {{ $denominator := float (index $parts 1) }} {{ if gt $denominator 0 }} {{ $fnumber = printf "f/%.1f" (div $numerator $denominator) }} {{ end }} {{ else }} {{ $fnumber = printf "f/%s" $fnumber }} {{ end }} {{ $exifParts = $exifParts | append $fnumber }} {{ end }} <!-- 3. 快门速度 --> {{ with .Tags.ExposureTime }} {{ $exposureTime := . }} {{ if findRE "^[0-9]+/[0-9]+$" $exposureTime }} {{ $parts := split $exposureTime "/" }} {{ $numerator := float (index $parts 0) }} {{ $denominator := float (index $parts 1) }} {{ if gt $denominator 0 }} {{ $value := div $numerator $denominator }} {{ if ge $value 1 }} {{ $exposureTime = printf "%.0fs" $value }} {{ else }} {{ $exposureTime = printf "1/%.0fs" (div 1 $value) }} {{ end }} {{ end }} {{ else }} {{ $exposureTime = printf "%ss" $exposureTime }} {{ end }} {{ $exifParts = $exifParts | append $exposureTime }} {{ end }} <!-- 4. ISO - 尝试多个可能的标签 --> {{ $isoValue := "" }} {{ with .Tags.ISOSpeedRatings }}{{ $isoValue = . }}{{ end }} {{ if not $isoValue }}{{ with .Tags.ISOSpeed }}{{ $isoValue = . }}{{ end }}{{ end }} {{ if not $isoValue }}{{ with .Tags.ISO }}{{ $isoValue = . }}{{ end }}{{ end }} {{ if not $isoValue }}{{ with .Tags.PhotographicSensitivity }}{{ $isoValue = . }}{{ end }}{{ end }} {{ with $isoValue }} {{ $isoInt := int . }} {{ if gt $isoInt 0 }} {{ $exifParts = $exifParts | append (printf "ISO%d" $isoInt) }} {{ end }} {{ end }} <!-- 5. 相机品牌 --> {{ with .Tags.Model }} {{ $cameraModel := . }} {{ if findRE "iPhone" $cameraModel }} {{ $cameraModel = "iPhone" }} {{ else if findRE "iPad" $cameraModel }} {{ $cameraModel = "iPad" }} {{ else if findRE "Canon" $cameraModel }} {{ $cameraModel = "Canon" }} {{ else if findRE "Nikon" $cameraModel }} {{ $cameraModel = "Nikon" }} {{ else if findRE "Sony" $cameraModel }} {{ $cameraModel = "Sony" }} {{ else if findRE "ILCE-" $cameraModel }} {{ $cameraModel = "Sony" }} {{ else if findRE "FUJIFILM" $cameraModel }} {{ $cameraModel = "Fujifilm" }} {{ else if findRE "X-T" $cameraModel }} {{ $cameraModel = "Fujifilm" }} {{ end }} {{ $exifParts = $exifParts | append $cameraModel }} {{ end }} <!-- 6. 镜头型号(完整显示) --> {{ with .Tags.LensModel }} {{ $lensModel := . }} {{ $lensModel = trim $lensModel " " }} {{ if and $lensModel (ne $lensModel "") }} {{ $exifParts = $exifParts | append $lensModel }} {{ end }} {{ end }} {{ if gt (len $exifParts) 0 }} <span class="exif">{{ delimit $exifParts " · " }}</span> {{ end }} {{ end }} </figcaption> {{ end }} </figure> {{ else }} {{ errorf "图片未找到: %s" $src }} {{ end }} 剩余 157 行代码 展开剩余代码 使用方法:
小十
入驻不足1年
万圣节和亳都·新象的一些照片
按照体感估算,感觉郑州已持续了两个月的阴雨天气,在这长时间恶劣天气之前还是实打实的夏季,就突然进入了冬天。 本来以为阴雨已经结束,结果又来了持续一周的大雾天气,从早浓到晚的大雾,长久不见阳光,“豫犬吠日”实至名归。 难得周六、周日太阳出现,刚好11月1日是万圣节,这天的前两天,临时起意想在这个周六去感受下年轻人的氛围,决定去看一下方特精怪夜活动。 11月2日周日这天便去了开业不久的“亳都 · 新象”凑了凑热闹。 万圣节那天 结果人是真的多,带上才到手第二天的85 1.4镜头,也没有拍到几张满意的照片。 难得第一次参与了万圣节,放出一些当日照片留个纪念。 到达方特门口时的落日 25-11-1 · 85mm · f/1.4 · 1/4000s · ISO160 · ILCE-7CM2 方特城堡 25-11-1 · 85mm · f/1.4 · 1/500s · ISO320 · ILCE-7CM2 盛装出游的小孩 25-11-1 · 85mm · f/1.4 · 1/250s · ISO500 · ILCE-7CM2 树上的挂件 25-11-1 · 85mm · f/1.4 · 1/160s · ISO500 · ILCE-7CM2 树上的挂件 25-11-1 · 85mm · f/1.4 · 1/250s · ISO500 · ILCE-7CM2 被求合影的NPC 25-11-1 · 85mm · f/1.4 · 1/250s · ISO800 · ILCE-7CM2 被求合影的NPC 25-11-1 · 85mm · f/1.4 · 1/125s · ISO800 · ILCE-7CM2 被求合影的NPC 25-11-1 · 85mm · f/1.4 · 1/50s · ISO800 · ILCE-7CM2 丧尸禁地 25-11-1 · 85mm · f/1.4 · 1/320s · ISO800 · ILCE-7CM2 争夺银票 25-11-1 · 85mm · f/1.4 · 1/320s · ISO1250 · ILCE-7CM2 快到手了 25-11-1 · 85mm · f/1.4 · 1/250s · ISO1250 · ILCE-7CM2 我喜欢这个显示器 25-11-1 · 85mm · f/1.4 · 1/80s · ISO800 · ILCE-7CM2 可爱的小女孩 25-11-1 · 85mm · f/1.4 · 1/200s · ISO1250 · ILCE-7CM2 可爱的小男孩 25-11-1 · 85mm · f/1.4 · 1/160s · ISO1600 · ILCE-7CM2 某种仪式 25-11-1 · 85mm · f/1.4 · 1/160s · ISO1250 · ILCE-7CM2 NPC 25-11-1 · 85mm · f/1.4 · 1/160s · ISO1000 · ILCE-7CM2 不想上班 25-11-1 · 85mm · f/1.4 · 1/400s · ISO1000 · ILCE-7CM2 冰糖葫芦 25-11-1 · 85mm · f/1.4 · 1/2000s · ISO1000 · ILCE-7CM2 聊斋剧场1 25-11-1 · 85mm · f/1.4 · 1/60s · ISO1000 · ILCE-7CM2 聊斋剧场2 25-11-1 · 85mm · f/1.4 · 1/20s · ISO1000 · ILCE-7CM2 聊斋剧场3 25-11-1 · 85mm · f/1.4 · 1/30s · ISO1000 · ILCE-7CM2 旋转木马 25-11-1 · 85mm · f/1.4 · 1/500s · ISO1600 · ILCE-7CM2 亳都 · 新象那天 亳都 · 新象位于郑州市管城区,东临商代城垣遗址,西接郑州文庙,由15所主题院落组成,总建筑面积约4万平方米,是郑州市首个中原传统文化主题街区。项目占地面积46亩,包含3条主街、多条巷道及4个文化主题广场,融合传统街巷肌理与现代建筑技术,采用老砖、青石板与玻璃砖等材料,体现“城、街、巷、院、景”五维布局。院墙采用“四色混拼”施工方法搭配新旧青砖,屋面瓦片使用“压五露五”等传统工艺铺设。
小十
入驻不足1年
利用短代码在hugo博客中加入bilibili播放器插件
王叨叨在 开源Bilibili播放器插件 一文中,分享了一个Typecho插件,用于将Bilibili默认播放器替换为HTML5移动端播放器,并提供更多自定义选项。 基于他的源代码,考虑将其移植到Hugo中,并通过短代码来实现bilibili视频的调用。 短代码是Hugo的一项强大功能,允许我们在Markdown中嵌入HTML片段,实现复杂的布局和功能。 王叨叨的Typecho插件主要通过以下方式工作: 通过正则表达式匹配和替换Bilibili播放器iframe 提供丰富的配置选项(宽度、高度、自动播放、弹幕开关等) 替换默认播放器为HTML5移动端播放器,提升用户体验 在上述的基础上,借助AI实现了一定程度的高度自适应和自定义封面功能,为什么说是一定程度上的高度自适应,其实是新加入了ratio参数,即明确了视频宽高比,再根据视频宽高比,使用 padding-bottom 技巧实现真正的宽高比自适应。 示例 .bilibili-player-wrapper { position: relative; width: 100%; height: 0; padding-bottom: 56.25%; margin: 1rem 0; border-radius: 8px; overflow: hidden; background-color: #f5f5f5; } .bilibili-player-wrapper iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none; border-radius: 8px; } .bilibili-custom-cover { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-size: cover; background-position: center; background-repeat: no-repeat; cursor: pointer; display: flex; align-items: center; justify-content: center; border-radius: 8px; z-index: 10; transition: opacity 0.3s ease; } .bilibili-custom-cover:hover .bilibili-play-button { transform: scale(1.1); background-color: #00a1d6; } .bilibili-play-button { width: 70px; height: 70px; background-color: rgba(0, 161, 214, 0.9); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-size: 24px; transition: all 0.3s ease; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); } .bilibili-play-button::after { content: "▶"; margin-left: 4px; } .bilibili-player-loading { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background-color: rgba(0, 0, 0, 0.7); color: white; font-size: 16px; z-index: 5; border-radius: 8px; } .bilibili-player-loading.hidden { display: none; } @media (max-width: 768px) { .bilibili-play-button { width: 60px; height: 60px; font-size: 20px; } } 视频加载中... function loadBilibiliPlayer(playerId) { const playerWrapper = document.getElementById(playerId); const cover = playerWrapper.querySelector('.bilibili-custom-cover'); const iframe = playerWrapper.querySelector('iframe'); const loading = playerWrapper.querySelector('.bilibili-player-loading'); if (iframe && iframe.hasAttribute('data-src')) { loading.classList.remove('hidden'); iframe.src = iframe.getAttribute('data-src'); iframe.style.display = 'block'; cover.style.opacity = '0'; iframe.onload = function() { loading.classList.add('hidden'); setTimeout(function() { cover.style.display = 'none'; }, 300); }; } } document.addEventListener('DOMContentLoaded', function() { const players = document.querySelectorAll('.bilibili-player-wrapper'); players.forEach(function(player) { const cover = player.querySelector('.bilibili-custom-cover'); const iframe = player.querySelector('iframe'); if (!cover && iframe && iframe.hasAttribute('data-src')) { iframe.src = iframe.getAttribute('data-src'); iframe.style.display = 'block'; } }); }); 具体实现 创建短代码文件 在Hugo项目的 layouts/shortcodes/ 目录下创建 bilibili.html 文件:
小十
入驻不足1年
天津两日纪行
10月25—26日,趁着周末两天,去了一趟天津。 第一天 早上5点半起床,上午约10点30分,抵达天津西站。出站后,搭乘出租车前往位于滨江道步行街内的酒店。 滨江道步行街是天津市最为繁华的商业街之一,其历史可追溯至20世纪初,汇集了众多商场与老字号品牌。午餐选择了“醉喜楼津菜馆”,尝试了特色菜八珍豆腐与皮皮虾陷的锅贴,菜品份量充足。 传统八珍豆腐 25-10-25 · 9mm · f/2.8 · 1/100s · ISO64 · iPhone 皮皮虾扇贝锅贴 25-10-25 · 50mm · f/1.8 · 1/341s · ISO80 · iPhone 午后,首先参观了西开教堂。其全称为天主教西开总堂,始建于1916年,是天津市规模最大的罗马风貌建筑群,建筑风格为罗曼式。
小十
入驻不足1年
解决林木木老师哔哔广场中同一Memos实例下多用户头像昵称显示错误问题
问题描述 非常感谢 林木木 老师编写的 哔哔广场 ,日常用于调取Memos网站的内容,并实现了同步订阅其他Memos用户及发表 memos 等功能。 但是,我这里有一个较为小众的需求,就是在使用 Memos 广场功能时,如下图界面: 当多个用户来自同一个 Memos 实例时,其动态内容能够正常显示,但用户信息(头像和昵称)却全部显示为同一个用户的信息。 例如,在我的订阅列表中: 小十 (ID: 1, 头像: avatar.png) 泥鳅胡子 (ID: 3, 头像: myh.jpg) 上述两个用户都使用同一个 Memos 实例 https://memos.xiaoten.com,但在广场模式下,所有动态都显示为泥鳅胡子的头像和昵称,即使这些动态实际上是小十发布的。 原因分析 主要原因 用户信息映射逻辑未覆盖这种特殊情况,在原始的代码实现中: 获取用户动态时,代码使用了同一个 Memos 实例下的第一个用户信息来覆盖所有用户的动态信息。 最初使用 creatorName 作为映射键,但当多个用户有相同的显示名时会导致冲突。 在合并用户信息和动态数据时,属性覆盖的顺序问题,会使用户信息被动态数据中的空值覆盖。 技术细节 在 Promise 内部处理动态数据时,原始代码使用了 matchedMemo 而不是当前用户 u 的信息: // 原因 for (let key in matchedMemo) { if (matchedMemo.hasOwnProperty(key)) { item[key] = matchedMemo[key]; } } 剩余 1 行代码 展开剩余代码 这会使同一个 Memos 实例下的所有用户都使用了第一个匹配的用户信息。 解决方式 1. 修改 Promise 内部的用户信息赋值 在获取每个用户的动态时,使用当前用户 u 的信息而不是 matchedMemo:
小十
入驻不足1年
在 Hugo 中集成 Memos 多用户微博系统
摘要 本文详细阐述在 Hugo 静态网站生成器中集成 Memos 0.18.2 多用户微博系统的完整技术方案。通过 RESTful API 调用、Twikoo 评论系统集成、图片资源处理等关键技术,实现了在静态网站中展示动态微博内容的功能。本文涵盖系统架构设计、核心代码实现、性能优化策略以及部署配置指南,为开发者提供完整的实施参考。样例请见本站 说说页面 。 1. 主要流程 1.1 系统组成 前端框架: Hugo 静态网站生成器 微博服务: Memos 0.18.2 RESTful API 评论系统: Twikoo 评论服务 图片处理: ViewImage 灯箱 + Lozad 懒加载 内容渲染: Marked.js Markdown 解析器 1.2 数据流架构 Memos API → 数据获取 → 内容处理 → 评论统计 → HTML 渲染 → 前端展示2. 核心实现方案 2.1 多用户配置管理 在 Hugo 模板中定义用户配置数组: var memosMyList = [ { "creatorName": "用户A", "website": "https://example.com", "link": "https://memos.example.com", "creatorId": "1", "avatar": "/avatars/user-a.png", "twikoo": "https://twikoo.example.com" }, { "creatorName": "用户B", "website": "https://example.org", "link": "https://memos.example.com", "creatorId": "2", "avatar": "/avatars/user-b.png", "twikoo": "https://twikoo.example.com" } ]; 剩余 13 行代码 展开剩余代码 2.2 API 集成与数据处理 2.2.1 并发数据获取 async function getAllUsersMemos() { const userPromises = currentUsers.map(user => getUserMemos(user.link, user.creatorId, user.creatorName, user.avatar) ); const allUserResults = await Promise.allSettled(userPromises); const successfulResults = []; allUserResults.forEach((result) => { if (result.status === 'fulfilled' && Array.isArray(result.value)) { successfulResults.push(...result.value); } }); return successfulResults; } 剩余 11 行代码 展开剩余代码 2.2.2 数据验证与标准化 function validateUserConfig() { const validUsers = currentUsers.filter(user => user.creatorId && user.link && user.creatorName ); return validUsers.length > 0; } function normalizeUrl(baseUrl, path) { const normalizedBase = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; const normalizedPath = path.startsWith('/') ? path : '/' + path; return normalizedBase + normalizedPath; } 剩余 8 行代码 展开剩余代码 2.3 评论系统集成 2.3.1 批量评论统计 async function getMemoCount(memos) { const twikooGroups = {}; // 按 Twikoo 环境分组处理 memos.forEach(item => { if (!item?.twikoo) return; const envId = item.twikoo; const memoUrl = normalizeUrl(item.link, `/m/${item.id}`); if (!twikooGroups[envId]) { twikooGroups[envId] = []; } twikooGroups[envId].push({ url: memoUrl }); }); const allTwikooCount = []; for (const [envId, items] of Object.entries(twikooGroups)) { try { const urls = items.map(item => item.url); const res = await twikoo.getCommentsCount({ envId: envId, urls: urls, includeReply: false }); if (Array.isArray(res)) { allTwikooCount.push(...res); } } catch (error) { console.error(`Twikoo 环境 ${envId} 评论数获取失败:`, error); } } // 关联评论数到对应 Memo memos.forEach(item => { if (!item?.twikoo) { item.count = 0; return; } const url = normalizeUrl(item.link, `/m/${item.id}`); const countData = allTwikooCount.find(o => o?.url === url); item.count = countData?.count || 0; }); return memos; } 剩余 44 行代码 展开剩余代码 2.4 内容渲染引擎 2.4.1 Markdown 内容处理 function processMemoContent(content) { const TAG_REGEX = /#([^#\s!.,;:?"'()]+)(?= )/g; const IMAGE_REGEX = /\!\[(.*?)\]\((.*?)\)/g; const LINK_REGEX = /(?<!!)\[(.*?)\]\((.*?)\)/g; let processed = content .replace(TAG_REGEX, '') .replace(IMAGE_REGEX, '') .replace(LINK_REGEX, '<a class="primary" href="$2" target="_blank">$1</a>'); return marked.parse(processed); } 剩余 7 行代码 展开剩余代码 2.4.2 图片资源处理 function processImageResources(content, memo) { let imageHtml = ''; // 处理内联图片 const inlineImages = content.match(IMAGE_REGEX); if (inlineImages?.length > 0) { const imageString = inlineImages.join('').replace(/,/g, ''); imageHtml = imageString.replace(IMAGE_REGEX, '<div class="memo-resource width-100">' + '<img class="lozad" data-src="$2" alt="$1">' + '</div>' ); } // 处理附件图片 if (memo.resourceList?.length > 0) { memo.resourceList.forEach(resource => { if (resource.type?.startsWith('image')) { const imageUrl = resource.externalLink || normalizeUrl(memo.link, `/o/r/${resource.uid || resource.id}`); imageHtml += '<div class="memo-resource w-100">' + '<img class="lozad" data-src="${imageUrl}">' + '</div>'; } }); } return imageHtml ? '<div class="resource-wrapper">' + '<div class="images-wrapper my-2" view-image>' + imageHtml + '</div>' + '</div>' : ''; } 剩余 27 行代码 展开剩余代码 3. 性能优化策略 3.1 分页加载机制 function pagination(data, page, limit) { const startIndex = (page - 1) * limit; const endIndex = startIndex + limit; return data.slice(startIndex, endIndex); } function updateData(data) { const validData = data.filter(item => item && typeof item === 'object'); validData.sort((a, b) => b.createdTs - a.createdTs); const pageData = pagination(validData, currentPage, itemsPerPage); renderMemoList(pageData); const totalPages = Math.ceil(validData.length / itemsPerPage); updatePaginationControls(currentPage, totalPages); } 剩余 11 行代码 展开剩余代码 3.2 图片性能优化 function initializeImageOptimizations() { // 图片懒加载 if (typeof lozad !== 'undefined') { const imageObserver = lozad('.lozad', { rootMargin: '50px 0px', threshold: 0.1 }); imageObserver.observe(); } // 图片灯箱初始化 if (typeof ViewImage !== 'undefined') { ViewImage.init('.images-wrapper img'); } } 剩余 10 行代码 展开剩余代码 3.3 缓存策略 class MemosCache { constructor() { this.cacheKey = 'memos-data-cache'; this.cacheTimeout = 5 * 60 * 1000; // 5分钟 } getCachedData() { const cached = localStorage.getItem(this.cacheKey); if (!cached) return null; const { data, timestamp } = JSON.parse(cached); if (Date.now() - timestamp > this.cacheTimeout) { localStorage.removeItem(this.cacheKey); return null; } return data; } setCachedData(data) { const cacheObject = { data: data, timestamp: Date.now() }; localStorage.setItem(this.cacheKey, JSON.stringify(cacheObject)); } } 剩余 22 行代码 展开剩余代码 4. 错误处理与监控 4.1 健壮性设计 async function getUserMemos(link, userId, userName, userAvatar) { try { // 参数验证 if (!link || !userId) { throw new Error('缺少必要参数'); } const normalizedLink = link.endsWith('/') ? link : link + '/'; const apiUrl = `${normalizedLink}api/v1/memo?creatorId=${userId}&rowStatus=NORMAL&limit=50`; const response = await fetch(apiUrl); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); if (!Array.isArray(data)) { throw new Error('API 返回数据格式错误'); } return data.filter(item => item && typeof item === 'object') .map(item => ({ ...item, link: normalizedLink, avatar: userAvatar, creatorName: userName })); } catch (error) { console.error(`获取用户 ${userName} 数据失败:`, error); return []; // 优雅降级 } } 剩余 29 行代码 展开剩余代码 4.2 性能监控 class PerformanceMonitor { static async measureApiCall(apiCall) { const startTime = performance.now(); try { const result = await apiCall(); const duration = performance.now() - startTime; if (duration > 1000) { // 超过1秒记录警告 console.warn(`API 调用耗时较长: ${duration.toFixed(2)}ms`); } return result; } catch (error) { const duration = performance.now() - startTime; console.error(`API 调用失败,耗时 ${duration.toFixed(2)}ms:`, error); throw error; } } } 剩余 14 行代码 展开剩余代码 5. 部署配置指南 5.1 文件结构规范 hugo-site/ ├── layouts/ │ └── _default/ │ └── memos.html ├── static/ │ └── memos/ │ ├── js/ │ │ ├── memos-core.js │ │ ├── twikoo.min.js │ │ ├── marked.min.js │ │ └── lozad.min.js │ └── css/ │ └── memos-styles.css └── content/ └── memos.md 剩余 10 行代码 展开剩余代码 5.2 Hugo 模板配置 <!-- layouts/_default/memos.html --> {{ define "main" }} <div class="memos-container"> <header class="memos-header"> <h1>{{ .Title }}</h1> </header> <div class="memos-content"> <div id="memo-list" class="memo-list-container"></div> <button id="load-more" class="load-more-button">加载更多</button> </div> </div> <script src="/memos/js/marked.min.js"></script> <script src="/memos/js/lozad.min.js"></script> <script src="/memos/js/twikoo.min.js"></script> <script src="/memos/js/memos-core.js"></script> {{ end }} 剩余 13 行代码 展开剩余代码 5.3 环境变量配置 // 生产环境配置 const MEMOS_CONFIG = { apiEndpoints: { memos: 'https://memos.example.com/api/v1', twikoo: 'https://twikoo.example.com' }, performance: { cacheTimeout: 300000, paginationSize: 10, imageLazyLoad: true }, features: { multiUser: true, comments: true, imageZoom: true } }; 剩余 12 行代码 展开剩余代码 6. 安全考虑 6.1 输入验证 function sanitizeUserInput(input) { if (typeof input !== 'string') return ''; return input .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#x27;') .replace(/\//g, '&#x2F;'); } 剩余 5 行代码 展开剩余代码 6.2 CSP 配置建议 <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://memos.example.com https://twikoo.example.com; img-src 'self' https: data:; style-src 'self' 'unsafe-inline'; connect-src 'self' https://memos.example.com https://twikoo.example.com"> 剩余 1 行代码 展开剩余代码 7. 测试方案 7.1 单元测试示例 describe('Memos Integration', () => { test('URL normalization', () => { expect(normalizeUrl('https://example.com/', '/m/123')) .toBe('https://example.com/m/123'); expect(normalizeUrl('https://example.com', 'm/123')) .toBe('https://example.com/m/123'); }); test('Content processing', () => { const input = 'Hello [world](https://example.com)'; const processed = processMemoContent(input); expect(processed).toContain('href="https://example.com"'); }); }); 剩余 10 行代码 展开剩余代码 8. 结论 本文提出的 Hugo 与 Memos 多用户微博系统集成方案,通过系统的架构设计和严谨的代码实现,成功在静态网站环境中引入了动态社交功能。关键技术贡献包括:
小十
入驻不足1年
借助 GitHub Actions 自动化部署 Hugo 网站到自有服务器
从手动部署到自动化的工作流演进 在网站开发的初期阶段,为了深入理解整个发布流程,我一直在本地环境中使用 Hugo 手动生成静态网站。这个过程包括:运行 hugo 命令生成静态文件,手动打包 public 目录,然后通过 SCP 或 FTP 上传到服务器,最后在服务器上解压并配置。虽然这种方法让我对网站部署的每个环节都有了清晰的认识,但随着内容的增多,这种重复性工作变得越来越耗时。 虽然现在有 Vercel、Netlify 等优秀的静态网站托管平台,但由于我已经拥有一台国内的轻量级云服务器(主要用于 frp 内网穿透服务),我决定充分利用现有资源,直接在这台服务器上托管我的 Hugo 网站。 自动化部署方案设计 最终实现的解决方案是:通过 GitHub 私有仓库存储 Hugo 源码,利用 GitHub Actions 实现自动化构建和部署,使用 rsync 将生成的静态文件同步到自己的服务器。 这种方案的优势在于: 自动化:代码推送后自动完成整个构建和部署流程 版本控制:所有变更都有完整的历史记录 安全性:私有仓库保护源代码,SSH 密钥保证传输安全 成本效益:充分利用现有服务器资源 服务器端配置 环境准备 我使用 1Panel 面板管理服务器,网站目录为: /opt/1panel/apps/openresty/openresty/www/sites/xiaoten.com/index安装 rsync 由于同步方案使用 rsync,需要在服务器上安装: # CentOS/RHEL 系统 sudo yum install -y rsync # Ubuntu/Debian 系统 sudo apt-get update && sudo apt-get install -y rsync安全配置:SSH 密钥认证 为了实现安全的自动化部署,需要配置 GitHub Actions 对服务器的访问权限。
小十
入驻不足1年
终于初步完成了从Typecho到Hugo的网站迁移
整体历程 2011年2月2日,本站启用并使用WordPress; 2021年7月29日,从WordPress迁移至Typecho; 2025年10月16日,终于正式把域名指向到了Hugo生成的静态页面。 主要过程 因为涉及到html转markdown,使用自动化的转换工具带了一些排版混乱、不合理的转义等等,本来想仿照有些博主,写个脚本自动优化,但随后又转念一想,还是都过一遍吧,于是在工作中见缝插针,以及下班后沉浸到半夜,持续两天枯燥的html转markdown的过程。 虽然当时WordPress转Typecho时已进行了一些格式上的适配,但这次回过头看,问题特别多。 因此还是启用了21年转Typecho时wordpress的备份,好在迁移到Typecho,并没有写过几篇文章,迁移到Typecho以后的文章手动添加就好了。 静态博客生成工具的确定 决定采用静态博客的目的,还是想学习现在的博客主流模式,让更多的注意力在写作本身上面,摆脱臃肿的博客程序,虽说typecho已经非常轻量了,但可能是使用主题的问题,总会有遇到各种各样的使用问题,自身也没有精力去学着去写一个精简的主题,另外静态博客可以很方便的托管到一些GitHub等这样的一些开源平台上,不用再受制于服务器等这些问题。 目前,整个的服务器都已迁移到家庭的nas里,通过购买阿里云的轻量云服务器仅用来内网穿透+反向代理使用,整体成本很低。 除了个人博客这个站点以外的其他网站应用或服务应用还得依托于家里的服务器。 经过浏览了多个业内大佬的网站之后,发现Hugo的覆盖率还挺高,另外Hugo官网的一些主题也都是偏简洁的风格,在B站看了Hugo的实现原理及教程,也很容易上手,因此确定了Hugo作为生成工具。 Hugo模板的确定 最终在官网上对比了几个比较主流的模板之后,最终目光还是集中在了一个比较简洁的一个主题: Hugo blog awesome 。 决定开始使用这个主题是在10月11日,截至写这篇文章已经调试使用了5天时间,目前整体使用起来没有大的问题,但还是有一些自己不满意且比较棘手的问题。不知道后续会不会再换模板,目前先这样用着。 字体 在原有模板的基础上应用了思源宋体,并通过 cn-font-split 实现了字体切割,实现网页字体的快速加载。 分页功能 另外想通过hugo自带的分页功能实现文章列表的分页,但尝试很多种方法,并借助deepseek都没有办法实现。因此最终通过了JavaScript脚本实现了分页功能,这个看后续有没有更好的解决办法。 已解决分页问题,详见文章: 解决 Hugo 分页器返回空页面的诡异问题:.Paginate 不能被多次调用 。 全文搜索 静态博客如果想实现全文搜索,方法也比较多,我采用了比较成熟的工具: Pagefind 。 代码高亮的自动转换 默认hugo自带的代码高亮功能,一般只能设置一个样式,固定下来之后,不管主题是白色还是黑色,都是统一的一个代码高亮样式,为了实现白色/黑色主题切换时代码高亮主题也自动进行更换,通过脚本实现不同主题下不同的代码高亮方案。 识别外部链接自动启用新窗口打开 通过在layouts/_default/_markup/目录下创建render-link.html文件,并写入代码: {{ $url := urls.Parse .Destination }} {{ $isExternal := eq $url.Scheme "http" "https" }} <a href="{{ .Destination | safeURL }}" {{ if $isExternal }}target="_blank" rel="noopener noreferrer"{{ end }} {{ with .Title }}title="{{ . }}"{{ end }}> {{ .Text | safeHTML }} </a> 剩余 3 行代码 展开剩余代码 实现自动识别非本站链接采用新窗口打开的功能。
Tree Hole
入驻不足1年
链路聚合负载不均导致部分链路拥塞
问题描述 核心交换机之间通过一条4*10G Eth-Trunk(链路聚合组)互联。监控发现,该聚合组的总流量并 […]
小十
入驻不足1年
PVE从8升级到9
今天完成了Proxmox Virtual Environment 从8升级到9。 升级前准备 确保您的所有节点都已升级到 Proxmox VE 8.4 的最新版本。 apt update && apt dist-upgrade pveversion检查是否满足升级条件 pve8to9或 pve8to9 --full需要 ·SUMMARY· 里没有警错误才能执行升级 备份 APT 软件源 cp /etc/apt/sources.list /etc/apt/sources.list.pve-old cp /etc/apt/sources.list.d/pve-enterprise.list /etc/apt/sources.list.d/pve-enterprise.list.old切换 APT 软件源 sed -i 's/bookworm/trixie/g' /etc/apt/sources.list sed -i 's/bookworm/trixie/g' /etc/apt/sources.list.d/pve-enterprise.list刷新软件包索引 apt update开始升级 apt dist-upgrade或 apt full-upgrade如出现更新日志按 q 键退出 后续提示可以默认按 回车 键继续 查看系统版本 cat /etc/debian_version
Tree Hole
入驻不足1年
Tcping 命令安装方法和使用教程
前言 测试网络连通性我们一般使用windows自带的ping命令,ping命令发送icmp包,但是如果服务器禁 […]
Tree Hole
入驻不足1年
使用DeepSeek生成python工具
前言 近期项目中遇到一个比较老的技术--SDH,在这之前完全没有听说过'时隙'这个概念,本次遇到需要用的这东西 […]
小十
入驻不足1年
使用 libwebp 高效批量转换图片至 WebP 格式
WebP 是一种现代图像格式,可为 Web 上的图像提供出色的无损和有损压缩。使用 WebP,网站管理员和 Web 开发人员可以创建尺寸更小、内容更丰富的图像,从而使 Web 更快。本文将指导您如何使用 Google 官方提供的 libwebp 工具包批量将您现有的图片(如 JPG, PNG, BMP, GIF 等)转换为 WebP 格式。 1、下载libwebp 打开https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html,划到底部找到最新版本,根据操作系统选择合适的版本。 比如Windows选择 libwebp-1.4.0-windows-x64.zip ,macOS X86选择 libwebp-1.4.0-rc1-mac-x86-64.tar.gz ,macOS ARM选择 libwebp-1.4.0-rc1-mac-arm64.tar.gz ……根据你的实际情况来 2、准备脚本 将下载的文件解压,在解压出来的文件夹旁边再创建两个叫“input”和“output”的文件夹,然后»> Windows 创建一个名为c.bat的脚本文件,并用记事本写入: @echo offss setlocal enabledelayedexpansion :: 设置输入文件夹路径 set "input_folder_path=input" :: 设置输出文件夹路径 set "output_folder_path=output" :: 检查输出文件夹是否存在,如果不存在则创建 if not exist "%output_folder_path%" mkdir "%output_folder_path%" :: 设置cwebp工具的路径,如果cwebp在环境变量中,则不需要设置 :: set "cwebp_path=C:\path\to\cwebp\cwebp.exe" :: 遍历输入文件夹中的所有图像文件 for %%i in ("%input_folder_path%\*.jpg", "%input_folder_path%\*.jpeg", "%input_folder_path%\*.png", "%input_folder_path%\*.bmp", "%input_folder_path%\*.gif") do ( :: 构建输出文件名 set "output_file=%output_folder_path%\%%~ni.webp" :: 使用cwebp转换图像文件 :: 如果cwebp在环境变量中,直接使用cwebp,否则使用完整的路径 :: 替换下面的cwebp为%cwebp_path%,如果你设置了cwebp工具的路径 cwebp -q 80 "%%i" -o "!output_file!" :: 输出转换结果 echo Converted %%i to !output_file! ) echo All images have been converted. endlocal 剩余 26 行代码 展开剩余代码 macOS/Linux 创建一个名为c.sh的脚本文件,并用纯文本编辑器写入:
小十
入驻不足1年
从宝塔到1Panel:Typecho站点HTTPS混合内容问题排查与解决
之前,我一直习惯使用外网服务器上的宝塔面板来实现网站的反向代理功能。然而,在尝试为网站设置防盗链等自定义Nginx配置时,我发现宝塔面板生成的配置文件直接编辑较为困难,且通过其自定义配置功能生成的代码也显得有些冗余和不够直观。 恰好,我内网的服务器早已换上了1Panel面板,并且使用体验相当不错,感觉在灵活性和功能性上比宝塔面板更胜一筹。于是,我决定将外网服务器也进行系统重装,并部署了1Panel。随后,我在新的1Panel环境中安装了FRPS(FRP服务端)应用和OpenResty应用。 通过FRPS的HTTP(S)内网穿透功能,结合OpenResty反向代理,将内网的网站服务暴露到公网,并利用1Panel自动SSL证书续签功能,实现了预期的HTTPS访问。整个配置过程非常直观。 然而,在迁移完成后,一个意想不到的问题出现了:现在的这个网站(其他网站均正常)虽然可以通过HTTPS访问,但其CSS和JS等静态资源却无法正确加载。通过浏览器开发者工具查看,发现这些资源的URL仍然是HTTP协议,导致在HTTPS页面下因“混合内容(Mixed Content)”而被浏览器阻止加载。 奇怪的是,这个问题在迁移前(使用宝塔面板时)并未发生。考虑到这个出问题的网站是基于Typecho博客程序搭建的,而其他网站并非如此,我推测问题可能出在Typecho本身与新环境的适配上。 后来,找到了一个针对Typecho的简单解决方案。具体操作如下: 打开Typecho网站的根目录,找到并编辑 config.inc.php 文件,在其中添加以下一行代码: /** 开启HTTPS */ define('__TYPECHO_SECURE__', true);这行代码的作用是显式告知Typecho当前站点已启用HTTPS。在此之前,我的配置文件中确实没有这行设置。 保存修改后,再次访问网站,CSS和JS资源均已通过HTTPS正确加载,问题解决
小十
入驻不足1年
记录我的 Chevereto V4 Docker 版升级过程
自从我安装了 Chevereto 相册应用之后,就发现它的更新迭代还是挺频繁的。目前的大版本是 V4,而小版本几乎每一到两周就会有一次更新。 我个人是采用 Docker 方式来部署 Chevereto 的,并且已经购买了 V4 大版本的永久授权。每次升级前,我都会先登录到 Chevereto 的官方后台,下载包含授权的最新程序文件,然后将它上传到我的服务器上。 不过,我并不会直接使用压缩包里的程序文件来覆盖安装。相反,我更倾向于利用程序文件包里面提供的 docker 文件夹。通过这个文件夹里的 Makefile,我可以使用 make 命令来编译和管理我的 Docker 镜像。 具体的升级步骤,我主要参考了官方的 Docker 安装和部署指南,这里查看原文: Install Chevereto on Docker | Chevereto V4 Docs 下面是我根据官方文档整理的 Docker 升级步骤: 升级 (Upgrading) 要升级 Chevereto,您需要执行以下三个步骤:(1) 同步仓库,(2) 重新构建容器镜像,以及 (3) 更新 Chevereto 实例。 步骤 1:同步仓库 (Step 1: Sync repository) 同步此仓库以获取最新的代码和更改。 make sync注意: 如果官方发布了新的分支(例如,从 4.2 升级到 4.3 时,可能会有新的 4.3 分支),您需要运行以下命令切换到新的分支: git switch 4.3(请将 4.3 替换为实际的最新分支名) 步骤 2:重新构建容器镜像 (Step 2: Re-build the container image) 构建一个新的容器镜像,以包含最新发布的版本内容。
小十
入驻不足1年
免费引入商用黑体字体系列整理及 CSS 字体引入亲妈式教程
声明:本文转载并基于 免费引入商用黑体字体系列整理及 CSS 字体引入亲妈式教程(20240915更新) – 风记星辰 进行修改和完善。以下为正文内容: 在现代网页设计中,字体的选择对于用户体验和视觉效果至关重要。鉴于系统默认的微软雅黑字体在某些设计场景下略显陈旧,许多开发者和设计师倾向于引入更现代的无衬线字体,如苹果的苹方(PingFang SC)。这样做不仅能优化在 macOS 设备上的显示,也可能(在用户已安装相应字体的前提下)改善 Windows 系统上的视觉呈现。 本文将整理一系列免费可商用的黑体(无衬线)字体资源,并提供详尽的 CSS 字体引入教程。 1. 免费可商用字体列表及引用资源 1.1 字体提供方站点 以下是一些提供免费可商用字体或有相关计划的官方站点: 服务商 链接 备注 Google Fonts https://fonts.google.com 国内访问可能需代理 Adobe Fonts https://fonts.adobe.com 通常需 Creative Cloud 订阅授权 阿里巴巴普惠体 https://fonts.alibabagroup.com 免费商用 钉钉进步体 https://page.dingtalk.com/wow/dingtalk/default/dingtalk/y-W5aF3_ZJwzulU0nceIl 免费商用 斗鱼追光体 https://www.douyu.com/topic/douyuZGT 免费商用 快看世界体 https://www.kuaikanmanhua.com/webs/fontPromotion 免费商用 Mi Sans (小米) https://hyperos.mi.com/font/zh/ 免费商用 HarmonyOS Sans (华为) https://developer.huawei.com/consumer/cn/design/resource-V1 免费商用 OPPO Sans https://open.oppomobile.com/new/developmentDoc/info?id=13223 免费商用 1.2 网页 CDN 引入 部分字体提供了 CDN 链接,方便直接在网页中引入:
小十
入驻不足1年
PVE 系统小版本升级实录:从 8.4.1 到最新稳定版
今天分享下我的 Proxmox VE (PVE) 系统小版本升级过程。我的 N150 小主机自从 5 月 8 日安装了 PVE 8.4.1 版本后,一直稳定运行,默默奉献。不知不觉已经过去一段时间,本着“尝鲜”和“保持系统健康”的原则,今天决定检查一下是否有可用的更新,并进行一次系统升级。 1. 检查当前 PVE 版本 首先,自然是 SSH 登录到 PVE 主机,查看一下当前的系统版本。通过 pveversion -v 命令(或者直接 pveversion 也能看到主要版本),我得到了以下信息: root@xiaoten:~# pveversion -v pve-manager/8.4.1/2a5fa54a8503f96d (running kernel: 6.8.12-10-pve) proxmox-kernel-6.8.12-10-pve: 6.8.12-10 proxmox-kernel-6.8: 6.8.12-10 proxmox-kernel-6.5: 6.5.13-5 proxmox-kernel-6.5.13-5-pve-signed: 6.5.13-5 ceph-fuse: 17.2.7-pve3 corosync: 3.1.7-pve3 criu: 3.17.1-2 glusterfs-client: 10.3-5 ifupdown2: 3.2.0-1+pmx8 ksm-control-daemon: 1.4-1 libjs-extjs: 7.0.0-4 libknet1: 1.28-pve1 libproxmox-acme-perl: 1.5.0 libproxmox-backup-qemu0: 1.4.1 libproxmox-rs-perl: 0.3.3 libpve-access-control: 8.1.3 libpve-apiclient-perl: 3.3.1 libpve-common-perl: 8.2.1 libpve-guest-common-perl: 5.1.1 libpve-http-server-perl: 5.1.0 libpve-network-perl: 0.9.9 libpve-rs-perl: 0.8.8 libpve-storage-perl: 8.2.1 libpve-user-cfg0: 1.0-1 libspice-server1: 0.15.1-1 lvm2: 2.03.16-2 lxc-pve: 5.0.2-4 lxcfs: 5.0.3-pve4 novnc-pve: 1.4.0-3 proxmox-backup-client: 3.2.2-1 proxmox-backup-file-restore: 3.2.2-1 proxmox-kernel-helper: 8.1.0 proxmox-mail-forward: 0.2.3 proxmox-mini-journalreader: 1.4.0 proxmox-offline-mirror-helper: 0.6.6 proxmox-widget-toolkit: 4.2.1 pve-cluster: 8.0.5 pve-container: 5.1.1 pve-docs: 8.2.2 pve-edk2-firmware: 4.2023.08-4 pve-firewall: 5.0.3 pve-firmware: 3.10-1 pve-ha-manager: 4.0.3 pve-i18n: 3.2.1 pve-qemu-kvm: 8.1.5-6 pve-xtermjs: 5.3.0-3 qemu-server: 8.2.1 smartmontools: 7.3-pve1 spiceterm: 3.3.0 swtpm: 0.8.0+pve1 vncterm: 1.8.0 zfsutils-linux: 2.2.4-pve1 root@xiaoten:~# 剩余 50 行代码 展开剩余代码 可以看到,当前 PVE Manager 版本为 8.4.1,运行的内核是 6.8.12-10-pve。确认无误,准备开始更新。
小十
入驻不足1年
解决软路由 OpenClash 环境下 Docker 镜像拉取不稳定的问题
前言 近期,在将网络环境迁移至软路由平台(具体为 iStoreOS)并配置 OpenClash 后,我遇到了一个相当棘手的问题:部署在该软路由下的设备,包括群晖 NAS、另一台 NAS 以及用于托管网站的 Debian/CentOS 服务器,在尝试拉取 Docker 镜像时表现极不稳定,经常遭遇连接超时或下载失败。这个问题困扰了我好几天,在经过多次排查与反复试验后,终于找到了一套能够稳定运行的配置方案。在此,我将解决问题的关键步骤总结为以下三个核心原则,希望能为遇到类似困境的朋友提供参考。 原则一:在软路由系统层面禁用 IPv6 问题分析: 在我的实践中,软路由操作系统(iStoreOS)层面启用的 IPv6 功能似乎与 OpenClash 的代理机制存在一定的冲突或兼容性问题,这成为了导致网络异常,特别是影响 Docker 镜像这类需要通过代理访问外部资源操作稳定性的潜在因素之一。 解决方案: 最直接有效的办法是在软路由的管理界面中,找到网络接口或系统相关设置,彻底禁用 IPv6 协议。 配置理由: 我的主要需求是利用软路由为特定的内网设备(NAS、服务器等)提供稳定、高效的代理网络访问,以应对特殊的网络环境。在这些应用场景下,IPv6 并非强制性要求。禁用 IPv6 可以简化软路由的网络处理逻辑,排除因双栈(IPv4/IPv6 并存)或 IPv6 路由策略引发的潜在冲突,从而显著提升 OpenClash 在处理代理流量时的稳定性和可靠性。请注意,此操作通常针对软路由本身,而非上游的主路由器。 原则二:优化 DNS 服务器配置 排查过程: 最初,我尝试过将 DNS 指向主路由网关地址,也尝试了国内主流的公共 DNS 服务,如阿里云 DNS (223.5.5.5 / 223.6.6.6) 和腾讯云 DNS (119.29.29.29)。然而,这些尝试均未能有效改善 Docker 镜像拉取的稳定性。 最终方案: 经过对比测试,效果最佳的配置是将 DNS 服务器指定为 Google DNS (8.8.8.8) 和 114 DNS (114.114.114.114) 的组合。这可以在软路由的 DHCP 服务设置中分配给客户端,或者直接在 OpenClash 的 DNS 配置部分进行指定。 配置理由: 8.8.8.8 作为全球广泛使用的 DNS 服务,对于解析 Docker Hub 等国际资源域名通常具有更好的效果和抗干扰能力。而 114.114.114.114 作为国内老牌的公共 DNS,可以为国内资源的解析提供稳定性和速度保障。实践证明,在需要代理的环境下,这种国内外 DNS 结合的策略,相较于单一使用国内 DNS,更能有效规避潜在的 DNS 污染或解析错误,确保需要代理的服务(如 Docker)能够正确、稳定地建立连接。 原则三:合理配置 OpenClash 运行模式与内核 关键配置 运行模式 (Mode): 建议设置为 增强模式 (Enhance Mode) 或 Fake-IP 模式 (具体名称请根据您使用的 OpenClash 版本界面为准)。这类高级模式通常能提供更广泛的兼容性,更好地处理复杂的网络流量。 代理模式 (Proxy Mode): 选择 规则模式 (Rule Mode)。这与大多数桌面 Clash 客户端的常用策略一致,允许 OpenClash 根据预定义的规则智能判断哪些流量需要通过代理,哪些直连,实现精细化控制,提高网络效率。 内核更新: 务必确保 OpenClash 所使用的 Clash 核心 (Kernel) 程序为最新稳定版。开发者会持续在新版本中修复 Bug、优化性能并增加新特性,使用最新内核是保障稳定运行的基础。 配置理由 合理的运行模式和代理模式组合,配合最新的内核,能够最大限度地发挥 OpenClash 的性能和稳定性,确保其正确处理 Docker 等应用发起的网络请求。
小十
入驻不足1年
Debian 12默认官方源/清华源/中科大源/腾讯云源/阿里云源/Linode源sources.list
一、备份现有apt源配置 mv /etc/apt/sources.list /etc/apt/sources.list.old二、替换为其他apt源 1、替换为默认官方源 这是一条命令,全部复制后,直接粘贴然后一起执行。 cat > /etc/apt/sources.list << EOF deb https://deb.debian.org/debian/ bookworm main contrib non-free non-free-firmware deb-src https://deb.debian.org/debian/ bookworm main contrib non-free non-free-firmware deb https://deb.debian.org/debian/ bookworm-updates main contrib non-free non-free-firmware deb-src https://deb.debian.org/debian/ bookworm-updates main contrib non-free non-free-firmware deb https://deb.debian.org/debian/ bookworm-backports main contrib non-free non-free-firmware deb-src https://deb.debian.org/debian/ bookworm-backports main contrib non-free non-free-firmware deb https://deb.debian.org/debian-security/ bookworm-security main contrib non-free non-free-firmware deb-src https://deb.debian.org/debian-security/ bookworm-security main contrib non-free non-free-firmware EOF 剩余 8 行代码 展开剩余代码 2、替换为清华源

© 2026 好站网HaoZhan.wang 1.5 版权所有

苏ICP备19065220号-4 萌ICP备20269980号 本站数据 版本历史 关于本站