惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hacker News - Newest:
Hacker News - Newest: "LLM"
AI
AI
T
Troy Hunt's Blog
GbyAI
GbyAI
H
Hacker News: Front Page
SecWiki News
SecWiki News
V2EX - 技术
V2EX - 技术
A
About on SuperTechFans
人人都是产品经理
人人都是产品经理
Hacker News: Ask HN
Hacker News: Ask HN
S
Secure Thoughts
Last Week in AI
Last Week in AI
MyScale Blog
MyScale Blog
L
LINUX DO - 最新话题
C
CERT Recently Published Vulnerability Notes
C
Cyber Attacks, Cyber Crime and Cyber Security
O
OpenAI News
S
SegmentFault 最新的问题
Y
Y Combinator Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Cloudflare Blog
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
美团技术团队
Google DeepMind News
Google DeepMind News
Simon Willison's Weblog
Simon Willison's Weblog
Know Your Adversary
Know Your Adversary
K
Kaspersky official blog
T
The Exploit Database - CXSecurity.com
S
Securelist
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
B
Blog RSS Feed
L
Lohrmann on Cybersecurity
Spread Privacy
Spread Privacy
博客园 - 司徒正美
Stack Overflow Blog
Stack Overflow Blog
博客园 - Franky
The GitHub Blog
The GitHub Blog
B
Blog
F
Fortinet All Blogs
I
InfoQ
C
Check Point Blog
Webroot Blog
Webroot Blog
博客园 - 叶小钗
P
Privacy International News Feed
Latest news
Latest news
Forbes - Security
Forbes - Security
博客园 - 三生石上(FineUI控件)
N
News | PayPal Newsroom

atpX

自建 S3 兼容对象存储服务 Garage 听风的歌 偶尔是深夜就好了 新玩具 AirPods Pro 3 公交车上的时间 从 WHOIS 到 RDAP 通过 WireGuard 访问 NFS 共享文件 自建音乐串流服务探索 我停止了探索 Fediverse 互联网背景噪音 我的数字生活降级 小城与确定性的墙 一只特立独行的猪 Fediverse 与社交 承认的勇气 mediaX - 轻量书影音记录管理工具 我的博客写作流程 网站新增 Misc 页面 擅长对线的鲍勃 在 Chroot 环境下使用 Rsync 同步 再见 JavaScript 当我玩博客时我在玩什么 为什么我的博客没有友链页面 小熊猫与大熊猫 (HDR 照片测试) 是时候为网站开启 HTTP/3 支持了吗 使用 AdGuard Home 搭建自用 DoH 服务 谈谈读书与消遣 2023 年终总结 西安两日游 从 AirPods「升级」到 EarPods Docker 搭建去中心化的微博客平台 Mastodon 聊聊 iPhone 15 Pro 使用感受 能否将 TOTP 二次验证存放在密码管理器里 认识史蒂夫·乔布斯 与“锤哥”克里斯·海姆斯沃斯一起养生 逃离爆炸的信息 你想活出怎样的人生 重温《龙珠》动画 为什么关闭评论 从域名注册商到 DNS 服务,找到自己的组合 如何提高用户网页阅读体验 从 Typecho 迁移到 Hugo 个人博客的最终归宿是静态网站吗 关于我的小破站折腾速度优化这件事 记一次家庭网络折腾 贴一些 SmokePing 记录 我的域名邮箱选择 不存在的语录 Typecho 评论验证插件 CaptchaPlus 谈谈写字这件事 新的网站 Logo 探索 曲线解决 Typecho 图片占位抖动问题 自建 vaultwarden / bitwarden_rs 密码管理器 使用 Plausible 自建网站流量统计分析工具 如何优雅地徒手剥开火腿肠 简单的集中隔离生活记录 新玩具 M2 MacBook Air 使用 acme.sh 申请 Google 公共证书 Docker 搭建去中心化的微博客平台 Misskey 搭建 Lsky Pro 兰空图床 折腾树莓派系统的一天 我的吉卜力之旅 使用 Uptime Kuma 自建服务器/网站在线率监控 Spartan Host Review - Best Hosting & Service Support Debian 下 Nginx 配合 Fail2Ban 减少恶意扫描和攻击 升级 Typecho 到最新开发版本 v1.2.0 Linux rm 命令详解 Debian 系统安装 Docker 教程 BandwagonHost - Reliable Blog and Business VPS Hosting 使用 Isso 为 Hugo/Hexo 等静态网站添加评论功能 推荐 5 款免费开源的网站流量分析统计工具 搭建 Shynet 网站流量统计分析工具 聊聊 DDoS 攻击那些事 Hugo 使用 Fancybox 实现图片灯箱/放大功能 从 Debian 10 升级到 Debian 11 教程 使用 Umami 自建网站流量统计分析工具 Linux 一次执行多个命令的 3 种方式 Typecho 修改永久链接后旧链接 301 跳转到新链接 从不同需求推荐几家稳定可靠的 VPS 服务商 Build EchoIP service with Docker 浅谈一下这些年折腾过的 VPS 单线复用解决一根网线同时 IPTV 和宽带问题 不完全吃灰的树莓派 开发一个自己的 Telegram Bot 使用 Nginx 实现 TCP 四层反向代理 画画真难 我读村上春树 使用 acme.sh 自动签发和更新证书 卡拉马佐夫式悲剧 安装 File Browser 轻量网盘工具 新玩具树莓派到手 Linux 定时自动备份数据到 OneDrive/Google Drive 记一次服务器崩溃 使用 rsync 同步文件 短信的消失 幻灭的艺术家 新玩具戴尔 S2721DGF 开箱 蝴蝶 用 Docker 整合 SeafilePro 搭建私人云盘 搭建自己的 kms 服务器
我选择了放弃 jQuery
ATP · 2023-02-10 · via atpX

