你是否曾经为自己写的代码而感到懊恼?你是否想过如何才能写出高质量代码?那就不要错过这个话题!在这里,我们可以讨论什么是高质量代码,如何写出高质量代码等问题。无论你是初学者还是资深开发人员,都可以在这个话题下进行分享,汲取灵感和知识,共同提高自己的编程水平和工作效率。
一、 前言
低质量代码最可怕,
易生错,难维护。
BUG频发,影响大,
效率低,不规范,
编程难,思路乱,
高质量,从头做,
一点都不难。
二、高质量代码的特征
以下是一些高质量代码的特征:
-
可读性强:高质量代码应该易于阅读和理解,采用清晰的命名、注释和缩进,以及结构化的代码布局,可以使代码更加易于理解。
-
可维护性强:高质量代码应该易于修改和扩展,采用模块化的设计和代码重构,可以使代码更加易于维护。
-
可测试性强:高质量代码应该易于测试和调试,采用单元测试和集成测试等测试技术,可以使代码更加易于测试和调试。
-
安全性高:高质量代码应该具有良好的安全性,采用安全编码技术和安全测试技术,可以使代码更加安全可靠。
-
性能优良:高质量代码应该具有较好的性能,采用优化算法和数据结构,可以使代码更加高效。
-
代码规范:高质量代码应该符合代码规范,采用一致的代码风格和规范的编码习惯,可以使代码更加易于理解和维护。
-
可重用性高:高质量代码应该具有较好的可重用性,采用模块化设计和面向对象编程等技术,可以使代码更加易于重用。
三、编程实践
这是一个使用Tampermonkey(或Greasemonkey)脚本在网页中自动下载PDF文件的代码。具体来说,此脚本会监听 https://copyright.lib.*..**/pdfindex.jsp 页面。
代码中的注释已经很详细地解释了脚本中各个部分的功能和作用,这里简要介绍一下:
- 脚本首先等待页面加载完毕,以便获得结题报告窗口的DOM元素。
- 脚本在结题报告窗口下面插入一个按钮,用于启动PDF下载。
- 当用户点击下载按钮时,脚本会自动遍历所有PDF页面并将其转换为一个单独的PDF文件。
- PDF的每一页都是以图片的形式保存的,脚本会首先获取图片链接,然后使用jQuery的$.get()方法获取图片数据。这个数据在服务器端被压缩成了base64编码,并通过JSON格式返回给客户端。
- 然后,脚本会将图片数据转换为Blob格式,并通过使用新的Image对象将其加载到页面中。这一步骤将确保jsPDF.addImage()方法只被调用一次,因为该方法需要从图片URL中获取图片数据。
- 然后,脚本将Image对象添加到jsPDF中,以便在所有页面都添加完成后将它们保存为一个PDF文件。
总体来说,这个脚本使用了多种技术和工具,包括了异步函数、Promise、MutationObserver、jQuery和jsPDF。这使得脚本在处理多个页面和图片下载时非常有效和可靠。
四、 实际代码
// ==UserScript==
// @name pdf_downloader
// @namespace https://blog.rhilip.info/
// @version 1.0
// @description 下载pdf
// @author DR-ZF-
// @match https://copyright.lib.****.***.**/pdfindex.jsp*
// @require https://unpkg.com/[email protected]/dist/jspdf.umd.min.js
// @require https://unpkg.com/[email protected]/dist/jquery.js
// ==/UserScript==
/* globals $, jspdf */
// 参考油猴NSFC_conclusion_downloader by Rhilip,进行改写
// Copy and edit from https://stackoverflow.com/a/61511955
function waitForElm(selector) {
return new Promise(resolve => {
if ($(selector).length > 0) {
return resolve($(selector));
}
const observer = new MutationObserver(mutations => {
if ($(selector).length > 0) {
resolve($(selector));
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
(async function() {
'use strict';
// 等待页面加载
const conclusionReportTab = await waitForElm('#tool_Zoom');
// 准备交互按钮,并插入到页面中
const downloadBtn = $('<button type="button" class="el-button el-button--default el-button--medium is-round">下载全文</button>');
conclusionReportTab.after(downloadBtn);
// 点击交互按钮时需要开始下载操作
downloadBtn.click(async () => {
downloadBtn.prop('disabled', true).addClass('is-disabled');
if (!/无页面/.test(conclusionReportTab.text())) {
// 获得项目信息
const urlParams = new URLSearchParams(location.search);
const dependUintID = urlParams.get('fid');
// 准备需要的PDF文件,并删除初始页
const doc = new jspdf.jsPDF();
doc.deletePage(1);
doc.setDocumentProperties({
title: 'pdf_downloader',
subject: location.href,
creator: 'pdf_downloader'
});
// 核心下载方法
const image = new Image();
for (let i=1;;i++) {
downloadBtn.text(`正在下载第 ${
i} 页`);
// 获得图片链接
const response = await $.get('/jumpServlet?', {
page: i, fid: dependUintID });
console.log(response);
if (!response){
break; // 此处应该明确为false才break
}
const jsonResponse = JSON.parse(response);
const {
list } = jsonResponse;
let firstSrc, firstId
if (Array.isArray(list) && list.length > 0) {
const [firstItem] = list;
if (firstItem.src && firstItem.id) {
firstSrc = firstItem.src;
firstId = firstItem.id;
} else {
console.error(`Unexpected response data. Missing 'src' or 'id' property. Response: ${
JSON.stringify(response)}`);
}
} else {
console.error(`Unexpected response data. 'list' property not found or empty array. Response: ${
JSON.stringify(response)}`);
}
// 获得Blob形式的imageData,这样可以防止image.src和jsPDF.addImage会产生两次图片请求,浪费带宽
// (实际变成了一次请求服务器和两次请求本地blob)
try {
const imageDataAsBlob = await $.ajax({
url: firstSrc,
method: 'GET',
xhrFields: {
responseType: 'blob'}
});
// 加载图片并获得图片的 width, height 属性
image.src = URL.createObjectURL(imageDataAsBlob);
await image.decode();
// 将图片添加进PDF中
doc.addPage([image.width, image.height], image.width < image.height ? 'p' : 'l');
doc.addImage(image, "PNG", 0, 0, image.width, image.height);
} catch (e) {
break; // 如果中间有任何失败,则直接break
}
}
// 我们并没法 await 保存过程,所以直接显示下载完成就好,浏览器处理好会自动显示下载文件
doc.save(`PDF下载.pdf`);
downloadBtn.text('下载完成');
}
});
})();
五、 总结
这段代码的主要功能是从网页上下载 PDF 文件。以下是其中的优秀点:
-
使用了异步函数和 Promise:代码使用了
async
函数和Promise
对象,使得在等待页面加载和图片下载等耗时操作时,代码可以继续执行其他任务,从而避免了页面阻塞或卡顿的情况。 -
使用了 MutationObserver:使用 MutationObserver 监听 DOM 树变化,当目标元素出现时再执行后续代码,可以提高页面加载速度和性能,同时避免了因页面加载慢而导致的代码执行错误。
-
代码可读性高:变量和函数名都具有语义化,注释清晰明了,代码结构清晰简洁,易于理解和维护。
-
使用了 jQuery 和 jspdf 库:代码使用了 jQuery 库简化了 DOM 操作,同时使用了 jspdf 库方便地生成 PDF 文件。
-
错误处理得当:代码对 HTTP 请求和其他异常情况进行了正确的处理,同时在出现异常时及时终止程序并给出相应的错误提示,防止代码出现严重的运行错误。