一、3天时间学习TS
遇到的问题
1. 对函数设定的返回值类型与return 的类型不一致,就会报错
在指定返回值为void时,如果设定了返回值,结果会怎么样?答案是:会报错。只要我们对函数设定的返回值类型与return 的类型不一致,就会报错
function abc(a: number, b: number): number{
}
let m: number = abc(1, 2);
function abc(a: number, b: number): void{
return a+b;
}
let m: number = abc(1, 2);
2. 为什么不确定数据类型时,使用unknown,但不能使用any?
any会跳过类型检查,我们将any类型的变量赋给别的任何类型,都不会出现问题。但是如果设置为unknown,赋给别的类型的变量,就会报错。我们可以为一个unknown类型的变量进行类型判断,满足某种类型的的情况下赋给某个类型的变量,例如:
如果这两个类型不一样,依旧会报错。
3. never
该类型不允许有返回值,但是不写返回值会报错,一般在抛出错误时使用它
4. 添加了静态属性、泛型
泛型:对对象的属性进行限制,告诉我们必须对象必须要存在哪些属性,属性值需要是什么类型。
二、webpack
webpack常用的功能:
在功能十分复杂的当下,前端社区涌现很多优秀的实践方案,就比如模块化。有了它们的存在,极大的提升了程序开发的便利与程序维护的成本,但是如果想要被浏览器识别,还需要借助webpack对程序进行解析。
1. 自动编译
配置loader,自动地对指定目录下的拓展语言,例如ts、less等浏览器不能直接解析地格式的文件全部编译
2. 一键打包
指定入口和打包文件所在地址,对项目一键打包
3. 代码拆分、异步加载
提供代码拆分功能,在拆分过后,我们就可以对块实行异步加载。在进入到指定的页面之后才去加载相应的模块。
4. 支持插件,提升开发体验
引入开发时所需的插件,例如clean-webpack-plugin,可以帮我们在每次打包时自动清除之前的打包。
5. 兼容性处理
自动进行兼容性处理:我们在开发时,常常基于ES6的标准,但是如果需要将项目运行在早期的IE,就会有很多的语法问题,在webpack中指定好相应的加载器和babel就可以自动地对项目进行兼容性处理。
6. 兼容性进一步提升
我们做的兼容性处理可能依旧存在一定程度的缺陷,对ts编译进行编译时,会将代码存放在一个匿名箭头函数中,但是IE11并不支持该语法,因此我们需要单独为这些问题做兼容性适配arrowFunction: false。
核心功能:
- 代码拆分(异步加载)
- 只能静态分析
- 模块热替换(开发时,项目发生变化,对变化的部分进行同步更新,不再需要我们手动刷新,热替换也减少了页面刷新所需的时间成本)
三、项目总结
1. 修改样式
首先,通过修改样式的方式,熟悉项目,这个过程中学习了应该如何对某个组件添加高效的样式。
起初的实践方案是从最外层一层一层向内去添加选择器,先将整个组件的选择器从最外层到最内层的整体结构全部搭建出来,然后找到我想要修改样式的选择器,在里面对样式进行更改。
在这时的我看来,因为可以看到整体的结构,这样写可能会更有利于维护。但是由于使用的是后代选择器,而非子元素选择器,在完成这部分样式调整后,去调节兄弟组件样式时,很快就发现,在兄弟组件上出现了被前一个组件样式影响的情况,因为组件均来源于Antd,二者存在很多雷同的结构,在class的命名上也会有很多相同的部分。
后来在仔细参考了同事写的样式时,就发现,当我们想要为一个组件添加样式时,应该先为一个组件添加专属的id,这样我们就可以很快的定位到该元素。同时,由于css的定位是自右向左解析的,所以当我们把css层级写的太深时,会一层一层地筛选过滤,直到我们写的css样式的最外层。这对于浏览器来说来说,会增加非常多的压力,哪怕在可能最内层的第一个id选择器就定位到对应元素,但是依旧会顺着我们写的层级关系,一层一层寻找到尽头。
2. 使用Antd提供的组件
由于缺乏对组件的使用经验,起初修改样式时,就盲目地认为在修改其样式时,这么细的地方,官方怎么可能给对应的API,于是选择任何样式都自己改,但是也暴露出很多问题,Antd官方使用的是子元素选择器,如果我们以后代选择器去更改样式,样式就不会生效,因为优先级不如前者。
最开始的解决方案是添加!important,但是自己也知道,其实!important不到万不得已,是不能使用的。后来看了Antd的样式添加规则后,就发现她们使用的是子元素选择器,如果我们也使用子元素选择器,就可以将原生的样式给覆盖掉。
但是这样做,依旧不是官方推荐的做法,因为官方其实已经为我们提供好了很多API,将我们在使用组件时,高度个性化的部分都暴露了出来,让我们可以通过API去自定义组件。
3. 封装功能函数
自己写的是:数据树、工程树目录的搜索框内的搜索功能
搜索框的样式结构依旧是来源于Antd,整个搜索功能大致包含了:过滤、排序、去空、操作记忆
每个数据都是一个对象,基本类型的属性包含了节点的各种信息,例如id,key,title等,引用类型的数据是一个名为childNode的数组,该数组内的元素依旧也是一个个的树形结构。
整体遍历依旧使用的是递归,数据树的文件夹都是一般性的文件夹,没有type属性,而工程树的文件夹是由数据树的文件节点组成,数据树的文件节点均拥有一个type属性,工程树的文件节点同文件夹节点一样,拥有type属性,这些type属性均实现了一个基类型的接口。
需要对两棵树构建出一个公共的过滤方法,就需要对退出条件做一个集成,能够同时满足两棵树的递归退出条件就可以,
过滤+操作记忆:我当时选择的方案是“双字段组合记录”,一个只负责保存过滤时展开的文件夹的id,另一个负责保存过滤时展开的文件夹以及过滤前手动展开的节点id。
首先,在输入关键字后,数据树、工程树均只显示包含关键字的项与该项所在的文件夹,结构依旧是树形结构。在搜索状态下,树仅由过滤的数据组成,全部为展开状态。
当用户清除输入时,树恢复成全部数据,展开的数据为上一步输入的数据,包括用户在输入前自己展开的文件夹。也就说,我们不能干预用户的展开和折叠。
首先,Antd提供有一个原生API,我们只需要传入节点对应id,就可以将这些文件夹展开,在过滤时,由于展开的只有含有关键字的节点的文件夹,而取消输入时需要展开之前所有的情况。这是两种完全不同的情况,于是就决定将这两种情况下对应的id存储在不同的数组中。
在过滤时,将过滤的包含关键字的节点的文件夹的id保存在数组1中,同时还需要保存到数组2中,而用户在点击节点展开文件夹时,只需要将被点击节点的id存储到数组2中即可。数组1在搜索时,被传递到API中,负责展开所有搜索到的数据,数组2则是当我们清除输入时,一方面需要展开上一步搜索的所有数据,一方面展开前面在过滤前已经被用户展开的数据。
两个字段需要分工协作。
在搜索框左侧,有一个收起、折叠树的按钮,点击展开时,也需要展开用户之前展开的所有文件夹。
去空就十分简单了,只需要判断当前节点是否是一个文件节点,是文件节点就保留,文件夹节点就判断其ChildNode是否为空,不为空才保留。
这个方法最开始在操作数据源时被执行,但是后来小组长提议还是在操作数据源时,重新走一边过滤空文件夹的方法会更好一些,因为操作了数据源,本身就需要重新对数据进行去空,就需要遍历一次,所以不如干脆将过滤关键字和去空混在一起,一次遍历解决两个问题。节省了一次遍历的时间。
排序就只需要在过滤结束之后执行就可以。
中间还对该方法进行了一些优化,例如在属于关键字时,增加一个防抖的效果,通过拼音输入关键字时,每一个字符其实都会触发过滤,但是这个时候的过滤显然是无效的,就可以增加一个时间限制,用户在500ms内不再输入,才去走过滤的流程。
其次还对首尾的空格进行了一个过滤,当用户输入空格时,或者在关键字的末尾添加空格,都属于无效输入,在匹配时,先对该字段进行首位的空格清除掉,然后和状态里的该字段进行比较,看是否相同,是否为空,这两者都满足时才去过滤。可以直接借助trim(),可以借助正则表达式:str.replace(/(^\s*)|(\s*$)/g, "");
匹配关键字使用的是include方法。还可以使用indexOf、lastIndexOf。实在不行,还能先转化为数组,再用filter、forEach、indexOf方法....
关键字渲染方法:
借助Antd原生的API TitleRender,该API可接收一个节点,只需要返回一个标签即可