跳到主要内容

软连接与硬连接的区别

· 阅读需 2 分钟
素明诚
Full stack development

假设我们有一个文件夹,里面有一个名为“原始文件.txt”的文件。

  1. 硬链接 (Hard Link):
  • 创建硬链接就像在文件夹中为“原始文件.txt”创建了一个镜像或克隆,我们可以称其为“硬链接文件.txt”。
  • 现在,“原始文件.txt”和“硬链接文件.txt”都指向同一块数据。无论你通过哪个文件来修改数据,另一个文件看到的数据也会改变,因为它们共享同一块数据。
  • 如果你删除了“原始文件.txt”,“硬链接文件.txt”仍然存在,并且包含所有的数据。删除其中一个不会影响另一个。
  1. 软链接 (Soft Link 或 Symbolic Link):
  • 创建软链接就像创建了一个指向“原始文件.txt”的快捷方式,我们可以称其为“软链接文件.txt”。
  • “软链接文件.txt”不包含任何实际的数据,它只是指向“原始文件.txt”的一个指针或快捷方式。
  • 如果你删除了“原始文件.txt”,“软链接文件.txt”会变成一个失效的快捷方式,因为它指向的文件已经不存在了。

Axios 下载

· 阅读需 4 分钟
素明诚
Full stack development

get请求,下载为例。一般项目中的axios都是经过封装的,所以你需要关注有没有添加responseType这是关键。

export const exportQuotation = (id: string | number) => {
return request({
responseType: 'blob', // 二进制流
url: '/dcomsp-sales/excelExport/v1/export',
method: 'get',
params: {
id,
},
})
}

通用下载函数

/**
* 下载文件
* @param fileData
* @param fileType
* @param fileName
*/
export function downloadFile(
fileData: any,
fileType: string,
fileName: string
): boolean {
let blob: Blob
let success = true // 假设初始状态为成功

// 创建 Blob 对象
try {
if (fileData instanceof Blob) {
blob = fileData
} else {
blob = new Blob([fileData], { type: fileType })
}
} catch (error) {
console.error('Error creating the Blob:', error)
return false
}

const a = document.createElement('a')
const url = window.URL.createObjectURL(blob)

// 下载后释放URL资源和清理逻辑
const cleanup = () => {
window.URL.revokeObjectURL(url)
document.body.removeChild(a)
}

try {
// 创建一个隐藏的可点击的a标签,然后模拟点击进行下载
a.href = url
a.download = fileName
a.style.display = 'none'
document.body.appendChild(a)
a.click()
} catch (error) {
console.error('Error triggering the download:', error)
success = false // 下载失败时,设置 success 为 false
} finally {
// 使用setTimeout确保清理逻辑在下载触发后立即执行
setTimeout(cleanup, 0)
}

return success // 返回下载的状态
}

使用,通过id进行下载

const download = async (id: string | number) => {
// exportQuotation 是我的接口,拿到流数据传递给下载函数
const exportData = await exportQuotation(id)
// 创建下载
const res = downloadFile(
exportData.data,
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
`${id}-${Date.now()}.xlsx`
)
if (res) {
ElMessage.success('下载成功')
} else {
ElMessage.error('下载失败')
}
}

常用下载格式

