Gulp 任务
前言
任何导出的函数都将被注册到 gulp 的任务系统中。
每个 gulp
任务都是一个异步 JavaScript
函数 - 接受 错误优先回调
或
返回 流
、promise
、事件触发器
、子进程
或 可观察对象的
函数。
注意📢:
由于某些平台限制,不支持同步任务,尽管有一个非常漂亮的 alternative
(可替代的)。
私有任务与公有任务
任务可以被视为 公共 或 私有。
- 公共任务从
gulpfile
中导出,这允许它们通过 gulp 命令运行。 - 私有任务供内部使用,通常用作
series()
或parallel()
组合的一部分。
私有任务的外观和行为与任何其他任务类似,但终端用户无法独立执行它。
任务注册
gulpfile
导出
任何导出的函数都将被注册到 gulp 的任务系统中。
1 | function test(cb) { |
task API 注册
- 命令行
1 | gulp --task |
task()
函数
1 | async function dev() { |
过去,task()
用于将函数注册为任务。
虽然该 API 仍然可用,但导出应该是主要的注册机制,除非在导出不起作用的边缘情况下。
Gulp
引言
在
Gulp 4.x
中,任务可以是异步的,支持多种处理方式,包括Promise
、async/await
、Stream
等。
当任务完成时,必须通知 Gulp,否则任务会挂起,导致后续任务无法执行。
Gulp 是基于 Node.js 的任务运行器,使用 异步 操作来处理文件流。自动化并增强工作流程的工具包。
利用 Gulp 和 JavaScript 的灵活性来自动执行缓慢、重复的工作流,并将其组合成高效的构建管道。
安装
1 | # 安装 gulp 命令行工具 |
配置文件
示例
在项目根目录中创建一个名为
gulpfile.js
的文件。任何导出的函数都将被注册到 gulp 的任务系统中。1
2
3
4
5
6function defaultTask(cb) {
cb();
}
/* 默认任务导出 */
exports.default = defaultTask1
2# 执行默认任务
gulp注意📢: 将所有逻辑添加到 gulpfile 中,如果文件太大,可以将其重构为单独的文件。
每个任务都可以拆分成自己的文件,然后导入到 gulpfile 中进行组合。gulpfile
详解gulpfile
是项目目录下名为gulpfile.js
(或者首字母大写Gulpfile.js
)的文件,在运行gulp
命令时会被自动加载。- Node 的模块的解析功能允许将
gulpfile.js
文件替换为同样命名为gulpfile.js
的目录,
该目录中包含了一个名为index.js
的文件,该index.js
文件将被当作gulpfile.js
使用。 - 如果使用了转译器(
transpiler
),需要给文件夹和目录相应地命名。
配置文件转移
Node 的大多数新版本都支持 TypeScript 或 Babel 所提供的大多数功能,但
import
/export
语法除外。如果只需要改语法的话,重命名为
gulpfile.esm.js
并安装 esm 模块。当使用 TypeScript 或 Babel 编写 gulpfile 文件时,需要进行转译。
通过修改gulpfile.js
文件的扩展名来表明所用的编程语言并安装对应的转译模块。- 对于 TypeScript,重命名为
gulpfile.ts
并安装ts-node
模块。 - 对于 Babel,重命名为
gulpfile.babel.js
并安装@babel/register
模块。 - 使用 ESModule 语法,重命名为
gulpfile.esm.js
并安装esm
模块。
1
2
3
4
5
6
7
8# 安装 ts-node 模块
npm install --save-dev ts-node
# 安装 @babel/register 模块
npm install --save-dev @babel/register
# 安装 esm 模块
npm install --save-dev esm- 对于 TypeScript,重命名为
提交规范配置
引言
要实现 Git 提交规范配置,通常会结合使用 commitlint
配合 husky
来确保提交信息符合约定的格式。
避免不规范的提交信息进入代码库。不仅提高了代码的可维护性,还能使团队协作更顺畅。
安装依赖
需要在项目中安装 commitlint
和 husky
,这两个工具能够帮助实现提交信息的规范化。
@commitlint/cli
: 是 commitlint 的命令行工具。@commitlint/config-conventional
: 是一组常用的提交信息规范规则。husky
: 用于在 Git 钩子(如pre-commit
或commit-msg
)中添加钩子功能。
1 | # 安装 commitlint 和 husky |
配置 Commitlint
在项目根目录下创建一个 commitlint
配置文件,通常是 .commitlintrc.js
、commitlint.config.js
或 .commitlintrc.json
,用于定义提交规范。
1 | // commitlint.config.js |
@commitlint/config-conventional
常见规则包括:
type
必须是以下几种之一:feat
(新功能)fix
(修复)docs
(文档)style
(代码风格)refactor
(代码重构)perf
(性能优化)test
(增加测试)build
(构建工具相关)ci
(CI 配置相关)chore
(杂项任务)revert
(回滚)。
scope
可选,描述修改的范围。subject
是提交的简短描述。
合规的提交信息示例:
1 | # message: type(scope): subject |
配置 Husky
Husky 允许在 Git 钩子中添加自定义的行为,比如在提交时检查提交信息。
初始化 Husky 配置
1 | npx husky |
commit-msg hook
添加 commit-msg
钩子,这个钩子会在每次提交时检查提交信息是否符合规范。
配置 .husky/commit-msg
文件:
1 | #!/usr/bin/env sh |
该钩子会在每次提交时自动调用 commitlint
,确保提交信息符合规范。
如果提交信息不符合规则,提交会被拒绝,用户需要修改提交信息后再提交。
(可选) 自定义提交规范
如果需要自定义提交规范,可以在配置文件中进行修改。
可以更改 type
的可选值,或者自定义 subject
的最大长度。
1 | module.exports = { |
TS
引言
TypeScript
是 JavaScript
的超集,通过添加静态类型系统和面向对象编程特性,旨在解决 JavaScript 在大型项目中存在的类型松散、代码维护困难等问题。
其核心优势包括:
静态类型检查:
编译时捕捉类型错误,减少运行时崩溃风险。
代码可读性:
类型注解可以明确变量、函数参数和返回值的用途,有助于开发者理解代码的意图,提升团队协作效率,并让代码更易于维护。
生态兼容性:
完全兼容 JavaScript,支持渐进式迁移,,降低了学习和转换成本
面向对象编程特性:
TypeScript 提供了类、接口、继承、泛型等面向对象编程(OOP)的特性,使得开发者能够编写更符合现代开发范式的代码。
原始类型
boolean
: 布尔值,true
或false
。number
: 数字,包括整数与浮点数。string
: 字符串。tuple
: 元组,固定长度和类型的数组,例如:[number, string]
。enum
: 枚举,定义一组命名的常量。any
: 任何类型,可以赋值给任何变量。尽量避免使用,因为使用它会失去类型检查的优势。void
: 没有返回值,通常用于函数。null
: 空值。undefined
: 未定义的值。never
: 永不存在的值的类型,例如抛出异常的函数。object
: 非原始类型,例如对象、数组和函数。unkown
: unknown 是一种类型,它表示任何值的类型,但与 any 类型不同,它要求在使用之前进行类型检查。
联合类型与字面量
1 | const name: 'typescript' | 'javascript' = 'typescript'; |
枚举
定义命名常量集合,增强代码可读性
1 | enum Language { ZH, EN }; |
函数类型与接口 - 规范代码的 “契约”
函数类型
指定参数和返回值类型,避免意外行为。
1
2
3function add(x: number, y: number): number {
return x + y
}接口
定义对象结构,强制实现一致性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23interface User {
/* 只读属性 */
readonly id: number
name: string
username: string
password: string
/* 可选属性 */
age?: number
/* 广泛定义 */
[key: string]: unknown
}
const user: User = {
id: 802,
name: '花楹一间',
username: 'huaying',
password: '123456789',
age: 18,
skill: 'code'
};
类与面向对象编程 - 代码的 “组织艺术”
类与继承
支持
public
、private
、protected
访问修饰符。1
2
3
4
5
6
7
8
9
10
11
12
13
14class Animal {
constructor(public name: string) {
}
move() {
console.log('name:', this.name);
}
}
class Dog extends Animal {
bark() {
console.log('woof!');
}
}实现接口
通过接口约束类行为。
1 | interface Loggable { |
高级类型与工具 - 解锁高阶玩法
泛型
泛型(Generics)是一种用于创建可重用组件的强大工具。它使得能够在定义函数、类、接口等时,提供一种方式来指定类型,而不需要预先指定具体的类型。
基本语法: 泛型使用尖括号 <>
来定义。
典型场景:
集合操作:例如数组、链表等的操作,可以使用泛型来定义通用的集合处理函数。
表单处理:在表单处理中,可能需要处理不同类型的字段和验证逻辑,泛型能够帮助保持类型安全并且避免重复代码。
API 请求响应:对于不同的 API 请求,响应可能是不同的类型,泛型可以帮助处理不同类型的请求和响应。
泛型函数
在函数中,泛型允许在函数的参数和返回值中使用类型占位符
1
2
3
4function identity<T>(id: T): T {
return id
}
const id = identity(802); // 调用时,自动推断为 number 类型泛型接口
泛型接口允许定义可以接受不同类型的函数签名或方法。
1
2
3
4
5interface GenericIdentityFn<T> {
(arg: T): T;
}
const identityFn: GenericIdentityFn<number> = (arg) => arg;泛型类
类也可以使用泛型,这使得可以创建针对不同类型的实例化对象。
1 | class GenericBox<T> { |
泛型约束
使用
extends
关键字来实现泛型约束,对泛型添加一些限制,确保符合特定的结构。1
2
3
4
5
6
7
8
9
10
11interface Person {
name: string
}
// 泛型约束:T 必须是一个包含 name 属性的对象
function greet<T extends Person>(person: T): string {
return `Hello, ${person.name}`;
}
const person = { name: "John", age: 30 };
console.log(greet(person));默认泛型类型
可以为泛型提供一个默认类型,如果调用时没有提供类型参数,则使用默认类型。
1
2
3
4function wrap<T = string>(value: T): T {
return value;
}
console.log(typeof wrap(123));泛型的多个类型参数
可以为泛型函数、接口或类指定多个类型参数。
1
2
3
4
5
6function merge<T, U>(object1: T, object2: U): T & U {
return { ...object1, ...object2 };
}
const merged = merge({ name: "John" }, { age: 30 });
console.log(merged); // 输出 { name: 'John', age: 30 }使用
keyof
和in
限制泛型1
2
3
4
5
6
7// keyof 限制:T 必须是一个对象类型,并且 key 需要是对象的键
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "John", age: 30 };
console.log(getValue(person, "name")); // 输出 "John"
类型别名与交叉类型
1 | type Person = { name: string }; |
类型断言
可以通过类型断言强制将一个值转换为特定的类型,而 TypeScript 不会对其进行进一步的类型检查。
尖括号语法(只在 TSX 中不可用)
1
2let someValue: unknown = "Hello, world!";
const len: number = (<string>input).length;as
语法(推荐)1
2const input: unknown = 'hello world!';
const len: number = (input as string).length;
类型守卫
类型守卫是 TypeScript 提供的一种方式,用于细化类型检查,并确保某个变量的类型符合预期。
类型守卫通常会使用 typeof
、instanceof
或自定义的类型保护函数来做类型判断。
typeof
类型守卫typeof
运算符用于判断原始类型(如 string、number、boolean、symbol、undefined)。1
2
3
4
5
6
7function printLength(value: string | number) {
if (typeof value === "string") {
console.log(value.length); // `value` 在这里被推断为 `string` 类型
} else {
console.log(value.toFixed(2)); // `value` 在这里被推断为 `number` 类型
}
}instanceof
类型守卫instanceof
运算符用于检查某个对象是否是某个类的实例,通常用来检查类的实例。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class Dog {
bark() {
console.log("Woof!");
}
}
class Cat {
meow() {
console.log("Meow!");
}
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark(); // `animal` 被推断为 `Dog` 类型
} else {
animal.meow(); // `animal` 被推断为 `Cat` 类型
}
}
TS 项目配置文件
1 | /* |
相关链接🔗
Hexo
前言
Hexo 是一个快速、简洁且高效的博客框架。
Hexo 使用 Markdown(或其他标记语言)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。
优点:
- 超快速度: Node.js 所带来的超快生成速度。 上百个页面在几秒内完成渲染。
- 一键部署: 只需一条指令即可部署到
GitHub Pages
,Heroku
或其他平台。 - 支持 Markdown: Hexo 支持
GitHub Flavored Markdown
的所有功能,甚至可以整合Octopress
的大多数插件。 - 插件: 支持数种模板引擎(
EJS
,Pug
,Nunjucks
等)。 可以与现有的NPM包 (Babel
,PostCSS
,Less/Sass
等) 轻松地集成。
安装与初始化
安装
1
2
3
4
5
6
7
8# 全局安装(命令工具)
npm install hexo-cli -g
# 局部安装(hexo 包)
npm install hexo
# 执行 hexo 命令
npx hexo [command]初始化
1
hexo init blog
创建新文章
1 | hexo new "我的新文章" |
更多信息: Writing
使用草图
草图是未发布的文章,可以通过以下命令查看和发布草图:
1 | # 创建草稿 |
启动服务器
1 | hexo server |
更多信息: Server
生成静态文件
1 | hexo generate |
更多信息: Generating
部署到远程站点
1 | hexo deploy |
更多信息: Deployment
ASAR 文件的解压缩
引言
ASAR(
Archive for ASar
)归档是一种专为 Electron 应用设计的简单扩展存档格式。
它旨在缓解 Windows 平台上的长路径名问题,提升require
速度,并保护源代码不被轻易修改。
- ASAR 通过将文件夹结构打包成一个二进制文件,简化了 Electron 应用的资源文件管理。
这种格式便于分发和部署应用,同时也提供了性能和安全性方面的改善。 - 使用 ASAR 格式的主要好处包括减少文件数量和大小,加快文件访问速度。
在 Electron 应用中,由于模块加载机制,使用 ASAR 格式可以减少文件 I/O 操作,从而提高require
速度。 - ASAR 格式通过打包资源文件,可以防止用户直接修改应用的内部文件,从而提高安全性。
然而,这并不意味着 ASAR 提供了高级别的安全防护,有经验的用户或攻击者仍然可能找到绕过的方法。
安装
1 | # 全局安装 |
常用命令
解压缩
解压:
1
2# extract|e <archive>(asar 文件) <dest>(目标解压文件夹)
asar extract [archive] [dest]压缩:
1
2# pack|p [options] <dir>(目标解压文件夹) <output>(asar 文件)
asar pack [dir] [output]
其他命令
列出归档文件与目录:
1
2# list|l [options] <archive>(asar 文件)
asar list [archive]提取单个文件:
1
2# extract-file|ef <archive> <filename>(目标文件)
asar extract-file [archive] [filename]