偶然看到 Github 的一篇文章 Removing jQuery from GitHub.com frontend,虽然看不懂,但让我大受震撼,然后陷入了思考。

jQuery 当然是一个很棒的 JavaScript 库,简洁快速,可以用更少的代码做更多的事情,作为一个时代的王者,无需多言。只是在大家都在转向 React, Vue 等框架或者库的今天略显落寞,新项目使用的人越来越少,但由于强大的底蕴,并不妨碍其一骑绝尘的占有率。

就我的网站来说,当时有不少功能,秉着能实现就行的想法,都是直接在网上找解决方案,自然而然的引入了 jQuery。等到如今再看,似乎并没有用到太多,虽然 jQuery 压缩后也就 30kb 左右的大小,但我用的估计不到 3kb,同时简单看了下,很多 jQuery 方法使用原生 JS 实现 (You might not need jQuery) 也并不复杂,同时能获得更好的性能,放弃 jQuery 也是一件自然而然的事情。

开始折腾

虽然上一次手撸 JS 还是在大学时的网页设计课上,但为了文艺复兴,总得挑战一下。一趟操作下来的体验就是面向谷歌编程,其中用得最多的是 console.log(),好在本站用到的 jQuery 的地方不是太多,跌跌撞撞用了几天完成了重写,最后注释掉 jQuery 引入的时候很爽。至此,终于可以宣布我的小破站于 2023 年 2 月 10 日已经完全移除了 jQuery 依赖。

使用 Fetch 替换 Ajax

网站首页翻页用到了 Ajax 功能,获取下一页数据局部加载到当前页面,为了干掉 jQuery,第一件事就是替换掉 Ajax,Fetch API 就是浏览器提供的用来代替 jQuery.ajax() 请求的接口,而且更加强大。一个简单的 Ajax 和 fetch 请求对比如下:

$.ajax({
  type: 'POST',
  url: url,
  data: data,
  dataType: dataType,
  success: function() {},
  error: function() {}
});
fetch(url)
  .then((response) => response.json())
  .then((data) => console.log(data));

Fetch API 返回的 Promise 不会返回 HTTP 错误,方便处理各种情况,只会在网络错误或者请求未完成的情况下 reject,上面的请求也可以使用 await 语法改写:

async function getJSON() {
  try {
    let response = await fetch(url);
    return await response.json();
  } catch (error) {
    console.log('Request Failed', error);
  }
}

具体的使用可以查看 MDN 文档。

目前使用的灯箱工具 Fancybox 也用到了 jQuery,需要换一个使用 Vanilla JS 的,对比体验下面几个轻量工具,感觉都不错,结合自己的需求最后选择了使用 PhotoSwipe

PhotoSwipe 的使用很简单,支持动态加载,并且很多功能和样式都可以自定义。唯一的缺点是需要预先写入图片的宽度和高度到 <a> 标签里,所以一开始准备用的是灯箱工具是 Simple Lightbox,刚好在我为了解决另一个图片占位抖动问题的时候给每张图片都添加了宽度和高度属性,顺手就换过来了。虽然 PhotoSwipe 的 JS 文件要比 Simple Lightbox 大上一些,但功能更丰富,动画也更加流畅,体验下来很不错。

其它

还有一些用到 jQuery 小功能就不提了,这里只简单谈谈前几天刚实现的文章目录随页面滚动高亮显示。例如本站用的方法使用 jQuery 写的话比较简单:

/* 
* Highlight ToC on scrolling
* Written by ATP on 2023-02-05
* Website: https://atpx.com 
 */