后缀类型格式
aacAAC audioaudio/aac
.abwAbiWord documentapplication/x-abiword
.arcArchive document (multiple files embedded)application/x-freearc
.aviAVI: Audio Video Interleavevideo/x-msvideo
.azwAmazon Kindle eBook formatapplication/vnd.amazon.ebook
.binAny kind of binary dataapplication/octet-stream
.bmpWindows OS/2 Bitmap Graphicsimage/bmp
.bzBZip archiveapplication/x-bzip
.bz2BZip2 archiveapplication/x-bzip2
.cshC-Shell scriptapplication/x-csh
.cssCascading Style Sheets (CSS)text/css
.csvComma-separated values (CSV)text/csv
.docMicrosoft Wordapplication/msword
.docxMicrosoft Word (OpenXML)application/vnd.openxmlformats-officedocument.wordprocessingml.document
.eotMS Embedded OpenType fontsapplication/vnd.ms-fontobject
.epubElectronic publication (EPUB)application/epub+zip
.gifGraphics Interchange Format (GIF)image/gif
.htm .htmlHyperText Markup Language (HTML)text/html
.icoIcon formatimage/vnd.microsoft.icon
.icsiCalendar formattext/calendar
.jarJava Archive (JAR)application/java-archive
.jpeg.jpgJPEG imagesimage/jpeg
.jsJavaScripttext/javascript
.jsonJSON formatapplication/json
.jsonldJSON-LD formatapplication/ld+json
.mid.midiMusical Instrument Digital Interface (MIDI)audio/midi audio/x-midi
.mjsJavaScript moduletext/javascript
.mp3MP3 audioaudio/mpeg
.mpegMPEG Videovideo/mpeg
.mpkgApple Installer Packageapplication/vnd.apple.installer+xml
.odpOpenDocument presentation documentapplication/vnd.oasis.opendocument.presentation
.odsOpenDocument spreadsheet documentapplication/vnd.oasis.opendocument.spreadsheet
.odtOpenDocument text documentapplication/vnd.oasis.opendocument.text
.ogaOGG audioaudio/ogg
.ogvOGG videovideo/ogg
.ogxOGGapplication/ogg
.otfOpenType fontfont/otf
.pngPortable Network Graphicsimage/png
.pdfAdobe Portable Document Format (PDF)application/pdf
.pptMicrosoft PowerPointapplication/vnd.ms-powerpoint
.pptxMicrosoft PowerPoint (OpenXML)application/vnd.openxmlformats-officedocument.presentationml.presentation
.rarRAR archiveapplication/x-rar-compressed
.rtfRich Text Format (RTF)application/rtf
.shBourne shell scriptapplication/x-sh
.svgScalable Vector Graphics (SVG)image/svg+xml
.swfSmall web format (SWF) or Adobe Flash documentapplication/x-shockwave-flash
.tarTape Archive (TAR)application/x-tar
.tif .tiffTagged Image File Format (TIFF)image/tiff
.ttfTrueType Fontfont/ttf
.txtText, (generally ASCII or ISO 8859-n)text/plain
.vsdMicrosoft Visioapplication/vnd.visio
.wavWaveform Audio Formataudio/wav
.webaWEBM audioaudio/webm
.webmWEBM videovideo/webm
.webpWEBP imageimage/webp
.woffWeb Open Font Format (WOFF)font/woff
.woff2Web Open Font Format (WOFF)font/woff2
.xhtmlXHTMLapplication/xhtml+xml
.xlsMicrosoft Excelapplication/vnd.ms-excel
.xlsxMicrosoft Excel (OpenXML)application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.xmlXMLapplication/xml 代码对普通用户来说不可读 (RFC 3023, section 3)text/xml 代码对普通用户来说可读 (RFC 3023, section 3)
.xulXULapplication/vnd.mozilla.xul+xml
.zipZIP archiveapplication/zip
.3gp3GPP audio/video containervideo/3gppaudio/3gpp(若不含视频)
.3g23GPP2 audio/video containervideo/3gpp2audio/3gpp2(若不含视频)
.7z7-zip archiveapplication/x-7z-compressed

JavaScript 中的 super 关键字解析

· 阅读需 2 分钟
素明诚
Full stack development

super 关键字的核心目的

super 是 JavaScript 中用于在子类(或派生类)中调用其父类(或超类)的方法的关键字。在 ES6 的类和继承的上下文中,它尤其有用。

在构造函数中

当你在一个派生类中定义一个构造函数时,你必须首先调用 super() 来确保父类(基类)的构造函数被正确地执行。这样做的目的是:

  • 初始化this绑定:确保对象(由 this 表示)被正确初始化。
class Parent {
constructor() {
this.name = "Parent";
}
}

