前端项目模块化的实践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 是当下最热门的前端资源模块化管理和打包工具.它可以将许多松散的模块按照依赖和规则打包成符合生 产环境部署的前端资源.还可以将按需加载的模块进行代码分隔,等到实际需要的时候再 ...
随机推荐
- 【转】Nginx学习---深入浅出Nginx的介绍
[原文]https://www.toutiao.com/i6595428119933354500/ Nginx是一款轻量级的Web服务器.反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在 ...
- SQL慢查询安装过程
SQL慢查询 基本操作 打开防火墙 firewall-cmd --zone=public --add-port=3306/tcp --permanent firewall-cmd --reload 安 ...
- [LOJ 6031]「雅礼集训 2017 Day1」字符串
[LOJ 6031] 「雅礼集训 2017 Day1」字符串 题意 给定一个长度为 \(n\) 的字符串 \(s\), \(m\) 对 \((l_i,r_i)\), 回答 \(q\) 个询问. 每个询 ...
- python第二十九课——文件读写(读取数据操作)
演示读取数据操作:path=r'a.txt' 1.打开文件f1=open(path,'r') 2.读取数据content1=f1.read(3)print(content1) content1=f1. ...
- BZOJ4401:块的计数(乱搞)
Description 小Y最近从同学那里听说了一个十分牛B的高级数据结构——块状树.听说这种数据结构能在sqrt(N)的时间内维护树上的各种信息,十分的高效.当然,无聊的小Y对这种事情毫无兴趣,只是 ...
- MetaMask/Website
https://github.com/MetaMask/Website 将这个包下载下来之后运行npm install出现下面的问题 gyp: No Xcode or CLT version dete ...
- Node.js实战(十)之EventEmitter
Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列. Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs. ...
- JS图片灯箱(lightBox)效果基本原理和demo
到年底了,项目不怎么忙,所以有空特地研究了下KISSY中源码JS灯箱效果,感觉代码比较简单,所以就按照他们的思路依赖于Jquery框架也封装了一个,特地分享给大家,以前经常看到网上很多这样的插件,感觉 ...
- JAVA框架 Spring 和Mybatis整合(传统dao)
一:我们使用spring处理service,mybaits处理dao层. 二:导入jar包 pom.xml文件内容: <?xml version="1.0" encoding ...
- JAVA框架 Spring JDBC模板
一:引入jar包: 1.数据驱动jar包: dbcp依赖的包: spring的事务包和数据库包: IOC包AOP包.log4j的包: 所有的jar包: 编写测试类: 注入JdbcTemplate类,该 ...