前端项目模块化的实践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 是当下最热门的前端资源模块化管理和打包工具.它可以将许多松散的模块按照依赖和规则打包成符合生 产环境部署的前端资源.还可以将按需加载的模块进行代码分隔,等到实际需要的时候再 ...
随机推荐
- Sql Server中的游标最好只用于有主键或唯一键的表
游标cursor,我想大多数人都在sql server里面用过.当一个表数据量不太大的时候,游标还是可以用的,毕竟游标是循环一个表中每一行数据的最简便办法.但是如果你用一个游标去循环一个没有主键或唯一 ...
- CSS| background_属性总结
Property List 属性 描述 CSS background 在一个声明中设置所有的背景属性. 1 background-attachment 设置背景图像是否固定或者随着页面的其余部分滚动. ...
- 使用动态跟踪技术SystemTap监控MySQL、Oracle性能
[IT168 技术]本文根据吕海波2018年5月11日在[第九届中国数据库技术大会]上的演讲内容整理而成. 讲师介绍: 吕海波,美创科技研究员,ITPUB管理版版主.出版技术书籍<Oracle内 ...
- 修改centos等linux的hostname-永久生效
步骤uname -a (或者hostname)可以看到hostname,修改步骤如下: uname -a 查看hostnamehostname newname 修改下,让hostname立刻生效.vi ...
- Rarfile解压不了的问题
最近用python调用rarfile进行解压rar压缩包时,报了如下错误: rarfile.RarCannotExec: Unrar not installed? (rarfile.UNRAR_TOO ...
- TruncateATable 清除一张表
当我们想删除一张表的全部数据时,我们可以使用 truncate 关键字,但如果要删除的表的主键被引用了,那么就无法执行语句. 1.制作清除数据的工具 ,在 nuget 控制台中输入 Install-P ...
- mysql数据导入导出与数据表优化
一.数据导入 mysqlimport -uroot oa d:/aa.txt --fields-terminated-by=, --fields-optionally-enclosed-by= --l ...
- vc MFC 通过IDispatch调用默认成员函数
CComPtr<IDispatch> spDisp(IDispatch *); if(!spDisp) return; DISPPARAMS dispParam={0}; //没有参数 V ...
- 关于Java集合类库中的几种常用队列
Java中几种常用的队列 阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞 ...
- python提示警告InsecureRequestWarning
在Python3中使用以下代码报错: import requests response = requests.get(url='', verify=False) 错误代码如下: InsecureReq ...