class Child extends Parent {
constructor() {
super(); // 这会执行 Parent 的构造函数,并初始化 this.name 为 "Parent"
this.type = "Child"; // 现在可以安全地使用 this
}
}

const obj = new Child();
console.log(obj.name); // 输出 "Parent"
console.log(obj.type); // 输出 "Child"

  • 确保继承属性和方法:派生类会继承父类上定义的所有属性和方法。这确保了你可以在子类实例上使用父类定义的所有属性和方法。

在派生类的方法中

当你想在子类的一个方法内调用父类的同名方法时,你可以使用 super 关键字。这使得继承更加简单,并允许子类在必要时重写或扩展其父类的功能。

class Parent {
greet() {
console.log("Hello from Parent");
}
}

class Child extends Parent {
greet() {
super.greet(); // 调用父类的 greet 方法
console.log("Hello from Child");
}
}

const obj = new Child();
obj.greet();
// 输出:
// Hello from Parent
// Hello from Child

super 的行为及其重要性

  • 继承和原型链的基础:在 JavaScript 的面向对象编程中,继承允许我们在一个类(子类)上基于另一个类(父类)创建新的属性和方法。super 关键字确保在创建子类的实例时,父类的构造函数首先被调用,从而确保所有的属性和方法都可以被子类实例使用。
  • this的初始化:在派生类中,this 在调用 super() 之前不能使用。如果在调用 super() 之前尝试使用 this,JavaScript 会抛出一个错误。这确保了对象的正确初始化和继承链的完整性。

clsx 库如何使用

· 阅读需 1 分钟
素明诚
Full stack development

示例

假设我们有一个用户卡片组件,它可以显示为正常、高亮或禁用状态。基于这些状态,卡片的样式类将会改变。

使用 clsx,我们可以轻松地构建这个组件的类名。

import clsx from 'clsx';

function UserCard({ highlighted, disabled, username }) {
const className = clsx('user-card', {
'highlighted': highlighted,
'disabled': disabled,
'normal': !highlighted && !disabled
});

return <div className={className}>{username}</div>;
}

在上述例子中:

  1. 如果 highlighted 属性为 true,类名将包括 highlighted
  2. 如果 disabled 属性为 true,类名将包括 disabled
  3. 如果都不是 highlighteddisabled,类名将包括 normal

所以,当你这样使用组件:

<UserCard highlighted={true} username="Alice" />

生成的 HTML 将是:

<div class="user-card highlighted">Alice</div>

通过 clsx,我们可以有条件地、清晰地和简洁地构建类名,而不需要冗长或重复的代码逻辑。

而且该库很小,兼容性也很好,是完美的classnames 替代品。

这个库在 React 社区中很受欢迎,因为 React 经常需要动态地构建类名

v-model 和 v-modellazy 的区别

· 阅读需 2 分钟
素明诚
Full stack development

v-model: 默认情况下,v-model 在每次输入事件 (input) 发生时都会更新数据。这意味着当用户在输入框中每次按键时,数据都会实时更新。

v-model.lazy: .lazy 修饰符会更改 v-model 的行为,使其在 change 事件而不是 input 事件上同步。这意味着数据只有在输入框失去焦点或用户按下回车键时才会更新,而不是用户每次键入字符时。

区别很简单,但是为什么非要有个lazy呢?

lazy 背后的考虑

  1. 性能考虑: 在某些情况下,实时处理每一个 input 事件可能是性能昂贵的。例如,如果输入绑定到一个复杂的计算属性或触发一系列的 DOM 变动,每次键入都会导致这些操作执行,这可能导致不必要的计算和渲染。通过使用 .lazy,你可以确保只在必要时执行这些操作,例如当用户完成输入并移出输入框时。
  2. 用户体验: 在某些应用中,你可能不希望在用户还在输入时即刻给出反馈(例如,实时的表单验证)。有时,直到用户完成输入并移出输入框,你才开始显示验证消息,这样可以避免中途打断用户。
  3. 控制: Vue 提供了一系列的修饰符(如 .lazy, .number, .trim),这样开发者可以根据需要精确地控制输入的行为。不是所有应用都需要实时的输入同步,.lazy 修饰符为那些需要更细粒度控制的场景提供了选择。
  4. 与其他库/框架的一致性: 其他前端框架和库中的双向数据绑定机制通常不会在每次键入时都更新数据,而是在某些特定事件(如失去焦点)时才更新。.lazy 修饰符为那些从其他框架迁移到 Vue 的开发者提供了熟悉的行为。

