10 天学会 Angular - 第 1 天 - 第 2 部分






4.77/5 (17投票s)
一个关于最新版Angular的10天系列 - 在本系列的第2部分中,我们将探讨TypeScript的一些更基本概念。
- 下载classExample.zip - 849 B
- 下载templateStringExample.zip - 284 B
- 下载arrowFunctions.zip - 396 B
- 下载looselyCouping.zip - 633 B
- 下载Destructuring.zip - 294 B
- 下载tsNameSpaceExample.zip - 294 B
- 下载tsModuleSample- 1.5 KB
- 下载tsConfigSample - 2.4 KB
- 下载TypeDefinationExample.zip

你在这里,这意味着你已经成功完成了本系列的第1天第1部分。在第2部分中,我们将探讨TypeScript的一些更基本概念。
完整系列
- 第 1 天 – 第 1 部分
- 第 1 天 – 第 2 部分
- 第 2 天
- 第 3 天
- 第 4 天 – 第 1 部分
- 第4天 - 执行技巧
- 第 4 天 – 第 2 部分
- 第 4 天 – 第 3 部分
- 第 5 天(即将推出)
- 第 6 天(即将推出)
- 第 7 天(即将推出)
- 第 8 天(即将推出)
- 第 9 天(即将推出)
- 第 10 天(即将推出)
目录
- 演示8 – 类
- 演示9 – 模板字符串
- 演示10 – 箭头函数
- 演示11 – 接口和抽象类
- 演示12 – 解构
- 演示13 – 命名空间
- 演示14 – 模块
- 演示15 – TypeScript配置
- 演示16 – 使用第三方JavaScript库
- 摘要
演示8 – 类
TypeScript使我们能够实现面向对象编程。它内置了对类和大多数面向对象特性的支持。让我们快速演示一下。
带有简单变量和函数的类示例
尝试下面的代码。
class Customer{ CustomerName:String; Address:String; Save():void{ console.log('Customer Saved:'+ this.CustomerName+"-"+this.Address); } } let c:Customer=new Customer(); c.CustomerName="Customer1"; c.Address="Address1" c.Save(); let c1:Customer=new Customer(); c1.CustomerName="Customer1"; c1.Address="Address1" c1.Save();
编译代码并使用node执行生成的JavaScript代码。
输出将如下所示。
访问修饰符示例
我们可以借助访问修饰符轻松决定类的哪些成员将暴露在类外部,哪些成员不会。默认访问修饰符是“public”。
class Customer{ private CustomerName:String; public Address:String; Save():void{ console.log('Thanks'); } } let c:Customer=new Customer(); console.log(c.Address); console.log(c.CustomerName);
编译将发出以下错误。
带有属性的类示例
属性只是带有getter和setter逻辑的变量的包装。
尝试下面的代码。
class Customer{ private _age:number; get age():number { return this._age; } set age(theAge:number) { console.log('new age received '+theAge) if(theAge<0 || theAge>100){ throw "Invalid Age"; } this._age = theAge; console.log('new age is set '+theAge) } } let c:Customer=new Customer(); c.age=55; c.age=-65;
使用“tsc classExample.ts”命令编译它,您将收到以下错误。
TypeScript默认配置为生成ES3代码。
为了支持属性,至少需要“ES5”。
使用“target”标志重新编译相同的代码,如下所示。
tsc classExample.ts --target es5
这次它将编译。现在使用node执行生成的JS文件。输出将如下所示。
如您所见,第一个成功运行,而第二个生成了运行时错误。
带有构造函数的类示例
构造函数是每个面向对象程序员都熟悉的功能。
它是一种特殊类型的方法,与类名相同,在类初始化时会自动调用。
让我们快速演示一下。
class Customer{ CustomerName:String; Address:String; constructor(){ console.log('Customer new object created'); } } let c:Customer=new Customer(); let c1:Customer=new Customer();
编译并执行,输出将如下所示。
带有简写属性的类示例
如果您是面向对象的程序员,您一定做过类似下面的事情。
class Customer{ CustomerName:String; Address:String; constructor(n,a){ this.CustomerName=n; this.Address=a; } } let c:Customer=new Customer("Sukesh","Mumbai"); let c1:Customer=new Customer("Ganesh","Bengalore"); console.log(c.CustomerName + "-"+c.Address); console.log(c1.CustomerName + "-"+c1.Address);
在构造函数内部,类成员只是用构造函数参数初始化。
编译并执行,输出将简单地如下所示。
相同的代码可以借助简写属性重新编写,如下所示。
class Customer{ constructor(public CustomerName:string; public Address:string;){ } } let c:Customer=new Customer("Sukesh","Mumbai"); let c1:Customer=new Customer("Ganesh", "Bangalore"); console.log(c.CustomerName + "-"+c.Address); console.log(c1.CustomerName + "-"+c1.Address);
编译并执行上述代码将生成完全相同的输出。
只需在构造函数中参数名称前指定访问修饰符,它就变成了属性。
“public”关键字表示它们是公共属性,如果需要,我们也可以将其设为私有。
继承示例
没有继承,面向对象编程是不完整的。
让我们快速演示一下。查看下面的代码。
class Customer{ CustomerName:string; protected Address:string; } class GoldCustomer extends Customer{ Discount:number; constructor(a:string){ super(); this.Address=a; } } let c:GoldCustomer=new GoldCustomer("Mumbai"); c.CustomerName="Sukesh"; c.Discount=100; console.log("CustomerName:"+c.CustomerName); console.log("Discount:"+c.Discount); console.log("Address:"+c.Address);
编译它,您将收到以下错误。
除了public和private,protected是第三个访问修饰符。
protected变量只能在定义的类和基类中访问。
将“Address”的访问修饰符更改为“public”并重试。(您甚至可以尝试相同的示例,不指定任何访问修饰符,因为默认访问修饰符是public。)
这次它将编译。使用node执行它,输出将如下所示。
演示9 – 模板字符串
在TypeScript中,借助模板字符串,字符串拼接变得容易得多。
它将通过一个特殊的符号,即反引号(`)来完成。
查看下面的代码。
let a:string="ValueA"; let b:string="ValueB"; let c:number=55; let text:string=`Value of a+b is ${a}${b} Value of c is ${c}` console.log(text);
注意
用于表示字符串的符号是反引号(`)。位于键盘上的Esc键正下方。
不要与单引号(')混淆。
编译并执行。输出将如下所示。
演示10 – 箭头函数
在JavaScript世界中,将函数作为参数传递是一种非常常见的做法。
TypeScript提供了一种非常方便的方式来定义无状态函数,称为箭头函数。无状态函数是指没有自己状态的函数,它将共享其定义所在外部函数的作用域。简单来说,箭头函数内部的“this”关键字指向词法作用域(外部函数的作用域)。
仍然感到困惑?让我们使用JavaScript的“setTimeout”方法进行快速演示。setTimeout函数简单地期望另一个方法作为参数,并在指定毫秒数后调用该函数。
让我们首先使用传统方法来做。
创建一个名为“arrowFunctions.ts”的新TypeScript文件,其中包含以下代码:
class Customer{ IsSaved:boolean=false; Save(f):void { setTimeout(function() { this.IsSaved=true; //this refers to the instance of current anonymous function (not customer class) f(); }, 100); } } let e:Customer=new Customer(); e.Save(function(){ console.log(e.IsSaved); });
编译并执行它,您将在输出中看到“false”。正如我之前所说,“this”指向传递给“setTimeout”函数的匿名函数的实例。此类问题可以使用箭头函数轻松解决。
使用箭头函数重写相同的代码,如下所示。
class Customer{ IsSaved:boolean=false; Save(f):void { setTimeout(()=>{ this.IsSaved=true; f(); }, 100); } } let e:Customer=new Customer(); e.Save(function(){ console.log(e.IsSaved); });
编译并执行它。输出将是“true”。
箭头函数语法也可以用于函数声明,使其类型安全。
查看下面的代码片段。
... Save(f:()=>void):void { … }
这意味着,save函数期望一个函数类型的参数(无参数且返回类型为void)。
演示11 – 接口和抽象类
没有接口和抽象类,面向对象编程是不完整的。
创建一个名为“looselyCouping.ts”的新TypeScript文件,其中包含以下代码。
interface ILogger{ LogError(e:string):void; SendEmailLog(s:string):boolean; } abstract class AbstractServiceInvoker{ Save(){ let ServiceURL:string=this.GetServiceURL(); let Credentials="UserName:abcd,password:123"; let data:string=this.getData(); //Logic for invoking service is written here. let Result:number=5; //Let’s assume that it is the value //returned by service this.setData(Result); } abstract GetServiceURL():string; abstract getData():string; abstract setData(n:number):void; }
如您所见,我们有一个接口ILogger和一个抽象类AbstractServiceInvoker。
现在首先让我们创建接口的实现。
class Logger implements ILogger{ LogError(e:string):void{ console.log(`error ${e} logged`); } SendEmailLog(s:string):boolean{ console.log(`message ${s} sent in email`); return true; } }
Logger类中必须定义LogError和SendEmailLog方法,否则编译器将发出错误。
现在让我们看看抽象类的实现。
class CustomerService extends AbstractServiceInvoker{ GetServiceURL():string{ return "http://someService.com/abcd" } getData():string{ return "{CustomerName:'a',Address:'b'}" } setData(n:number):void{ console.log(`Success:${n}`) } }
派生类中必须定义所有抽象方法,否则编译器将发出错误。
演示12 – 解构
使用此功能,我们可以非常轻松地从数组中提取值到单个变量中。
它还允许我们轻松地将对象的属性值提取到独立的变量中。
创建一个名为“Destructuring.ts”的新TypeScript文件。
首先让我们尝试对象解构
检查以下代码
var a={CustomerName:'A',Address:'b'}; var {CustomerName,Address}=a; console.log(`${CustomerName}-${Address}`);
编译并执行它。
它将简单地在输出中显示“a-b”。
现在让我们尝试数组解构。
查看下面的代码。
var myArray=[1,2,3,4]; var [v1,v2,v3]=myArray; console.log(`${v1}-${v2}-${v3}`);
编译并执行它。
它将简单地在输出中显示“1-2-3”。
演示13 – 命名空间
在第1部分中,我们看到了JavaScript命名空间的实际应用。我们理解了它的重要性以及实现它的逻辑方式。
现在,在TypeScript中,无需担心编写IIFE或任何其他逻辑来实现命名空间。我们在这里内置了对命名空间的支持。
查看下面的代码。
namespace MathsExample{ export function add(x, y) { console.log(x+y); } function sub(x, y) { console.log(x-y); } export class MyClass{} } MathsExample.add(1,2);
在上面的示例中,只有“add”函数和“MyClass”类在命名空间外部可用。
如果您编译代码,您将发现JavaScript中生成了一个自定义的IIFE逻辑,
var MathsExample; (function (MathsExample) { function add(x, y) { console.log(x + y); } MathsExample.add = add; function sub(x, y) { console.log(x - y); } var MyClass = (function () { function MyClass() { } return MyClass; }()); MathsExample.MyClass = MyClass; })(MathsExample || (MathsExample = {})); MathsExample.add(1, 2);
这是一个简单的JavaScript代码
。要进行测试,请打开命令提示符并尝试以下命令。
node tsNameSpaceExample.js
演示14 – 模块
就像命名空间一样,TypeScript对模块有现成的支持。
当一个TypeScript文件至少包含一个带有export关键字的全局成员时,该文件将成为一个TypeScript模块。模块名称将与文件名相同,但不带扩展名。
TypeScript编译器将以“CommonJS”格式生成输出。这是默认的模块格式器。
让我们看一个例子。
步骤1 – 设置文件夹
创建一个名为“TsModuleSample”的新文件夹,并在其中创建两个TypeScript文件。Reusable.ts和Index.ts
步骤2 – 定义Reusable.ts
将以下代码放入Reusable.ts中
function Add(...values:Array<number>):number{ let sum:number=0; values.forEach(e=>{ sum+=e; }) return sum; } export function AddTwoNumbers(x:number,y:number){ return Add(x,y); }
步骤3 – 定义Index.ts
将以下代码放入Index.ts中
import {AddTwoNumbers} from "./reusable" console.log(AddTwoNumbers(11,12));
注意:如果TypeScript文件名为“A.ts”或“a.ts”,则Typescript模块名为“a”。
步骤4 – 编译它
现在有趣的地方来了。编译index.ts。它将自动编译Reusable.ts
tsc index.ts
打开“reusable.js”文件。它将如下所示。
"use strict"; exports.__esModule = true; function Add() { var values = []; for (var _i = 0; _i < arguments.length; _i++) { values[_i] = arguments[_i]; } var sum = 0; values.forEach(function (e) { sum += e; }); return sum; } function AddTwoNumbers(x, y) { return Add(x, y); } exports.AddTwoNumbers = AddTwoNumbers;
上述代码遵循“CommonJS”模块格式。
注意:默认情况下,TypeScript配置为生成“CommonJS”代码。稍后我们将看到如何更改此配置。
步骤4 – 执行它
使用node命令执行生成的“index.js”。
node index.js
输出将是“23”。
您可能想知道如何在没有模块加载器的情况下成功执行。
Node完美支持“CommonJS”模块格式器。它能很好地理解它。
如果模块格式器更改为AMD或其他,我们将在没有模块加载器的情况下无法执行它。
演示15 – TypeScript配置
TypeScript编译器可以根据我们的需要进行配置。
步骤1 – 创建配置文件
首先创建一个名为“TSConfigSample”的新文件夹,并在其中创建一个名为“tsconfig.json”的新文件。此文件存在于目录中表明它是我们TypeScript源代码项目的根目录。
步骤2 – 创建示例文件
创建一个新的TypeScript文件“configTest.ts”。将以下代码放入其中。
class Customer{ Save():void{ console.log("Thanks"); } }
步骤3 – 编译
打开命令提示符,导航到“TSConfigSample”文件夹并简单尝试以下命令。
tsc
当tsc不带文件名使用时,它将编译当前文件夹中的所有文件。
您将收到一个非常奇怪的错误。
将以下行放入tsConfig.json文件并重复编译过程。
{}
现在编译将正常工作,输出文件将如下所示。
var Customer = (function () { function Customer() { } Customer.prototype.Save = function () { console.log("Thanks"); }; return Customer; }());
步骤4 – 更改配置
默认情况下,TypeScript配置为生成“ES5”代码。它可以设置为ES2015。为此,将以下内容放入“tsConfig.json”文件中。
{ "compilerOptions": { "target": "es2015" } }
现在再次编译它。(确保使用“tsc”而不是“tsc filename.ts”)。
这次输出文件将如下所示。
class Customer { Save() { console.log("Thanks"); } }
探索更多编译器选项
module
此选项使我们能够决定模块格式器。默认选项是“CommonJS”,但也可以更改为“AMD”或其他可用选项。
target
我们期望的目标文件的版本是什么。默认为“ES3”,但可以将其设置为更高版本。
noEmitOnError
默认值为false。如果设置为true,当存在编译错误时,TypeScript将不会生成目标JS文件。
allowUnreachableCode
默认为false。当设置为true时,不报告无法访问的代码错误。
查看下面的代码。
class Customer{ Save():boolean{ if(true){ return true; } return false; } }
如果“allowUnreachableCode”被显式设置为true,则上述代码将编译失败
noImplicitAny
默认值为false。当编译器无法根据变量的使用方式推断变量类型时,编译器将类型视为“any”。例如,查看下面的代码。
Save(s):void{ console.log(s+1); }
在上述情况下,未提及参数类型。“类型推断”根据第一次赋值决定数据类型,在上述情况下,第一次赋值发生在运行时。因此,数据类型将设置为“any”
将“noImplicitAny”设置为true强制我们指定数据类型。每当出现上述情况时,就会抛出错误。
suppressImplicitAnyIndexErrors
您听说过JavaScript世界中的方括号表示法吗?查看下面的JavaScript代码。
var a={"key1": "value1", "key2": "value2"}; var myKey= "key1"; console.log(a[myKey]); // display value1
上述JavaScript代码是有效的JavaScript代码。
现在让我们尝试下面的TypeScript代码,其中“noImplicitAny”设置为true。
class Customer{ CustomerName:string; Address:string; Save():boolean{ if(true){ return true; } return false; } } let c:Customer=new Customer(); c.CustomerName="Sukesh Marla"; c.Address="Mumbai"; let key: string = 'CustomerName'; console.log(c[key]);
编译它,将发生一个奇怪的编译错误。
configTest.ts(23,27): error TS7017: Element implicitly has an 'any' type because type '{ firstKey: string; secondKey: string; thirdKey: string; }' has no index signature.
错误很明显,TypeScript编译器无法推断“c[key]”的数据类型,因此它将被视为“any”。
将“suppressImplicitAnyIndexErrors”设置为true将简单地忽略索引基于访问的“noImplicitAny”。
sourceMap
默认值为false。如果设置为true,编译时将生成一个额外的文件,扩展名为“.js.map”。拥有这些文件可以让外部工具(如Visual Studio、chrome开发者工具等)在运行时调试TypeScript而不是JavaScript。
我们将在使用Angular时看到演示。
removeComments
默认值为false。如果设置为true,编译器将不会在生成的JavaScript文件中发出注释。
experimentalDecorators
TypeScript有一个名为装饰器的特性,它将用于向类、方法、属性等添加额外信息。
如果您有C#背景,它们类似于C#中的属性,如果您有Java背景,它们类似于注解。
在TypeScript中,装饰器可以附加到类/方法/属性/变量/参数等。
语法非常简单。假设我们有一个类装饰器“myClassDecorator”,一个函数装饰器“myFunctionDecorator”,一个属性装饰器“myPropertyDecorator”和一个参数装饰器“myParameterDecorator”。应用它们的代码片段将如下所示。
@myClassDecorator() class Customer{ @myPropertyDecorator()customerName:string; constructor(@myParameterDecorator()s:string){ } @myFunctionDecorator() Save():void{ } }
注意:在本课程中,我们不会创建自定义装饰器,这将超出本系列的范围。我们将更专注于Angular。但我们肯定会在整个课程中使用许多现成的装饰器。
装饰器目前是TypeScript中的实验性功能。它可能会在TypeScript的更高版本中更改。为了在项目中使用装饰器,我们需要在“tsconfig.json”文件中将“experimentalDecorators”设置为true。
为了创建Angular应用程序,将此标志设置为true是必须的。借助装饰器,我们向类/函数/属性等添加元数据。Angular在执行状态后期使用此元数据。
emitDecoratorMetadata
为了使用装饰器,我们还需要将此标志设置为true。它强制Typescript编译器将类型信息与元数据一起保存。
装饰器是一个在与Angular等第三方框架一起工作时非常有用的功能。我们将来会注意到许多框架利用装饰器。借助装饰器,我们将为我们的方法/类/属性等提供一些额外的元数据,这些元数据将在运行时被框架(或一些自动化代码)使用。
简单地说,“experimentalDecorators”使我们能够在TypeScript应用程序中使用装饰器,而“emitDecoratorMetadata”只是将类型信息添加到因装饰器生成的元数据中。
moduleResolution
在我们之前的一个演示中,我们讨论了TypeScript模块。
“moduleResolution”是编译器用于定位表示导入模块的文件的过程。
它可以设置为两个可能的值之一——经典和node。
当模块设置为“AMD”、“System”或“ES2015”时,“moduleResolution”的默认值将是“classic”。在所有其他情况下,默认值将是“node”
导入语句可以以两种不同的方式编写。
- 相对导入 – 以/ 或 ./ 或 ../ 开头的导入
例如,Import {a} from "./mymodule"
- 非相对导入 – 不以任何上述符号开头的导入。
例如,Import {a} from "mymodule"
假设在以下位置创建了一个TypeScript文件。
"H:\Day 1 Source code\TS Examples\TSConfigSample"
它包含以下两个导入语句
Import {a} from "./mymodule"
Import {b} from "mymodule2"
经典
第一次导入将导致以下查找。
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.d.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.ts
第二次导入将导致以下查找。
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule2.d.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule2.ts
- H:\Day 1 Source code\TS Examples\mymodule2.d.ts
- H:\Day 1 Source code\TS Examples\mymodule2.ts
- H:\Day 1 Source code\mymodule2.d.ts
- H:\Day 1 Source code\mymodule2.ts
- H:\mymodule2.d.ts
- H:\mymodule2.ts
节点
第一次导入将导致以下查找。
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.d.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule\somemainfile
查找3只会发生,
- 当前文件夹内有一个名为mymodule的文件夹
- my module文件夹包含package.json
- package.json将有类型属性设置为某个“somemainfile.ts”或“somemainfile.d.ts”文件。
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule\Index.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\mymodule\Index.d.ts
第二次导入将导致以下查找
- H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2.d.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\somemainfile
(与第一次导入中讨论的package.json和类型概念相同) - H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2\index.ts
- H:\Day 1 Source code\TS Examples\TSConfigSample\node_modules\mymodule2\index.d.ts
- H:\Day 1 Source code\TS Examples\node_modules\mymodule2.ts
- H:\Day 1 Source code\TS Examples\node_modules\mymodule2.d.ts
- H:\Day 1 Source code\TS Examples\node_modules\somemainfile
(与第一次导入中讨论的package.json和类型概念相同) - H:\Day 1 Source code\TS Examples\node_modules\mymodule2\index.ts
- H:\Day 1 Source code\TS Examples\node_modules\mymodule2\index.d.ts
- H:\Day 1 Source code\node_modules\mymodule2.ts
- H:\Day 1 Source code\node_modules\mymodule2.d.ts
- H:\Day 1 Source code\node_modules\somemainfile
(与第一次导入中讨论的package.json和类型概念相同) - H:\Day 1 Source code\node_modules\mymodule2\index.ts
- H:\Day 1 Source code\node_modules\mymodule2\index.d.ts
- H:\node_modules\mymodule2.ts
- H:\node_modules\mymodule2.d.ts
- H:\node_modules\somemainfile
(与第一次导入中讨论的package.json和类型概念相同) - H:\node_modules\mymodule2\index.ts
- H:\node_modules\mymodule2\index.d.ts
lib
到目前为止,我们还没有看到我们的全局node模块安装在哪里。它是以下文件夹。
%AppData%\npm\node_modules
在此文件夹中,您将找到为每个已安装的全局node模块创建的一个文件夹。
打开“TypeScript”文件夹。里面有一个名为“lib”的文件夹,其中包含许多TypeScript文件。
如果您检查这些文件的名称,它们大多遵循一个共同的命名约定。所有文件都命名为“*.d.ts”。
“d.ts”文件称为类型定义文件。
类型定义文件是TypeScript文件(命名为“something.d.ts”),其中包含特定库或框架中可用构造的声明。例如,jQuery的类型定义文件包含jQuery函数的声明。
最简单的定义是,TypeScript定义文件是向TypeScript编译器教授新事物的方式。
“lib”文件夹中的文件包含常见JavaScript构造的声明。
例如,它们包含“console.log”、“document”、“Array”、“alert”等的声明。
根据这些定义文件中给出的声明,TypeScript将检查我们的代码并确认我们是否犯了任何错误。例如,“alert”在类型定义文件中声明如下:
declare function alert(message?: any): void;
当我们尝试使用多个参数调用alert函数时,Typescript编译器将发出错误,因为根据声明它只能接受一个参数。
“lib”文件夹中有两个非常重要的文件。它们是包含“ES5”提供的构造声明的“lib.d.ts”和包含ES2015中存在的构造声明的“lib.es6.d.ts”。
(如果您检查这两个文件,您会注意到里面有一些相似的声明。例如,“alert”在这两个文件中都将完全按照上面的方式定义。)
根据我们设置的目标,其中一个文件将自动包含在编译过程中。这些文件只是让我们编写类型安全的代码。
现在假设我们有一个场景,我们希望目标是“ES5”,但我们想使用“ES2015”功能。一旦我们将目标设置为“ES5”,如果我们使用了任何ES2015函数或类,编译器将开始发出错误。
在这种情况下,我们将通过使用“lib”选项来覆盖默认包含。它将让我们手动决定要包含哪些库。
例如,查看下面的“tsconfig.json”文件。
{ "compilerOptions": { "target": "es5", "lib": [ "dom","es2015" ] } }
它将简单地包含两个库文件。
- lib.dom.d.ts – 它将允许我们使用常见的DOM相关构造,如“console”、“document”、“window”等。
- lib.es2015.core.d.ts – 它将允许我们使用ES2015运行时中存在的构造。例如,我们可以使用ES2015 promises。
注意
- ES2015只是ES6的新名称,所以不要混淆它们。
- lib.es6.d.ts与lib.es2015.core.d.ts非常不同。
- 第一个是目标为“ES2015”时的默认包含。它包含JavaScript中几乎所有构造的声明。它包括核心构造(如Array、Boolean等)和DOM构造(如document、window、console等)。
- 第二个只包含JavaScript核心构造的声明,这就是为什么DOM构造需要包含单独的库的原因。
演示16 – 使用第三方JavaScript库
我们已经讨论了TypeScript编译器根据配置设置加载的默认库。这些库是简单的类型定义文件,其中包含核心JavaScript构造的声明。TypeScript编译器仅基于这些文件确认我们代码的有效性。如果任何构造以无效方式使用,它只会发出错误。
现在的问题是,如何在TypeScript中处理第三方JavaScript库?例如,假设我们想在TypeScript中使用“jQuery”函数。TypeScript将无法识别“$”或“jQuery”中的任何其他函数。使用它们将导致编译错误。
解决方案是类型定义文件。除了JavaScript库文件,我们还必须下载相应的类型定义文件。
让我们一步一步地进行演示。
步骤1 – 设置文件夹
创建一个名为“TypeDefinationExample”的新文件夹,并在其中创建一个名为“jQuerySample.ts”的新TypeScript文件
步骤2 – 下载库和类型定义
打开命令提示符。导航到上述文件夹,并使用“npm”下载jQuery,如下所示。
npm install jquery
注意:npm命令区分大小写。请确保“jquery”按原样编写。
上述命令将下载jQuery库并将其放置在node_modules文件夹中。
接下来使用以下命令下载类型定义文件。
npm install @types/jquery
步骤3 – 编写代码
打开“jQuerySample.ts”文件并编写以下代码。
import * as $ from "jquery" $(document).ready(()=>{ $('#MyElement').html("<b>Hello World</b>"); });
(不用担心上面的jQuery代码。我们的目标是Angular而不是jQuery。上面的代码将找到ID为“MyElement”的HTML元素,并将其内部HTML设置为“<b>Hello World</b>”)
步骤4 – 测试代码
在命令提示符中写入以下命令。
tsc jQuerySample.ts
它将导致以下错误。
步骤5 – 解决bug
最新版本的jQuery基于ES2015规范。这意味着我们希望在编译时包含ES2015库。可以通过两种方式完成。
- 创建tsConfig.json文件,并使用“lib”选项与“es2015”和“dom”,然后使用tsc编译它
- 在命令提示符本身中使用“lib”编译器选项。
对于我们的示例,让我们使用第二种方法。
tsc jQuerySample.ts --lib es2015,dom
现在它将成功编译。
步骤6 – 创建源文件
创建一个名为“Test.html”的新HTML文件,如下所示。
<!DOCTYPE html> <html> <head> <title></title> <meta charset="utf-8" /> <script src="../../node_modules/systemjs/dist/system.js"></script> <script> SystemJS.import('./jQuerySample.js'); </script> </head> <body> <div id="MyElement">Loading...</div> </body> </html>
如您所见,我们借助SystemJs加载了“jQuerySample.js”。
步骤7 – 启动服务器
在命令提示符中,只需写入以下命令即可启动Web服务器。
http-server
(“http-server”是我们之前在某个实验中安装的Node模块。如果您尚未安装,请执行“http-server -g”命令进行安装)
步骤8 – 测试
打开浏览器并浏览“Test.html”。您将收到以下错误。
理解问题
在我们的例子中,SystemJs(模块加载器)负责在运行时加载其他文件。这意味着SystemJs无法找到“jQuery”。
发生了两件非常奇怪的事情。
- “jQuery”是“node_modules”中的一个文件夹。这意味着SystemJs正在尝试加载文件夹。模块加载器用于动态加载JS文件,在我们的例子中它正在尝试加载文件夹。
- 其次,“jQuery”文件夹存在于“node_modules”文件夹中,但SystemJs正在根文件夹中搜索它。(仔细检查错误,它说,“Fetch error: 404 Not Found Instantiating http://192.168.0.100:8080/jquery”)。
让我们看看生成的“jQuerySample.js”文件。
"use strict"; exports.__esModule = true; var $ = require("jquery"); $(document).ready(function () { $('#MyElement').html("<b>Hello World</b>"); });
如果您还记得,我们在TypeScript文件中写了以下语句。
import * as $ from "jquery"
因此生成的JS文件包含以下语句。
var $ = require("jquery")
它应该如下所示。
var $= require("/node_modules/jquery/src/jquery.js")
问题的解决方案
解决方案又是“教导”。您必须告诉SystemJs,加载“jquery”意味着加载“/node_modules/jquery/src/jquery.js”。这可以通过创建SystemJs配置文件来完成。
步骤9 – 配置SystemJs
创建一个新的JS文件并将其命名为“system.config.js”。
现在在其中放入以下内容。
(function (global) { System.config({ map: { 'jquery': '/node_modules/jquery/src/jquery.js' }, }); })(this);
在“Test.html”中,在system.src.js之后和main.js之前包含上述文件,如下所示。
<script src="../../node_modules/systemjs/dist/system.js"></script> <script src="system.config.js"></script> <script> SystemJS.import('./jQuerySample.js'); </script>
步骤9 – 重新检查输出
切换到浏览器并刷新页面。
注意:在使用JavaScript时,有时可能会因为缓存而感到困惑。如果输出不符合预期,请务必清除缓存。
理解问题
花点时间思考一下这个错误的原因。
我们在“jQuerySample”中导入了jQuery。同样地,在“jQuery”中导入了许多其他与jQuery相关的文件,“selector.js”就是其中之一。
如果您打开“/node_modules/jquery/src”文件夹,您会在那里看到“selector.js”。
这意味着这次路径是正确的。那么问题是什么?问题是文件名。
它正在寻找一个名为“selector”的文件,我们没有。我们在“/node_modules/jquery/src”文件夹中有“selector.js”。
这个问题的解决方案
解决方案又是教导。我们必须告诉SystemJs为所有请求添加默认扩展名。
步骤10 – 重新配置SystemJs
向“system.config.js”添加更多设置,如下所示。
(function (global) { System.config({ map: { 'jquery': '/node_modules/jquery/src/jquery.js' }, // packages tells the System loader how to load when no filename and/or no extension packages: { '/': { defaultExtension: 'js' } } }); })(this);
查看上面新的“packages”部分。
它包含一个条目,告诉SystemJs为所有对根(“/”)文件夹内文件发出的请求添加“js”扩展名。
步骤11 – 重新检查输出
切换到浏览器并刷新页面。
总结
到此,第二部分结束,TypeScript学习也告一段落。
是时候庆祝了。您成功完成了第一天的所有演示。
现在终于到了Angular的时候了。第二天再见。
敬请期待。
敬请期待。
想了解更多关于我的信息。请访问这里