前端项目模块化的实践3:使用 TypeScript 的收益
以下是关于前端项目模块化的实践,包含以下内容:
- 使用 TypeScript 的收益
- 使用 Mocha/Jest 进行单元测试 [实现中]
使用 Webpack 打包基础设施代码已经很大程度上解决了生产力,但日益复杂业务和逻辑仍然让前端陷入“动态一时爽、重构火葬场”的笑谈,TypeScript 为解决这个问题而来。
在本章节我们使用 TypeScript 完成一个类似 LINQ 中 Enumerable<T> 的实现,涉及代码编写与测试用例,仍然不深入关于 TypeScript 的讨论。
场景
假想需要实现这样一个功能:比如一组学生,我们希望按照根据班级分组,接着按年龄排序,最后按照名称排序。
这并不复杂,这些步骤是有先后的,每步操作得到的都是一组对象集合,分组和排序是常规数组操作,写几个循环就可以达到目标。
C# 的实现
void Main() {
var students = new List<Student> {
new Student { Classes = "class-1", Name = "Rattz", Age = 11 },
new Student { Classes = "class-2", Name = "Rose", Age = 10 },
new Student { Classes = "class-1", Name = "Mike", Age = 11 }
};
var seq = students.GroupBy(x => x.Classes)
.Select(g => new {
Classes = g.Key,
Members = g.OrderBy(x => x.Age)
.ThenBy(x => x.Name)
.Select(x => x)
});
}
class Student {
public String Classes { get; set; }
public String Name { get; set; }
public Int32 Age { get; set; }
}
得力于静态语言的类型保证及 LINQ 语法对内存对象的操作能力,一行代码就简单而有表现力地解决了问题,在 LINQPad 里组织如下:

ES6 很明了但代码略多
现在来看 JavaScript 的写法,使用 ES6 的 Map 对象极大地减化了代码。
let students = [
{'classes': 'class-1', 'name': 'Rattz', 'age': 11},
{'classes': 'class-2', 'name': 'Rose', 'age': 10},
{'classes': 'class-1', 'name': 'Mike', 'age': 11},
];
let map = new Map();
for (let item of students) {
let classes = map.get(item.classes);
if (Object.is(classes, undefined)) {
classes = [item];
map.set(item.classes, classes);
}
else {
classes.push(item);
}
}
for (let [, value] of map.entries()) {
value.sort((p1, p2) => {
if (p1.age !== p2.age) {
return p1.age - p2.age;
}
return p1.name.localeCompare(p2.name);
});
}
let groups = [];
for (let [key, value] of map.entries()) {
groups.push({
classes: key,
members: value,
});
}
ES5 reduce 很强有力但很难一次编写正确
代码略多,包含了3个循环,如果不使用 Map 代码就要进行数组查找;如果想压缩循环,就使用 Array.prototype.reduce 函数,代码就很难懂了。
let students = [
{'classes': 'class-1', 'name': 'Rattz', 'age': 11},
{'classes': 'class-2', 'name': 'Rose', 'age': 10},
{'classes': 'class-1', 'name': 'Mike', 'age': 11},
];
let groups = students.reduce((previous, current) => {
let arr = previous.filter(x => x.key === current.classes);
if (arr.length > 0) {
arr[0].members.push(current);
}
else {
previous.push({
classes : current.classes,
members: [current],
});
}
return previous;
}, []);
for(let g of groups) {
g.members.sort((a, b) => a.name.localeCompare(b.name));
}
TypeScript 的使用
下文通过编写类库完成类似功能,我们充分使用生产力而先不考虑语言、版本问题,看看具体的调用部分
interface Student {
classes: string,
name: string
age: number
}
let students: Student[] = [
{'classes': 'class-1', 'name': 'Rattz', 'age': 11},
{'classes': 'class-2', 'name': 'Rose', 'age': 10},
{'classes': 'class-1', 'name': 'Mike', 'age': 11},
];
let groups = Enumerable.from(students)
.groupBy(x => x.classes)
.select(x => ({
classes: x.key,
members: Enumerable.from(<Student[]>x.items)
.sortBy((x, y) => x.age - y.age)
.thenSortBy((x, y) => x.name.localeCompare(y.name))
}));
完整代码见于 Enumerable 行 100 左右。
如果 Enumerable 的实现是 JavaScript 版本,这部分代码可能充满了疑问,即便阅读源码也很难一下子理解实现者的用意
groupby需要什么样的参数?select使用了groupBy返回值,它包含怎样的数据结构?sortBy但返回了什么?thenSortBy如何进行二次排序,又返回了什么?
TypeScript 能够解答上述问题问题,以下是函数签名。
groupBy:完整签名是groupBy<K, U>(keySelector: (value: T, index: number) => K, valueSelector?: (value: T, index: number) => U): Enumerable<Group<K, T | U>>,虽然稍长但是阅读起来也就那回事keySelector: 接受2个参数(分别是数组元素和索引)返回1个值,通常是对象的某个属性,valueSelector: 和keySelector相似,表示得到新元素的方法,术语是“投影”Enumerable<Group<K, T | U>>是groupBy的返回类型,表示仍然是Enumerable<>实例,只是内部元素由传入的valueSelector和keySelector决定,该签名使得链式调用成为可能;
select: 完整签名是select<U>(callback: (value: T, index: number) => U): Enumerable<U>,和groupBy类似,但更加简单sortBy: 和Array.prototype.sort功能相似,但签名是sortBy(compareFn: (a: T, b: T) => number): OrderedEnumerable<T>,返回OrderedEnumerable<>实例thenSortBy:同sortBy,依赖OrderedEnumerable<T>的内部实现
我们可以在安全地传入参数和引用返回值,在代码编写阶段就能得到编译器的语法、值入参数合法性检查。
前端项目模块化的实践3:使用 TypeScript 的收益的更多相关文章
- 前端项目模块化的实践2:使用 Webpack 打包基础设施代码
以下是关于前端项目模块化的实践,包含以下内容: 搭建 NPM 私有仓库管理源码及依赖: 使用 Webpack 打包基础设施代码: 使用 TypeScript 编写可靠类库 使用 TypeScript ...
- 前端项目模块化的实践1:搭建 NPM 私有仓库管理源码及依赖
以下是关于前端项目模块化的实践,包含以下内容: 搭建 NPM 私有仓库管理源码及依赖: 使用 Webpack 打包基础设施代码: 使用 TypeScript 编写可靠类库 使用 TypeScript ...
- [Vue 牛刀小试]:第十七章 - 优化 Vue CLI 3 构建的前端项目模板(1)- 基础项目模板介绍
一.前言 在上一章中,我们开始通过 Vue CLI 去搭建属于自己的前端 Vue 项目模板,就像我们 .NET 程序员在使用 asp.net core 时一样,我们更多的会在框架基础上按照自己的开发习 ...
- TypeScript在react项目中的实践
前段时间有写过一个TypeScript在node项目中的实践. 在里边有解释了为什么要使用TS,以及在Node中的一个项目结构是怎样的. 但是那仅仅是一个纯接口项目,碰巧赶上近期的另一个项目重构也由我 ...
- 前端工程模块化——以一个php项目为例
实现一个页面功能总是需要 JavaScript.CSS 和 Template 三种语言相互组织,所以我们真正需要的是一种可以将 JavaScript.CSS 和 Template 同时都考虑进去的模块 ...
- TypeScript在node项目中的实践
TypeScript在node项目中的实践 TypeScript可以理解为是JavaScript的一个超集,也就是说涵盖了所有JavaScript的功能,并在之上有着自己独特的语法.最近的一个新项目开 ...
- 从一个前端项目实践 Git flow 的流程与参考
Git flow 出自 A successful Git branching model,这里使用了一个前端项目配合本文稿实施了 git flow 并记录流程作出示例和参考,对 hotfix 与持续部 ...
- 基于CocoaPods的iOS项目模块化实践
什么是CocoaPods? CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. It has ove ...
- Webpack:前端资源模块化管理和打包工具
一.介绍: Webpack 是当下最热门的前端资源模块化管理和打包工具.它可以将许多松散的模块按照依赖和规则打包成符合生 产环境部署的前端资源.还可以将按需加载的模块进行代码分隔,等到实际需要的时候再 ...
随机推荐
- SQLSERVER 分区表实战
背景:对NEWISS数据库创建分区表T_SALES的SQL.按照日期来进行分区步骤:1:创建文件组2:创建数据文件3:创建分区函数4:创建分区方案5:创建表及聚集索引6:导入测试数据(此处略),并查询 ...
- EF5.0中的跨数据库操作
以前在用MVC + EF 的项目中,都是一个数据库,一个DbContext,因此一直没有考虑过在MVC+EF的环境下对于多个数据库的操作问题.等到要使用时,才发现这个问题也不小(关键是有个坑).直接说 ...
- codeforces 671D Roads in Yusland & hdu 5293 Tree chain problem
dp dp优化 dfs序 线段树 算是一个套路.可以处理在树上取链的问题.
- 【Ansible 文档】【译文】模式
Patterns 模式 Ansible中的模式是指我们如何决定那些机器执行管理操作.这里意味着与那些主机通信,但是对于playbook,它是指哪些主机应用特定的配置或执行特定程序. 我们将重温一下In ...
- Asp.Net WebApi服务的创建
Web API一种REST架构风格的Web服务.所谓的REST架构与技术无关,而是面向资源的一种软件架构设计. WCF自3.5之后也提供了对REST风格的支持,但和WebAPI来比较显得较为笨重,We ...
- 将Vue-cli搭建的项目改造成多页面应用时对项目结构和配置的调整
创建项目 首先初始化一个Vue项目模板,之后在模板下载时候会弹出如下配置选项 vue init webpack demo 配置好后按下回车就构建完成了Vue脚手架,之后cd进入项目,并且进行node模 ...
- 2-7 R语言基础 数据框
#数据框 > df <- data.frame(id=c(1,2,3,4),name=c("a","b","c","d ...
- 2-2 R语言基础 向量
#Vector 向量的三种创建方法,两个参数:类型,长度 > x <- vector("character",length=10)> x1 <- 1:4&g ...
- android与JS交互,互相调用方法,跳转到网页
在main下面New - Folder - Assets Folder,在Assets下面新建一个js_android.html <html><head> <meta h ...
- svn 更新提交文件冲突
文件冲突定义:svn up更新服务器文档到本地的时候发现本地的文件有所改动,和svn服务器不同步 服务器会报冲突,让你觉得已谁的为准,根据实际情况我们需要选择是以服务器还是以本地代码为准 报错: Co ...