1.class的继承与实现:class可以继承单个类,实现多个接口,这也是java中的单继承、多实现。指定要实现的接口后,则必须提供好接口中的内容。//定义一个动物类classAnimal{name:string;//名称age:number;//年龄//构造函数constructor(name:string,age:number){this.name=name;this.age=age;}}//小狗类继承动物类,类只能继承一个类classDogextendsAnimal{variety:string;//品种constructor(name:string,age:number,variety:string){//初始化父类的构造函数super(name,age);this.variety=variety;}}//定义一个动物的接口interfaceAnimalMethods{eat():void;//吃饭}//定义一个猫的接口interfaceCatMethods{cry():void;//叫}//小猫类继承动物类,并且实现动物接口和猫接口classCatextendsAnimalimplementsAnimalMethods,CatMethods{constructor(name:string,age:number){//初始化父类的构造函数super(name,age);}//吃饭eat(){console.log('喵喵~好吃');};//叫cry(){console.log('喵喵喵~');};}2.权限修饰符在TypeScript中,class类的可见修饰符共有三种。public:默认可见性,可以在任何地方被访问。protected:受保护的,仅在自己的类中或者子类中可访问,不许通过实例对象访问。private:私有的,只有自己的类中才可以访问。protected示例:classFather{protectedsay(){console.log('大家好~我是爸爸昂!');}}classSonextendsFather{publicsay(){//由于父类中的say()被设置了protected可见类型,所以在子类中是可以访问的。super.say();}}//由于父类的say()方法设置的是protected,所以不允许通过实例对象访问,这里会报错。newFather().say();private示例:classFather{privatename:string;constructor(name:string){this.name=name;}}classSonextendsFather{constructor(name:string){super(name);}say(){//这里是报错了,由于父类的name属性设置了private,只有它自己的类中才能访问。console.log(super.name);}}3.只读属性关键字:readonly,它用于防止在构造函数之外去修改成员属性的值。只允许初始化时赋值或者在构造函数中对成员变量赋值。classPerson{readonlyname:string='';constructor(name:string){//在构造函数中是允许修改的this.name=name;};//该函数会报错,因为被readonly修饰的属性,只允许在构造函数中被修改。setName(name){this.name=name;}}如果在声明时,没有指定属性的类型,并且指定了初始值,则类型就成了字面量类型,然后就不允许修改成其他的了。classPerson{//此时,name的类型就成了字面量类型:匿名。readonlyname='匿名';}readonly还可以在接口或者其他上进行使用。interfacePersonInterFace{readonlyname:string;}constp1:PersonInterFace={name:'p1人'};//由于接口中的name属性被readonly设置成了只读的,所以不能再被修改了。p1.name='';
1.typeof关键字typeof在TypeScript中,不仅保留了检测类型的作用,还可以进行类型查询。//假设我们已经声明了一个变量,并且已经有了对应的值constperson={name:'甄雨锋',age:20};/***可以使用typeof关键字判断p参数,是否和person对象中的参数相同|p:{name:string;age:number}*-在使用typeof时,如果右侧是对象,那么p的值,就应该包含右侧对象的每个属性,及属性的类型。*/functioncreatePerson(p:typeofperson){}typeof只能用于查询变量属性类型,右侧不能是接口或者函数的返回值。lettemp1=1;//检测temp2的类型,是否和temp1的类型相同。lettemp2:typeoftemp1=3;lettemp1={name:'甄雨锋',age:20};//检测temp2的类型,是否和temp1对象中的name属性类型相同。lettemp2:typeoftemp1.name='张三';
1.枚举类型作用:枚举用于定义一组可选值,使用enum关键字声明。/***定义一组方向枚举*-枚举的值无需使用""等包裹起来。*/enumDirection{Top,Right,Bottom,Left}/***定义一个方向函数*-将该函数的参数,设置为枚举类型,这样就只允许接收4个方向了。*/functioneditDirection(dir:Direction){//...}//调用函数editDirection(Direction.Top);/***其实,上面就是一个扯淡的写法,还不如直接:联合类型搭配字面量类型*-functioneditDirection(dir:"Top"|"Right"|"Bottom"|"Left"){};*/我觉得让枚举去声明一组状态码更比较合适,假设我们在做一个物流的需求。//定义一组物流状态码enumLogisticsStatus{notYetShipped=0,//待发货shipped=1,//已发货haveBeenSigned=2,//已签收}//编辑此订单的状态functioneditStatus(status:LogisticsStatus){//...}//将该订单的状态更新成已签收editStatus(LogisticsStatus.haveBeenSigned);2.枚举的值枚举中的每个成员,都会有一个默认值。第一个成员属性的默认值:0,第二个就是1,依此往后推。我们还可以为枚举的成员设置值(数值、字符串)。如果第三个枚举成员的值被指定成了9,那么第四个成员的值就自动成了10。如果第五个枚举成员的值被设置成了一个字符串,那么第六个成员我们就必须要为它手动指定值,如果第六个我们指定的是一个数值,则后面的就又会递增,如果还是设置的字符串类型,那么还需要手动向后指定。
1.定义接口定义接口关键字:interface,它和type(类型别名)作用相同,用于描述一个内容的结构。//人类基本信息的接口interfacePersinInfo{name:string;age:number;form:string;sex?:string;};//定义一个人的对象,去实现接口constp1:PersonInfo={//当实现接口后,需要将接口中定义的属性,全部实现。name:'甄雨锋',age:20,form:'地球',//由于sex设置了可选,所以在实现时,可以不填.};2.类型别名和接口的区别:接口:只能为对象指定类型。类型别名:不仅可以为对象指定类型,还能把好几个类型包括起来,给他指定一个名称,然后供其他变量使用。3.接口继承继承关键字:extends,当A接口继承B接口后,A接口就拥有了B接口中的所有的属性或方法了。当实现A接口时,也需要将B接口中的属性写上,因为A接口继承B接口了。如果B接口继承了C接口,那么在实现时,就需要将这三个接口中的属性都写出来。接口可以多继承,就是一个接口继承多个接口,每个继承内容使用逗号分隔。在继承时,两个接口中,不允许有相同的内容。interfaceB{age:number}//继承B接口interfaceAextendsB{name:string}//实现A接口时,需要将A、B两个接口实现.constperson:A={name:'甄雨锋',age:20};4.函数接口//运算函数接口interfaceIArithmetic{//这个是函数的函数体(operation:"add"|"subtract",val:number[]):number;//这里定义的是函数中的属性add:(val:number[])=>number;subtract:(val:number[])=>number;}
1.原始类型原始类型就是JavaScript中已有的六种类型。注意!!类型注解都是小写的、并且一旦明确类型,则值类型只能是声明的类型。letage:number=20;letmyName:string='小明';letsex:boolean=true;letfamilyId:null=null;letaddress:undefined=undefined;lets:symbol=Symbol();2.数组类型格式:数组类型一共有两种写法。变量名后加一个类型注解,类型注解后跟着一个[]。constarr:string[]=[];还可以添加一个Array的泛型,并在声明的时候指定泛型类型。constarr:Array<string>=[];2.1写法:下面两种声明的数组,只可以添加字符串类型的元素,如果包含其他类型元素,就会报错。constarr1:string[]=['1','2'];//写法一:推荐constarr2:Array<string>=['1','2'];//写法二如果元素支持字符串和数值,则可以使用联合类型(|)。//数组中的元素,可以出现number和string类型。constarr3:(number|string)[]=['1',2];/***注意:这里一定要使用()包围起来,不然就会成为另一种意思.*-constarr:number|string[]=???;*-上面的这段代码的意思是:arr的值只能是一个number的常量,或者是一个字符串类型的数组。*/constarr4:Array<string|number>=['1',2];//使用Array泛型,则不需要写(),因为它已经声明是一个Array类型的了。3.联合类型|在TS中叫做:联合类型,表示取值可以为多种类型中的一种。//temp的值可以是string类型,也可以是number类型。lettemp1:string|number;temp1='Hi~';temp1=666;//在数组中,也可以理解成`或`的意思,在下面两行代码中,如果多输一个其他类型元素,就会报错。//此时,如果你觉得上面这个是正确的理解,那你就错了,请看下一段注释!letarr:(string|number)[]|(boolean,undefined)[]=['1',2];letarr:(string|number)[]|(boolean,undefined)[]=[true,undefined];/***在上面两行的理解中,其实理解的和设计者的理解是有一点误区的,设计者是这样理解的:*-一开始我们选择的是联合类型右侧的:Array<string|boolean>。*-正如联合类型的解释一样:取值时可以为联合类型多种类型中的一种。*-我们的arr是可以重新赋值的,在重新赋值时,我可以选择联合类型左侧的那一种。*/letarr:Array<number>|Array<string|boolean>=['1',true];arr=[1,2,3];4.类型别名类型别名:相当于为N个类型,定义成一个变量(类型)。关键字:type自定义类型名称=value;/***假设我们有两个数组,每个数组中的元素都可以存储为:JS的五个基本类型。*-通过下面两行代码可以看出,代码很冗余,两个数组的类型都是一样的。*-并且,如果两个数组的参数都需要变化[相同],还要一个一个去改。*/constarr1:(string|number|boolean|undefined|null)[]=[];constarr2:(string|number|boolean|undefined|null)[]=[];/***这时,我们可以使用`类型别名`,相当于将这几个类型,定义成一个变量。*///定义类型别名typejsBasicType=(string|number|boolean|undefined|null);/***数组:使用类型别名,以下代码相当于:*-constarr1:(string|number|boolean|undefined|null)[]=[1,'2',true];*/constarr1:jsBasicType[]=[1,'2',true];//基本变量:使用类型别名lettemp1:jsBasicType=1;temp1=true;//将string类型,定义一个别名:NametypeName=string;//NameResolver的类型是一个:函数,并且这个函数的返回值是string类型typeNameResolver=()=>string;//NameOrResolver:联合类型->传递一个string或者传递一个返回string类型值的函数。typeNameOrResolver=Name|NameResolver;//n:接收一个string或者函数&&Name:该函数返回string类型。functiongetName(n:NameOrResolver):Name{//如果n是字符串,则直接返回一个字符串。if(typeofn==='string'){returnn;}else{//如果N不是字符串,则返回调用n()返回的string。returnn();}}5.函数类型函数类型:指的是形参的类型和函数返回值类型。5.1:单独指定参数和返回值类型//n1和n2都是接收一个number类型,但是该函数返回的内容要求是一个string。functionadd(n1:number,n2:number):string{return`${n1}+${n2}的合为:${n1+n2}`;//返回的是一个string}5.2:同时指定参数和返回值类型注意!!这种形式只适用于函数表达式(不是使用function声明的)。//看着已经懵逼了吧~constadd:(n1:number,n2:number)=>string=(n1,n2)=>`${n1}+${n2}的合为:${n1+n2}`;/***以上代码,可以分为这几部来理解:**constadd:(n1:number,n2:number)=>string;*-add的类型为:一个函数,并且函数的n1和n2都是number类型,该函数的返回值为string。*=(n1,n2)=>`${n1}+${n2}的合为:${n1+n2}`;*-这句代码就是在实现前面的函数,前面已经指定了参数类型及返回值,实现时则不需要在指定。*/看完同时指定参数和返回值类型后,可以回想一下这句代码。//这不就是将一个待实现的函数表达式赋值给了NameResolver类型别名吗!typeNameResolver=()=>string;/***如何实现该表达式?*-形参t:接收一个空参数,并且返回string类型的函数。*/functiontest(t:NameResolver):string{//这个函数返回的是一个string,于是我们可以直接返回该函数的返回结果,同样是string类型。returnt();}//实现test(()=>'Hi~');5.3:函数可选参数:在为函数传递参数时,有些参数我们可以设置非必填参数,只需要在该参数后面,类型前面加上一个?号即可。注意!!可选参数后面,不允许再出现必选参数。//这段代码:第一个参数是必填的,后面两个参数可以不填。functiontest(t:number,start?:number,end?:number){}5.4:函数形参的默认值在TS中,如果函数的形参设置成了一个可选参数,那么我们可以为它指定一个默认值。需要注意的是:要在类型后面指定默认值。//如果使用的是该方式声明函数,在设置默认值时,需要在实现函数时的参数中设置。constsayHi:(value?:string)=>void=(value='你好!')=>{console.log(value);}sayHi();functionsayHi(value:string='你好!'):void{console.log(value);}6.void类型如果该函数没有返回值,那么默认就是void类型。functiontest():void{console.log('Hi~');}7.对象类型设置对象类型,可以有以下几种方式。7.1:描述对象的结构,每个结构使用;分隔在声明对象时,对象中的属性必须要和描述的结构一致,不要多也不要少。const变量:{对象中的key:类型;...}={key:value,....};/***如果对象的属性中,拥有函数,那么在描述对象的结构时,可以这样描述函数。*-constperson:{函数名称(参数①:类型):返回值类型}={};*-constperson:{sayHi(value:string):void}={};**-constperson:{函数名称:(参数①:类型)=>返回值类型}={};*-constperson:{sayHi:(value:string)=>void}={};*/constperson:{name:string;age:number;sayHi(value:string):void}={name:'甄雨锋',age:20,//由于在描述对象结构时,已经声明了该函数的参数类型,在这里无需再次声明。sayHi(value){console.log(`${name}说:${value}`);},};7.2:对象类型直接写成objectconstperson:object={};7.3:对象的可选属性在对象中,也可以设置可选属性,同样是使用?号修饰。//form属性可以实现,也可以不实现。constperson:{name:string;age:number;form?:string;}={};8.never类型nerver类型:指那些用永不存在的值类型。死循环:如果进入死循环,则该函数就没有返回值。在函数中必定抛出一个异常时:如果抛出异常,则函数就中断执行,则无法返回。当变量的外层被永不为真的条件包围时:这样它就不会被执行,它的值为never。//死循环functiontestFun1():never{while(true){//永远都在这里执行,不会有返回值.}}//抛出异常functiontestFun2():never{//抛出一个必定的异常,如果后面有代码,肯定是会中断,所以就会无法执行。thrownewError();}9.元组元组可以限制数据有多少个数据,并且准确规定数组中每一个位置对应的类型。//假如说:我们要使用数组存储一个地理位置,那么它有经度和维度/***注意:*-元组类型中,有几个数据,所对应的数组中就应该包含几个值。*-数组中每个位置的值,都要对应元组位置中的类型。*/constposition:[number,number]=[39.5412,116.2317];10.字面量类型conststr1='您好';letstr2='不好';/***对于上面两行代码,会惊奇的发现,str1的类型竟然是`您好`。*-这是因为它是被const声明的常量,既然不能被修改,所以将它的类型设置成了初始值。*-这种类型,叫做字面量类型。*/字面量类型的用法:/***这是一个移动函数。*-参数接收一个方向:并且方向只能是上下左右。*这时,我们可以使用联合类型去给参数设置字面量类型。*//***这段代码的意思就相当于:*-direction的值(类型)只能为:'up'|'down'|'left'|'right',不能为其他值。*/functionmove(direction:'up'|'down'|'left'|'right'):void{}11.any类型在TypeScript中。不推荐使用any类型。这会让TS失去类型保护的优势。当类型为any时,可以对该值进行任意操作,并且不会有任何提示。当声明一个变量未赋值时,或者没有给形参执行类型时,它们会被当成隐式any类型。lettemp;temp=1;temp='1';functiontest(t){}test(1);test('1')
描述今天在开发小程序的时候,发现了一个问题。在测试的时候,就发现日历组件不展示日期了,然后我用开发者工具和手机打开后,是正常的,只有苹果手机才不显示,最后看源码,最后在大神的指导下,才改回来的。就是给newDate()传递构造参数,让它去解析的时候,如果年月日之间的分隔符,使用的是-,那么在苹果手机返回的就会有问题,它就返回一个NaN。代码复现newDate('2022-9-2023:41').getDate();这一串代码,在苹果手机中,返回的是一个NaN,而在安卓端,则返回正常的内容。解决办法:直接使用String.replace(正则匹配,替换内容)函数,通过正则表达式,将-全部替换成/,如果存在T,则也需要将T替换成空格即可。这种方式,对苹果和安卓都是兼容的。//全局搜索-,将它替换成/,然后拿着被替换完的字符串,链式再调用,去匹配T,替换成空格。newDate(testDateTime.replace(/-/g,'/').replace(/T/g,''));
TypeScript是JavaScript的一个超集,优化了JavaScript中没有静态类型的缺点。并且在编译时,可以发现一些错误,在vscode中,当出错时,未当编译时就会出现红色波浪线。1.安装提示:在安装TypeScript之前,需要先安装Node.npminstall-gtypescript当安装之后,全局中则多了一个tsc命令。2.编写及编译在安装TypeScript之后,我们则可以创建以.ts为后缀的文件了,以.ts为后缀的文件,里面就是TypeScript代码。当TypeScript文件中的代码写完之后,我们需要对它进行编译成JavaScript文件,就像Java一样,需要将.java文件编译成.class。需要在终端中执行以下命令,进行编译。tsc文件名.ts当执行完以下命令后,则生成了一个编译好的.js文件。vscode报错问题:在vscode中,如果不生成tsconfig.json配置文件,在编译TypeScript生成JavaScript文件后,就会报重复定义的错误。解决办法:只需要生成一下该文件即可[无论是用tsc命令生成,还是手动创建一个tsconfig.json文件]。3.编译错误在编译的过程中,会检查你编写的TypeScript代码有没有问题,如果出现了问题,那么在终端中则会显示哪里出现了问题。但是,它并不会影响生成JavaScript文件,就算编译时出现了错误,它依旧会生成JavaScript文件。我们可以在tsconfig.json中把noEmitOnError这个选项解开注释使其生效,这样当TypeScript编译错误时,就不会在生成JavaScript文件了。4.tsc命令1.编译某个文件tsc文件名.ts2.监听某个文件,变化时自动编译tsc文件名.ts--wach3.一次性编译所有文件tsc4.监听所有文件的变化,变化时自动编译tsc--watch5.生成配置文件tsc--init5.配置文件我们可以通过tsc--init命令去生成一个配置文件,在终端运行以下命令。tsc--init当运行完如上命令后,在文件夹中则会生成一个tsconfig.json文件夹。该文件中,存储的都是配置选项,noEmitOnError选项就在其中[TypeScript编译错误时不生成JavaScript]。在该JSON文件中,是可以写注释的,并且它一共有5个大选项。compilerOptions:编译选项,生成配置文件时默认就带该选项。files:单独需要编译的文件,此次编译只会编译里面的文件。include:指定文件夹,此次编译文件夹中的文件。exclude:不编译该文件夹中的内容,默认值为:node_modules…extends:表示继承的其他配置文件。compilerOptions说明:编译选项.target:说明:将TypeScript编译成ECMAScript哪个版本?"compilerOptions":{"target":"es2015"或者"target":"es6"}outDir:说明:默认情况下,是将编译后的.js文件放到根目录的,我们可以通过该选项去指定编译生成的.js文件放到哪个位置。假如main.ts放在了src目录下,并且将outDir设置成了./js,那么编译后,这个被编译生成的main.js是放在./js/src/main.js中的。"compilerOptions":{"outDir":"./js"}module:说明:该选项可以选择模块化系统。可选值:CommonJS【require】、Es6【import】、UMD、AMD、System、ES2020、ESNext."compilerOptions":{"module":"CommonJS"}files说明:如果在该数组中指定了文件路径,那么此次编译就只会编译元素中的文件。提示:该选项的元素中,只能指定一个单独的.ts文件,不能指定文件夹。"files":["./src/index.ts"]extends说明:继承其他配置文件的配置。"extends":"./src/tsconfig.json"include说明:每个元素的内容为一个文件夹地址,编译时会编译文件夹中的文件。提示:可以选择通配符,详情看代码注释。"include":[//编译src文件夹下的,所有文件夹[**]中的所有文件[*]"./src/**/*"]exclude说明:数组每个元素是一个地址,对这些元素中的地址不进行编译。默认值:node_module…"exclude":["..."]
今天在项目开发时,由于素材都是存在本地了,然后还没压缩,导致小程序项目超出了2MB,然后无法编译了,就了解了一下分包,在采取分包后,顺利解决!本篇文章中,小程序采用的是Uniapp框架,和原生小程序中的分包流程相同,只不过无需修改manifest.json文件。分包简介在微信小程序中,默认只有一个主包,在项目打包时,所有的资源(页面及资源等)都被放到了主包中。造成的问题:在小程序中,主包的体积最大只可以达到2MB,如果超出了2MB,则不通过了。对于以上的问题,可以采取分包,分包就相当于将指定的页面及资源,单独放在一个包中,不在放到主包中,从而减少主包的体积。分包的上限及条件:开发者可以分N个包,但是要求每个包不允许超过2MB,整个小程序所有分包大小不能超过20MB。分包的好处:主包中一般存放的都是Tabbar页面,如果将非Tabbar页面从主包中移除,则可以加快加载速度。分包中的内容,默认不加载,只有需要用到它时,再去现场加载,当然,可以通过分包预下载去提前加载分包中的内容。使用分包-uniapp注意:如果是Tabbar页面,或者是多个分包需要用到的公共资源,请放到主包中。分包可以访问主包中的资源,而主包不允许访问分包中的资源。假设:现在有一个用户端[公共的],一个管理端[需要权限],我们可以将用户端放到主包中,将管理端的内容分成一个包。因为用户在进入小程序时,默认进入的都是用户端,没必要将管理端的资源也加载上,只有当拥有权限并且在切换到管理端时,我们在去加载管理端的内容。//当前文件结构树[未分包],默认都存放到pages中,当分包后,pages中的内容就是主包的内容。pages:|-...//假设用户端页面有N个,这里就不写了|-backStage/index/index.vue:管理端首页|-backStage/xxxxx/index.vue:管理端第二页/*分包准备:1.新建一个文件夹,和pages同级,*每个文件夹相当于一个分包*,并将静态资源命名为static然后放到分包的根目录下。2.在该分包下新建一个pages文件夹,该文件夹中存储页面文件夹。3.然后将分包中的内容,从主包中移走。*///主包pages:|-...//用户端的内容//管理端分包backStagePackage:|-pages:管理端页面|-index/index.vue:管理端首页|-xxxxx/index.vue:管理端第二页|-static:静态资源然后打开manifest.json文件,在mp-weixin节点中的根节点下,添加配置:"optimization":{"subPackages":true}接着打开pages.json配置分包:在和pages同级节点下,添加subPackages字段,值为数组,它用于配置分包。它的每一个元素是一个对象,每个元素及为一个分包。使用管理端分包为例,首先在数组中添加一个对象,对象中拥有:root,pages,name,independent字段。root属性:它的值为分包文件夹的名称,即:backStagePackage。pages属性:它的值为一个数组,每一个元素填写分包中的页面路径即可(不需要加上分包名)。name属性:分包别名,分包预下载时可以使用。independent属性:是否开启独立分包,默认不开启(false)。"subPackages":[{"root":"backStagePackage","pages":["pages/index/index","pages/xxxxx/index"]}]
1.说明:mockjs可以提供假数据,其原理就是拦截所有发送的请求,如果此次请求的url中在mock存在,则拦截下来,然后返回Mock中对应的内容。2.安装npminstallmockjs--save3.模块化新建一个mock文件夹。新建index.js文件:该文件只负责引用其他文件。新建对应的模块文件,每个模块可以对应一个js文件。然后在main.js中,只引入mock/index.js即可。4.使用假设现在有一个商品模块,则在mock文件夹下创建一个shopping.js。//引入MockimportMockfrom'mockjs';//然后在下面编写路由及控制器/*Mock.mock(path,method,controller)-path<String|正则>:路由地址-methods<String>:请求方式-controller<Function|Object>:-Function:可以接收到一个<config>参数,它存储着请求的信息,例如参数等...然后通过return返回内容。-Object:返回的对象-在返回时,支持写Mock语法*///获取全部的商品信息Mock.mock('/mock/api/shopping','get',{code:200,//该数组中,有100条对象"data|100":[{id:'@id',name:'@name'}]});5.注意(超级重要,踩过坑)在配置axios时,如果你要配置baseURL。千万不要写http://localhost,直接写你mock路由地址的前缀即可,忽略本地地址!忽略本地地址!Mock.mock('/mock/api/shopping','get',{...});baseURL:'/mock'然后在发送请求时,只需要写/api/shopping即可。