若是开发过node.js的话应该对js(javascript)很是熟悉,TypeScript(如下简称ts)是js的超集。javascript
下面是ts的官网:java
1.环境配置(若是已经进行过环境配置,能够跳过此步)git
开发ts须要先简单的配置开发环境,若是使用的是Visual Studio,只须要简单装一个node.js的组件包便可:github
该组件包主要包含Node.js开发工具,js和ts语言支持;除了该工具包外,还须要额外安装ts sdk:正则表达式
但若是使用的是VS Code的话,这些就须要本身手动安装和配置了,node.js开发工具的下载地址为:typescript
https://nodejs.org/en/download/数据库
安装完node就能够运行npm指令了,npm是Node.js包管理器(node package manager),你能够认为它是一个巨大的云端数据库,其中集成了大量js或ts开发中须要的包和代码模块,当你在项目中须要引用这些包或模块时,随时能够利用npm指令进行快速下载使用,这样不被引用的模块没必要占用过多项目空间。好比,能够直接利用npm来安装ts,打开cmd输入:npm
> npm install -g typescript
其中-g表示全局安装,在npm指令中,install也能够简写为i:编程
> npm i -g typescript
ts安装完成后,就能够直接建立一个空文件夹做为工程目录了,但这时建立的ts文件并不能编译,由于一个新的ts工程还须要先初始化npm和ts配置文件,能够在VS Code中直接调用新的终端:
> npm init -y
> tsc -init
执行完这两条指令后,咱们会发现工程中生成了两个json文件,它们分别是package.json和tsconfig.json;参数-y表示按照默认方式生成,tsc即为type script config的缩写。
package.json中记录了整个工程的基本信息,简化的命令行指令,以及当前工程的依赖模块和库等;开发者能够自行在该文件的scripts块中添加自定义的指令,例如:
"start": "tsc main.ts && node main.js", "build": "tsc main.ts && pkg -t win main.js"
这样即可以直接调用:
> npm run start
> npm run build
来代替执行自定义添加的命令行内容;在第一次build时,系统通常会告诉你它蠢蠢的没有找到pkg,这时,你只须要执行安装它的指令便可:
> npm i -g pkg
一样的,以后在编译过程当中遇到了引用的模块或库找不到的状况,能够先考虑该模块是否安装,若是没有,均可以执行相似的安装指令,但须要区分是否全局安装。
回过头来讲下pkg是什么东西,这是将Node.js项目打包为可执行文件的一个工具,参数-t win 表示生成的目标(target)平台为windows,更多详情了解可见github:
另外,为了更方便的经过ts来引用一些经常使用的node.js库,能够考虑提早执行如下指令:
> npm i @types/node --save-dev
完成后,工程目录的node_modules下会自动添加对应安装的库。参数-save意思是在package.json中保存并写入该依赖库,-dev指的是仅在开发阶段须要依赖该库,编译部署后则再也不依赖。
2.正式编写
在正式开始编写以前,须要明确的是,ts并不是强封装类型的语言,和不少面向对象的编程语言有必定的区别,也不须要程序入口同样的main函数,而是从上到下,从左到右依次读取程序中的每一行;
固然了,这并不表明ts不能实现封装,你依然能够将固定的代码块封装为函数或类,但这并不是是强制性的。
为了对文件和路径进行操做,须要提早引用一些模块,相似于C#中的using,ts中的格式则相似于:
import * as fs from 'fs';
import * as path from 'path';
由于以前已经安装过@types/node, 因此这里不会出现找不到引用的报错。固然了,还能够用另外一种方式来引用模块:
const fs = require('fs');
const path = require('path');
顺便提一句ts中声明的几个关键字const,var,let;const和var在C#也有,分别用于声明常量与局部变量,而let是我以前没有见过的,在网上查阅以后,发现let和var不少地方都是相似的,但有如下几点区别:
1.var声明的变量会自动提高到该语句所在代码块的开头(但注意初始化的赋值并不会),这种现象称为变量提高;而let不具有变量提高的特性
形成的影响即是,var能够先使用后声明,不会有任何报错,而是会输出未定义类型undefined,但let这么作就会直接报错(迷)
2.var容许重复声明同一变量,会覆盖以前变量的值,但let则不能重复声明同一变量(迷)
3.var重复声明变量时内部代码块的值能够覆盖外部值(什么还有这种操做?!),但let则表现为不一样的两个变量,
主要由于var与let声明的变量做用范围不一样,var的做用范围包含子块以及它所在的函数的任何位置(迷),而let只在当前块(不包含子块)中有效
emm...感受和C#里的var彻底不同啊,做为新手若是为了保险起见,能够均使用let来声明局部变量。
下面的方法为查找指定路径下的文件,并将全部文件的绝对路径存储到一个临时的数组中:
1 let temp: string[] = new Array(); 2 function fileDisplay(filePath: string) { 3 // 根据文件路径读取文件,返回一个文件列表 4 const files = fs.readdirSync(filePath); 5 // 遍历读取到的文件列表 6 for (let filename of files) { 7 // path.join获得当前文件的绝对路径 8 const filepath = path.join(filePath, filename); 9 // 根据文件路径获取文件信息 10 const stats = fs.statSync(filepath); 11 const isFile = stats.isFile(); // 是否为文件 12 const isDir = stats.isDirectory(); // 是否为文件夹 13 if (isFile) { 14 temp.push(filepath); 15 } 16 if (isDir) { 17 fileDisplay(filepath); // 递归,若是是文件夹,就继续遍历该文件夹里面的文件 18 } 19 }; 20 }
注意在上述的方法中须要须要同步读取文件(Sync),而不该该采起默认的异步读取,这样以后的代码中取到temp数组时才会获得正确的值,若是非要异步读取,则须要用回调的方式来写json。
为了得到命令行中输入的参数,可使用下面的语句:
let argument = process.argv.splice(2);
process.argv()为node.js中返回当前命令行参数的方法,其中2表明的是实际输入的参数数组,若是输入0的话则表明获取node,1的话返回执行的js的完整路径
以后直接将命令行输入的第一个参数,也就是用户键入的文件夹路径做为参数传递给fileDisplay方法便可:
fileDisplay(argument[0]);
获得全部的文件路径后,接下来就是按照文件的类型写入json中了
首先咱们须要先遍历全部的文件路径,经过路径字符串能够获得文件的一些基本信息,例如文件的拓展名,文件的基本名称等,经过文件的扩展名能够对文件资源的类型重定义和分类:
1 for (let item of temp) 2 { 3 let extname = path.extname(item);//获取文件的扩展名,带. 4 let basename = path.basename(item, extname);//获取文件的基本名称,第二个参数为须要剔除的扩展名 5 //... 6 }
固然了,若是你不想用path模块的方法,也能够直接用字符串的方式来截取:
let fileExtension = item.substring(item.lastIndexOf('.')); // let fileName = item.substring(item.lastIndexOf('\\') + 1, item.lastIndexOf('.'));
须要注意的是,在ts中遍历元素内容的方式为of而非in(习惯C#了这里被坑了一把),in只能遍历出索引...
另外,匹配[\]时须要用两个[\\]才能够,由于一个[\]表明的大多为转义字符。
根据文件的扩展名返回自定义的文件类型:
1 function GetType(extension: string): string { 2 switch (extension) { 3 case ".png": 4 case ".jpg": 5 return "image"; 6 case ".fnt": 7 return "bitmapFont"; 8 case ".TTF": 9 case ".ttf": 10 return "font"; 11 case ".spine": 12 case ".particle": 13 return "particle"; 14 case ".mp3": 15 return "audio"; 16 default: 17 return "null"; 18 } 19 }
筛选过滤文件:
1 let type = GetType(extname); 2 //过滤非指定类型文件 3 if (type == "null") 4 continue; 5 //过滤重名文件 6 if (resources[basename]) { 7 console.log(`错误!!!该文件名已存在【${basename}】`); 8 continue; 9 }
定义json基础数据结构:
1 let outjson: any = {} 2 let resources: any = {}; 3 let d: any = {}; 4 5 d.tye = type; 6 d.url = item; 7 8 resources[basename] = d; 9 outjson.resources = resources;
上面是为了更方便读者理解而将这三个变量放在一块儿,实际上变量d是在循环体内部声明的局部变量,any类型是ts中的一种特殊类型,它能够被定义为任何一种其余类型,这里将它定义为了一种大括号类型的数据结构,表明它的内部还有一些其余的任意成员变量。
若是是在C#中书写json的数据结构,将是一件很是麻烦的事,须要严格的定义为一个新的类或结构体,但ts中彷佛至关自由,只须要用一个变量来代替便可,甚至直接在赋值初始化的时候来肯定键值。
但网上关于大括号类型的any讲解并很少,因此作了一点额外的测试:
1 let a: any = {}; 2 let b: any = {}; 3 let c: any = {}; 4 a.b = "c"; 5 a.c = 5.6; 6 a.a = a.b; 7 a["b"] = a.b; 8 b["c"] = a.c; 9 b["a"] = a; 10 c.a = b; 11 c[a.b] = a; 12 console.log(c);
你们能够推导下会打印出什么结果;好,接下来公布答案:
1 { 2 a: { 3 c: 5.6, 4 a: { 5 b: 'c', 6 c: 5.6, 7 a: 'c' 8 } 9 }, 10 c: { 11 b: 'c', 12 c: 5.6, 13 a: 'c' 14 } 15 }
下面来进行一个简单的梳理:
测试第四行 表明a中有一个键(变量名)为b的成员,它的值为字符串c
测试第五行 表明a中有一个键(变量名)为c的成员,它的值为数字类型5.6(ts中全部的数字类型均为浮点型,省去了不少其余编程语言中值类型数据的繁琐分类)
测试第六行 表明a中有一个键(变量名)为a的成员,它的值初始化为a中键为b的那个成员的值,也便是一样的字符串c
测试第七行 实际意义与第四行相同,但这里是为了测试[key]这种书写形式所存在的意义,实际上结合第十一行就能得出结论,那就是——当咱们须要一个字符串变量而很是量来做为键时就不能直接用“.成员名”的方式了,由于这样的方式只能生成固定的字符串名,
能够再比较如下例子:
1 let x1: any; 2 let x2: any = {}; 3 x1 = "x2"; 4 x2.x1 = x1; 5 x2[x1] = x1; 6 console.log(x2); 7 x1 = "6"; 8 x2.x1 = x1; 9 x2[x1] = x1; 10 console.log(x2);
你们能够再推导一下会打印出什么结果;好,如今公布答案:
1 { 2 x1: 'x2', 3 x2: 'x2' 4 } { 5 '6': '6', 6 x1: '6', 7 x2: 'x2' 8 }
是否是让人有些惊讶,实话说,第二次的打印结果笔者也没有作对,我没有想到它居然能打印出3个值...缘由就在于在第一次中x2[x1]中x1对应的字符串x2这一键并无被修改或删除,而x2.x1中键x1是一个固定的变量名,因此它的值理所固然的被改变为了后面的字符串6,又由于x1的值已经发生了改变,因此x2[x1]已经再也不是原来的任何一个键,从而又生成了一个新的键值对。
通过上面的对比测试,应该已经能够很好的区分何时用".成员名",何时用[变量]了,返回前面的json的数据结构;由于文件名这一键是根据文件的不一样随时都会变化的值,因此采用中括号的形式,而typ,url等为固定的字符串,每个文件都具有这类成员名,因此直接用点的形式便可。
接下来只须要将json写入到指定的路径便可:
1 //写入json文件选项 2 function writeJson(data: any, jsonFilePath: string) { 3 fs.writeFileSync(jsonFilePath, JSON.stringify(data, null, 4).replace(/\\\\/g, "/"), 'utf-8'); 4 } 5 writeJson(outjson, "./default.res.json");
我在写入json时遇到了一个问题,就是路径的\老是在写入时实际文件时变为\\,但在控制台打印字符串时又是正常的(迷),因此没办法就用正则表达式全局匹配\\替换为\,至于出现这个问题的缘由到如今尚未弄清楚,若是有大佬发现是什么缘由欢迎告知笔者。
3.生成可执行文件和批处理文件
在环境配置时已经说了pkg安装与运行指令,这里直接在命令行中调用:npm run build便可,由于已经设置了平台为win,build后文件夹中就会出现exe文件。
此时直接点击这个应用程序没有任何效果,由于程序中设置的是须要获得用户输入的命令行参数——搜索的文件夹路径才行,固然了,你能够直接打开cmd来执行该exe并设置参数,但每次都要设置参数未免有些难受,这是就能够写一个批处理来执行当前exe所在路径下的文件查找和生成json,这样即便是程序白痴也能用了。
main.exe .\
pause
打开看一下生成的json是否让人满意:(只截取了一部分)