以下是关于前端项目模块化的实践,包含以下内容:

  1. 搭建 NPM 私有仓库管理源码及依赖
  2. 使用 Webpack 打包基础设施代码
  3. 使用 TypeScript 编写可靠类库

使用 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<> 实例,只是内部元素由传入的 valueSelectorkeySelector 决定,该签名使得链式调用成为可能;
  • 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 的收益的更多相关文章

  1. 前端项目模块化的实践2:使用 Webpack 打包基础设施代码

    以下是关于前端项目模块化的实践,包含以下内容: 搭建 NPM 私有仓库管理源码及依赖: 使用 Webpack 打包基础设施代码: 使用 TypeScript 编写可靠类库 使用 TypeScript ...

  2. 前端项目模块化的实践1:搭建 NPM 私有仓库管理源码及依赖

    以下是关于前端项目模块化的实践,包含以下内容: 搭建 NPM 私有仓库管理源码及依赖: 使用 Webpack 打包基础设施代码: 使用 TypeScript 编写可靠类库 使用 TypeScript ...

  3. [Vue 牛刀小试]:第十七章 - 优化 Vue CLI 3 构建的前端项目模板(1)- 基础项目模板介绍

    一.前言 在上一章中,我们开始通过 Vue CLI 去搭建属于自己的前端 Vue 项目模板,就像我们 .NET 程序员在使用 asp.net core 时一样,我们更多的会在框架基础上按照自己的开发习 ...

  4. TypeScript在react项目中的实践

    前段时间有写过一个TypeScript在node项目中的实践. 在里边有解释了为什么要使用TS,以及在Node中的一个项目结构是怎样的. 但是那仅仅是一个纯接口项目,碰巧赶上近期的另一个项目重构也由我 ...

  5. 前端工程模块化——以一个php项目为例

    实现一个页面功能总是需要 JavaScript.CSS 和 Template 三种语言相互组织,所以我们真正需要的是一种可以将 JavaScript.CSS 和 Template 同时都考虑进去的模块 ...

  6. TypeScript在node项目中的实践

    TypeScript在node项目中的实践 TypeScript可以理解为是JavaScript的一个超集,也就是说涵盖了所有JavaScript的功能,并在之上有着自己独特的语法.最近的一个新项目开 ...

  7. 从一个前端项目实践 Git flow 的流程与参考

    Git flow 出自 A successful Git branching model,这里使用了一个前端项目配合本文稿实施了 git flow 并记录流程作出示例和参考,对 hotfix 与持续部 ...

  8. 基于CocoaPods的iOS项目模块化实践

    什么是CocoaPods? CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. It has ove ...

  9. Webpack:前端资源模块化管理和打包工具

    一.介绍: Webpack 是当下最热门的前端资源模块化管理和打包工具.它可以将许多松散的模块按照依赖和规则打包成符合生 产环境部署的前端资源.还可以将按需加载的模块进行代码分隔,等到实际需要的时候再 ...

随机推荐

  1. SQLSERVER 分区表实战

    背景:对NEWISS数据库创建分区表T_SALES的SQL.按照日期来进行分区步骤:1:创建文件组2:创建数据文件3:创建分区函数4:创建分区方案5:创建表及聚集索引6:导入测试数据(此处略),并查询 ...

  2. EF5.0中的跨数据库操作

    以前在用MVC + EF 的项目中,都是一个数据库,一个DbContext,因此一直没有考虑过在MVC+EF的环境下对于多个数据库的操作问题.等到要使用时,才发现这个问题也不小(关键是有个坑).直接说 ...

  3. codeforces 671D Roads in Yusland & hdu 5293 Tree chain problem

    dp dp优化 dfs序 线段树 算是一个套路.可以处理在树上取链的问题.

  4. 【Ansible 文档】【译文】模式

    Patterns 模式 Ansible中的模式是指我们如何决定那些机器执行管理操作.这里意味着与那些主机通信,但是对于playbook,它是指哪些主机应用特定的配置或执行特定程序. 我们将重温一下In ...

  5. Asp.Net WebApi服务的创建

    Web API一种REST架构风格的Web服务.所谓的REST架构与技术无关,而是面向资源的一种软件架构设计. WCF自3.5之后也提供了对REST风格的支持,但和WebAPI来比较显得较为笨重,We ...

  6. 将Vue-cli搭建的项目改造成多页面应用时对项目结构和配置的调整

    创建项目 首先初始化一个Vue项目模板,之后在模板下载时候会弹出如下配置选项 vue init webpack demo 配置好后按下回车就构建完成了Vue脚手架,之后cd进入项目,并且进行node模 ...

  7. 2-7 R语言基础 数据框

    #数据框 > df <- data.frame(id=c(1,2,3,4),name=c("a","b","c","d ...

  8. 2-2 R语言基础 向量

    #Vector 向量的三种创建方法,两个参数:类型,长度 > x <- vector("character",length=10)> x1 <- 1:4&g ...

  9. android与JS交互,互相调用方法,跳转到网页

    在main下面New - Folder - Assets Folder,在Assets下面新建一个js_android.html <html><head> <meta h ...

  10. svn 更新提交文件冲突

    文件冲突定义:svn up更新服务器文档到本地的时候发现本地的文件有所改动,和svn服务器不同步 服务器会报冲突,让你觉得已谁的为准,根据实际情况我们需要选择是以服务器还是以本地代码为准 报错: Co ...