为什么 JavaScript 的0 和 -0 是相等的

· 阅读需 1 分钟
素明诚
Full stack development

+0 和 -0 这两个是特殊情况,虽然他们两个相等,但是他们两个有特殊的意义才这么设计的

ffd5be6bfa26e251140129416440fe45## 为什么需要两种零的表示:

  1. 表示特定的数学极限: 如之前所述,考虑函数 1/x。当 x 从正数接近零时,结果趋向于正无穷。而当 x 从负数接近零时,结果趋向于负无穷。这种区分允许计算机模拟这些数学上的极限行为。
  2. 数值稳定性: 在某些数值算法中,保留 +0-0 的区分可以提供更准确和稳定的结果。
  3. 位级表示: 在 IEEE 754 标准中,+0-0 在位级上有所不同,尤其是在符号位上。这种区别使得我们可以在不牺牲其他功能的情况下,明确地表示和区分这两种零。

虽然这种比较,从人直觉的角度很不一致,但是这么设计的目标是为了解决数学和数值计算中的特定问题

Maven 依赖是什么

· 阅读需 1 分钟
素明诚
Full stack development

拿 ruoyi 的依赖项举例子

io.springfox:springfox-boot-starter:3.0.0
io.swagger:swagger-models:1.6.2
com.mysql:mysql-connector-j:8.0.33
com.ruoyi:ruoyi-framework:3.8.6
com.ruoyi:ruoyi-quartz:3.8.6
com.ruoyi:ruoyi-generator:3.8.6

io.springfox:springfox-boot-starter:3.0.0

  • Springfox 是一个生成 Swagger API 文档的库。这个特定的依赖是 Spring Boot 的起步依赖,它允许你轻松地在 Spring Boot 项目中集成 Springfox。

io.swagger:swagger-models:1.6.2

  • 这是 Swagger 的模型库,提供了定义和操作 Swagger API 文档的类。

com.mysql:mysql-connector-j:8.0.33

  • 这是 MySQL 的 JDBC 驱动,允许 Java 程序连接到 MySQL 数据库。

com.ruoyi:ruoyi-framework:3.8.6

  • 这是 RuoYi 项目的框架部分。RuoYi 是一个基于 Spring Boot 的快速开发框架。

com.ruoyi:ruoyi-quartz:3.8.6

  • 这是 RuoYi 项目的 Quartz 部分,用于任务调度。

com.ruoyi:ruoyi-generator:3.8.6

  • 这是 RuoYi 项目的代码生成器部分,帮助你快速生成代码模板。

org.springframework.boot:spring-boot-devtools:2.5.15

  • 这是 Spring Boot 的开发工具库。它提供了一些在开发时非常有用的功能,如自动重启应用程序以查看更改、热交换等。

添加依赖

为了在 Maven 项目中添加一个依赖,你需要在 pom.xml 文件中的 <dependencies> 部分添加一个 <dependency> 元素。

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.10</version>
</dependency>

这些依赖就是你当前项目需要的库

为什么Java的int类型一定是4字节

· 阅读需 2 分钟
素明诚
Full stack development

在早期的计算机系统中,数据总线和地址总线的宽度通常是 8 位、16 位或 32 位。当 32 位的微处理器(如 Intel 的 80386)变得流行时,它们天然地支持 32 位宽的数据操作。为了最大限度地提高效率,许多数据类型,特别是整数,都被设置为这些处理器的自然字大小,即 32 位或 4 字节。