$(".post-content h2, h3").each(function (i, elements) {
    if (scroH > $(this).offset().top - 30 && scroH < $(this).offset().top + $(this).height() - 30) {
        $('.catalog a').removeClass('active');
        $('.catalog a[href="#' + $(this).attr('id') + '"]').addClass('active');
    }
    if (i == $(".post-content h2, h3").length - 1 && scroH > $(".post-end").offset().top) {
        $('.catalog a').removeClass('active');
    }
});

如果换成原生 JS,花了半天改成了如下代码:

/* 
* Highlight ToC on scrolling
* Updated on 2023-02-10 to remove jQuery dependency
* Website: https://atpx.com 
 */
function offSet(element) {
    var rect = document.querySelector(element).getBoundingClientRect();
    var offset = {
        top: rect.top + window.scrollY, 
        left: rect.left + window.scrollX, 
    };
    return offset;
}

window.addEventListener("scroll", function() {
    const postContent = document.querySelector(".post-content"),
        headings = [];
    if(postContent) {
        headings = postContent.querySelectorAll("h2, h3");
        headings.forEach(heading => {
            let elementId = heading.getAttribute("id"),
                anchor = "#" + elementId;
            if (pageScroll > offSet(anchor).top - 30 && pageScroll < offSet(anchor).top + heading.clientHeight - 30) {
                document.querySelectorAll('.catalog a').forEach(a => {
                    a.classList.remove('active');
                });
                document.querySelector('.catalog a[href="' + anchor + '"]').classList.add('active');
            }
            if (pageScroll > offSet(".post-end").top) {
                document.querySelectorAll('.catalog a').forEach(a => {
                    a.classList.remove('active');
                });
            }
        });
    }
});

代码量增加了不少,可读性也差一些,但大橘为重🍊。写的过程中有个小问题,如果元素不存在,querySelectorAll() 返回的 NodeList 会存在 null 值,导致后面无法获取高度,让我 debug 了半天,需要先判断要选择的元素是否存在后再 querySelectorAll(),也可以用 Array.from()NodeList 转化为数组处理后再继续(如果需要兼容 IE 浏览器可以使用 Array.prototype.slice.call())。虽然实现了但感觉当前内容的判断逻辑还是有些不完善,以后有时间再优化下。

Updated on 2023-02-11

无聊在一台性能较差的虚拟机中测试时发现 scrollgetBoundingClientRect() 似乎会造成性能问题,延迟巨大并且页面卡顿,一番研究之后决定使用 Intersection Observer 替换。

/* 
* Highlight ToC on scrolling
* Updated on 2023-02-11 to replace scroll event with IntersectionObserver
* Website: https://atpx.com 
 */
let postContent = document.querySelector('.post-content');
if (postContent) {
    const ifActive = (headings) => {
        headings.forEach((heading) => {
            let elementId = heading.target.getAttribute('id'),
                anchor = '#' + elementId;
            if (heading.isIntersecting) {
                document.querySelectorAll('.catalog a').forEach(a => {
                    a.classList.remove('active');
                });
                if (elementId){
                    document.querySelector('.catalog a[href="' + anchor + '"]').classList.add('active');
                }
                // headingObserver.unobserve(heading.target);
            } 
        });
    }
    const endNode = document.querySelector('.post-end'),
        headings = postContent.querySelectorAll('h2, h3'),
        headingObserver = new IntersectionObserver(ifActive, {
        rootMargin: '0px 0px -70% 0px',
        threshold: 1
    });
    Array.from(headings, heading => headingObserver.observe(heading));
    headingObserver.observe(endNode);
}

但使用 IntersectionObserver API 之后对于是否高亮的逻辑判断我还是比较迷惑,不知道该如何判断标题所在整块 <div> 内容占当前视窗的百分比来决定是否高亮,只能判断标题 <h3>本身那一行是否出现或者占比,可能效果不是很好。我思考一番后直接放弃,选择用标题是否越过距离顶部 30% 的位置作为参考点来判断。此外还有一个小问题是 IE 浏览器不支持该方法,我也选择直接放弃兼容 IE 浏览器。不过好处是改写之后性能问题确实解决了,在低配虚拟机中也很流畅。

总结

写到这里,我并不是鼓励你放弃 jQuery,jQuery 在今天仍然是一个可靠的选择,正是因为经过时间的沉淀,你想实现的功能基本上都有大量的现成文档供你查询,可以帮你高效的解决不少麻烦。不过随着时代的发展,即使是目前流行的新 JS 库和框架早晚也会淘汰,甚至以后还需不需要程序员手写代码也说不定。就目前这个时间点来说,如果放弃 jQuery 对你的网站来说是一件利远大于弊的事情,有时间折腾一下也是不错的。