为什么 Java 的 int 类型,一定是 4 字节呢

Java 的设计哲学是“一次编写,到处运行”(write once, run anywhere,简称 WORA)。为了实现这个目标,Java 需要确保其基本数据类型在所有平台上具有相同的大小和行为。这样,无论 Java 程序在哪里运行,它都可以期望相同的数据类型行为。

为了确保 Java 程序在所有平台上都有一致的行为,Java 语言规范明确指定了基本数据类型的大小。与 C 和 C++ 等语言不同,这些语言的数据类型大小可能会根据平台和编译器而变化,Java 的 int 总是 32 位,无论在哪个平台上。

在 Java 诞生的时代(1990 年代中期),许多流行的计算机都使用 32 位的架构。定义 int 为 32 位使得 Java 可以在这些机器上高效运行,同时保持了足够的整数范围(从-2,147,483,648 到 2,147,483,647)来满足大多数应用的需求。所以 Java 的 int 类型一定是 4 字节。

padStart 和 padEnd

· 阅读需 2 分钟
素明诚
Full stack development

padStartpadEnd 是 ES8 (ES2017) 中引入的两个字符串原型方法。它们用于在字符串的开头或结尾填充指定的字符,直到字符串达到给定的长度。这两个方法常用于格式化输出。

padStartpadEnd 是 ES8 (ES2017) 中引入的两个字符串原型方法。它们用于在字符串的开头或结尾填充指定的字符,直到字符串达到给定的长度。这两个方法常用于格式化输出。

1.padStart(targetLength [, padString])

这个方法会在当前字符串的开头填充字符,直到字符串达到 targetLength 的长度。如果填充的字符串 padString 没有指定,那么默认使用空格进行填充。

示例:

'5'.padStart(2, '0');       // '05'
'123'.padStart(5, '0'); // '00123'
'abc'.padStart(6, '01'); // '01abc'
'abcdef'.padStart(4, '0'); // 'abcdef', 因为原字符串已经超过或等于目标长度,所以返回原字符串

2.padEnd(targetLength [, padString])

这个方法会在当前字符串的结尾填充字符,直到字符串达到 targetLength 的长度。如果填充的字符串 padString 没有指定,那么默认使用空格进行填充。

示例:

'5'.padEnd(2, '0');       // '50'
'123'.padEnd(5, '0'); // '12300'
'abc'.padEnd(6, '01'); // 'abc010'
'abcdef'.padEnd(4, '0'); // 'abcdef', 因为原字符串已经超过或等于目标长度,所以返回原字符串

这两个方法在处理数字、日期等格式时非常有用,它们可以确保输出的字符串具有统一的长度和格式。

实现字符串大数相加

· 阅读需 1 分钟
素明诚
Full stack development

流程如下:

  1. 初始化进位 carry 为 0。
  2. 使用 padStart 方法确保两个数字字符串长度相同。
  3. 从低位到高位(也就是从字符串的最后一位到第一位)进行遍历,并执行加法。
  4. 计算当前位的结果,如果大于 9 则产生进位。
  5. 如果遍历完所有位后仍有进位,则添加到结果的最前面。
function bigNumberAddition(num1, num2) {
let carry = 0 // 初始化进位
let result = []

// 使两个数字字符串长度相同
const length = Math.max(num1.length, num2.length)
num1 = num1.padStart(length, '0')
num2 = num2.padStart(length, '0')

// 从低位到高位进行相加
for (let i = length - 1; i >= 0; i--) {
let tempSum = parseInt(num1[i]) + parseInt(num2[i]) + carry
result.unshift(tempSum % 10) // 取模得到当前位
console.log(result)
carry = Math.floor(tempSum / 10) // 取整得到进位
}

// 如果最后仍有进位,添加到结果前面
if (carry) {
result.unshift(carry)
}

return result.join('')
}

// 测试
const numA = '99'
const numB = '99'
const sum = bigNumberAddition(numA, numB)
console.log(sum)