成都創(chuàng)新互聯(lián)于2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目成都做網(wǎng)站、成都網(wǎng)站建設(shè)網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元古浪做網(wǎng)站,已為上家服務(wù),為古浪各地企業(yè)和個人服務(wù),聯(lián)系電話:18980820575
搭建基礎(chǔ)開發(fā)環(huán)境
要準備的環(huán)境
node.js 14.14以上 ,vs code最新,vs code TS開發(fā)插件
開始開發(fā)
方式一:TS原生編譯開發(fā)
補充知識:i是install的簡寫,-g是global的簡寫,除此外還有-D = --save-dev 、-S= --save,現(xiàn)在新版npm cli好像會默認執(zhí)行—save
npm i -g typescript && tsc -init
vs code創(chuàng)建 文件夾/index.ts,
手動編譯 tsc+文件名,改一次編譯一次
自動編譯 Ctrl+shift+B —> 監(jiān)視模式,文件變動時自動編譯 或 如下:
① tsc 文件名 -w,缺點是只能監(jiān)視一個文件,除非開多個命令行。
② 創(chuàng)建tsconfig.json,只寫一對{},使其符合json規(guī)范,然后直接執(zhí)行 tsc,即可監(jiān)視所有ts文件的改動。
運行結(jié)果:創(chuàng)建index.html, script.import 編譯好的index.js,用瀏覽器打開index.html
開箱即用見 附件【TS_ori】
編譯控制
tsconfig.json是ts編譯器的配置文件,可以對編譯進行自定義
不知道可選值可以先給一個錯誤的值,編譯器會列出所有可選的正確配置值
tsconfig.json可以寫注釋,因為其是ts編譯器的配置文件。
{
// 初步
"include":[ //指定哪些目錄的ts文件需要被編譯,包含目錄
"./src/**/*" //.當(dāng)前目錄 src文件夾 **任意目錄 *任意文件
],
"exclude":[],//指定哪些ts文件不被編譯,排除目錄,有默認值,比如node_modules
"extends":"./config/base",//繼承某個配置文件,配置復(fù)用
"files":[],//指定ts文件編譯,包含文件,與include類似
//進階
"compilerOptions":{
"target":"ES6", //指定ts編譯成何種js版本,即目標代碼的版本
//ES2015(ES6),ES2016...ESNext(最新版ES)
"module":"ES6",//指定模塊化解決方案
"lib":["dom"],//用來指定項目中使用到的庫,一般不寫此屬性(有默認值),除非代碼在nodejs中運行,缺少某些庫,如dom。dom庫為document
"outDir":"./dist",//指定編譯后文件所在目錄。dist即distribution,發(fā)行版
"outFile":"./dis/app.js",//將代碼合并為一個文件,若需要把各模塊合并為一個文件,只能支持amd and system模塊方案
"allowJs":false, //是否對js進行編譯,默認否 - false //合并文件一般不用outFile,而是結(jié)合打包工具來實現(xiàn)
"checkJs":false, //是否檢查js代碼是否符合語法規(guī)范
"removeCommentss":true,//是否移除注釋
"noEmit":false,//執(zhí)行編譯但是不生成編譯后的文件,場景是只用到ts編碼和類型檢查, 不須編譯
"noEmitOnError":false,//發(fā)送錯誤時不生成編后的譯文件,默認為false,為的是讓js程序員緩慢過渡到ts,建議改為true
"strict":false,//以下三個檢查的總開關(guān),建議開發(fā)時設(shè)為true
"alwaysStrict":false,//指定編譯后的js文件是否使用嚴格模式。嚴格模式:語法嚴格,在browser性能好一些
"noImplicitAny":false,//Implicit隱式的,開啟隱式any的檢查,不允許使用
"noImplicitThis":false,//不允許不明確類型的this
"strictNullChecks":false,//嚴格檢查空值,若一個變量可能為null,則報錯,除非先進行非空判斷或box?.addEventListener()
}
}
方式二:自動化開發(fā)
補充知識:webpack-cli是通過命令行來使用webpack、ts-loader是webpack加載器,是ts和webpack的橋梁
補充知識:webpack這東西就是會沿著你給定的一個入口文件去遍歷所有關(guān)聯(lián)的文件,然后按照一定規(guī)則重新整理、壓縮成新的一批文件,我們的文件格式是無窮無盡的,webpack不可能認識和處理全部格式,所以我們通過加入各種loader,稱加載器,來幫助webpack認識它們。
創(chuàng)建空文件夾,執(zhí)行npm init -y(完成項目初始化)
npm i -D webpack webpack-cli typescript ts-loader
撰寫webpack配置。要注意的是,webpack打包必須配合tsconfig.json使用,也就是你的ts代碼處要有這個tsconfig.json文件,因為ts程序的具體編譯的工作還是由ts本身提供,而ts編譯本身要用到tsconfig.json,現(xiàn)在看來,ts-loader真的僅僅是個橋梁。
在package.json中加入打包命令build:webpack,執(zhí)行npm run build。
撰寫配置
使用chrome來查看ts程序的運行結(jié)果
補充知識:webpack可以支持自動生成html文件并引入打包好的資源,以演示代碼效果,這比自己手動寫個html方便多了,生成這個html文件有兩種方式,配置式,如傳個title參數(shù),其他由webpack自己決定,或者自己擬定一個html模板交給插件。
cnpm i -D html-webpack-plugin
自動化構(gòu)建--所寫即所見
cnpm i -D webpack-dev-server ,package.json 加入 "start":"webpack serve --open" ,npm run start
清楚舊的打包文件
打包默認模式是用新文件覆蓋舊文件,可能存在殘留問題,解決方法:clean-webpack-plugin
指定可引入的文件
指定哪些文件可以被其他文件作為模塊來引入,這里的引入是代碼文件之間的引入,這樣我們就可以愉快地使用ESM(ES Module)了
解決目標代碼的兼容性問題
補充知識:前端常見兼容性問題有兩種,一種是瀏覽器內(nèi)核類,一種是規(guī)范版本類。前者主要表現(xiàn)為在Chrome能運行的代碼,在Firefox卻出現(xiàn)問題,在iPhone默認瀏覽器能運行的代碼在華為默認瀏覽器卻有問題;后者主要表現(xiàn)為ES6的代碼在瀏覽器中報錯,因為ES6對比ES5變化是較大的,現(xiàn)在ES規(guī)范每年一個版本,瀏覽器跟進也比以前快了,這個問題正變得越來越不是問題。
TS的tsc僅僅能夠?qū)崿F(xiàn)把ts源碼編譯成不同ES規(guī)范版本的js代碼而已。
babel/core是babel的核心庫、present-env是預(yù)置環(huán)境,預(yù)置不同瀏覽器環(huán)境,幫助代碼兼容不同瀏覽器,babel-loader是結(jié)合webpack和babel的橋梁、core-js(Modular standard library)可以使老版本的瀏覽器使用到新版本的js的一些技術(shù),如promises等,由于這個庫比較龐大,內(nèi)含很多小庫,且是模塊化的,我們應(yīng)按需使用,按需使用我們直接通過webpack來實現(xiàn)
webpack rules的use執(zhí)行順序是從下往上執(zhí)行,我們先用ts-loader把ts轉(zhuǎn)換為js,然后用babel-loader把新版本的js轉(zhuǎn)換為老版本/兼容性高的js
關(guān)于兼容性打包后仍報錯
補充知識:設(shè)置了targets.ie==11后,打包的代碼拿到ie11運行依然報錯,原因在于webpack打包后的代碼用了一個箭頭函數(shù)實現(xiàn)的自執(zhí)行函數(shù)包裹,作用是創(chuàng)造一個代碼作用域,防止全局變量污染等,它實際是webpack自動生成的,與babel無關(guān),babel只能源文件內(nèi)的箭頭函數(shù)起作用,實際上,這可能是webpack故意為之,其本身就是不想兼容某些低版本瀏覽器,解決方法,out加上environment:{arrowFunction:false},取消箭頭函數(shù)。
TS語言
報錯信息,assign:賦值、指派、指定,resolve:解析、決定、解決
默認ts代碼有錯誤,仍然可以編譯生成js,在tsconfig中可更改
ts可編譯成任意版本js
若賦初值,ts會根據(jù)值類型推算變量類型,這會使變量聲明加上類型變得多此一舉,沒錯,實際上,類型檢測更多是用給函數(shù)傳參(形參)和返回值的。
類型
補充知識:可用字面量代替類型名,如10,以后只能賦值10,有點常量的意思。除此之外,還有聯(lián)合類型、任意類型等
talk is cheap,show me the code
export const zex: number = 1;
{
let a: number = 10;
let b: number = 20;
console.log(a, b)
//計算變量類型
let c = true;
//計算函數(shù)返回值類型
function sum(a: number, b: number): string {
return a + b + "";
}
let result = sum(a, b);
}
const zex:number=1;
{
// 字面量賦值
let a:10;
let a1:number;
let b:10=10;
a=10;
a1=10;
// 用 或符號 構(gòu)成 聯(lián)合類型
let c:"male"|"female";
c="male";
c="female";
let d:string|number;
d=10;
d="hello";
// 任意類型 - 關(guān)閉類型檢測
let e:any;
e=10;
e="female";
e=true;
// 隱式any
let f;
f=10;
f="female";
f=true;
// 賦值
// 以下不報錯,這導(dǎo)致a的類型檢測失效
a1=e;
// unknown同any差不多,但是可解決以上問題,是一個類型安全的any
let g:unknown;
// a=g;報錯,應(yīng)改為
if(typeof g == "number"){
a1=g;
}
//斷言:判斷的語言,根據(jù)實際情況,把某個變量人為(自己)地斷定為某種類型,跳過編譯
a1 = g as number;
a1 =
//函數(shù)的返回值
//返回值為number|string型
function sum(a:number,b:number){
if(a>b){
return a+b;
}else{
return a+b+"";
}
}
function sum2(a:number,b:number):number|string{
if(a>b){
return a+b;
}else{
return a+b+"";
}
}
// 空返回
function sum3(a:number,b:number):void{
return;
}
function sum4(a:number,b:number):void{}
function sum5(a:number,b:number):void{
return undefined;
}
// never:永遠不會返回結(jié)果
// 沒有返回值也是一種返回值,而never是空空
// 在程序報錯時,代碼立即停止執(zhí)行,程序結(jié)束,函數(shù)結(jié)束,所以永遠不會有值返回,事情不會發(fā)生
function err():never{
throw new Error("err");
}
}
{
// object其實是無用的,因為ts一且皆對象,并沒有起到類型限制的作用
let a:object;
a={};
a=function(){};
//以下有效
let b:{name:string};
b={name:"John"};
// b={name:"John",age:12}結(jié)構(gòu)不一致報錯
// ?-可選屬性
let c:{name:string,age?:number};
c={name:"John",age:12};
c={name:"John"}
// 任意屬性:自由添加屬性,新屬性未知
// 新屬性名為字符串,屬性值為任意類型,propName命名隨意
let d:{name:string,[propName:string]:any}
d={name:"John",str:123}
let d1:{name:string,[propName:string|number]:any}
d1={name:"John",str:123}
d1={name:"John",123:123}
// 限制函數(shù),單Function無意義
let e:(a:number,b:number)=>number
// 數(shù)組
let f:string[];
f=["John"]
let f1:Array
f1=[123];
// 元組:固定長度的數(shù)組
let g:[String,String];
// 必須符合給定,不多不少
g=["Hello","Hello"];
//考慮數(shù)據(jù)存儲與表示分離,數(shù)據(jù)庫存儲應(yīng)簡短、非字符串,此時Object并不滿足要求
// 枚舉,默認從0開始
enum Gender{
male=0,
female=1
};
let h:{name:string,gender:Gender};
h={name:"Jhon",gender:Gender.female};
console.log(h.gender,h.gender==Gender.female)
// & - 與,類型組合
let i:number&string //無意義
let i1:{name:string}&{age:number};
i1={name:"John",age:18};
// 類型的復(fù)用-別名
let j1:number;
let j2:1|2|3|4|5;
let j3:1|2|3|4|5|6;
type myType=number;
type myType2=1|2|3|4|5;
let k1:myType;
let k2:myType2;
let k3:myType2|6;
k3=3
k3=6
// k3=7 報錯
}
// 類
class Person {
// 實例屬性,通過實例訪問
readonly name: string = "默認名字";
age: number = 18;
// 類屬性,通過類訪問
static avgAge: number = 18;
//只讀屬性
static readonly baseName: string = "張"
// 類方法
sayHello(name: string): string {
return "Hello";
}
static sayHi(name: string): string {
return "Hi";
}
};
const per = new Person();
Person.avgAge = 19;
per.age = 20;
// Person.name="三三"; // 報錯
// Person.baseName="李"; // 報錯
console.log(Person.avgAge, per.age);
// 構(gòu)造器與this
class Dog {
name: string;
age: number;
constructor(name: string, age: number) {
// this表示當(dāng)前實例
this.name = name;
this.age = age;
}
bark() {
// 當(dāng)前調(diào)用方法的對象,如dog1.bark(),this為dog1
console.log(this, "旺旺旺");
}
}
const dog: Dog = new Dog("旺財", 3);
// 繼承
{
class Animal {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
bark() {
}
}
class Dog extends Animal {
bark() {
console.log(this.name + this.age + "旺旺旺");
}
run() {
console.log("蹦蹦跳跳");
}
}
class Cat extends Animal {
sex: string;
constructor(name: string, age: number, sex: string) {
// 調(diào)用父類構(gòu)造器
super(name, age);
this.sex = sex;
}
bark() {
// 引用父類的方法
super.bark();
}
}
const cat = new Cat("小喵", 3, "母");
//抽象類
//對于某些類,由于本身拿來實例化是不合適的,且我們也不希望被這樣做
//因此我們就把他設(shè)為抽象類,只可以繼承,不可以實例化
abstract class Food {
name: string;
color: string;
// 抽象類須有構(gòu)造器
constructor(name: string, color: string) {
this.name = name;
this.color = color;
}
abstract eat(name: string): void;
abstract cook(name: string): number | string;
}
/**
* 在限制對象的類型上,以下兩種方式功能一致
* 接口,定義了類的結(jié)構(gòu)(屬性、方法)
* 此接口非彼接口,它與Java的接口有點不同
*/
type myType = {
name: string,
age: number
};
interface myInterface {
name: string;
age: number;
}
// 接口可以重復(fù)定義,實際效果是同名接口的總和
interface myInterface {
sex: string;
}
const man: myInterface = {
name: "張三",
age: 18,
sex: "男"
}
// 限制類的結(jié)構(gòu)
interface myInterface2 {
name: string;
sayHi(): void;
}
interface myInterface3 { }
class People implements myInterface2, myInterface3 {
name: string;
constructor(name: string) {
this.name = name;
}
sayHi(): void {
console.log(this.name);
}
}
/**
* 抽象類和接口
* 抽象類:
* 1、可以普通方法,也可以抽象方法
* 2、通過繼承來使用,TS 類的設(shè)計為單繼承
*
* 接口:
* 1、只有抽象方法
* 2、通過實現(xiàn)來使用,支持多繼承
*/
}
// 屬性的封裝 - 保護類的屬性
// 屬性可被隨意修改將導(dǎo)致對象中的數(shù)據(jù)變得不安全
// 通過類修飾符解決這個問題
// 默認為public,屬性可被隨意修改
{
class Person {
name: string;
public sex: string;
public age: number;
constructor(name: string, sex: string, age: number) {
this.name = name;
this.sex = sex;
this.age = age;
}
}
// 最強安全性
// private修飾的屬性,只有兩種修改方式:
// 1、構(gòu)造器傳入
// 2、調(diào)用實例方法修改,這種方法在java中被稱為setter
// 此外,這種方式也給屬性的訪問帶來麻煩,我們同樣只能通過方法return該屬性來訪問,在java中稱為getter
// 這種setter和getter模式在C#中被吸收為語法:
/**
* get{
return _name;
}
set{
_name = value;
}
*/
class Person2 {
private name: string;
private sex: string;
private age: number;
protected height: number;
constructor(name: string, sex: string, age: number, height: number) {
this.name = name;
this.sex = sex;
this.age = age;
this.height = height;
}
setName(name: string): void {
this.name = name;
}
getName(): string {
return this.name;
}
}
// 此外,private屬性也不可以被子類訪問,protected可以
class Person3 extends Person2 {
showName(): void {
// 無法訪問name,可以訪問height
// console.log(this.name);
console.log(this.height);
}
}
// 泛型
// 當(dāng)一種類型是什么要在實際中才能確定是,我們使用泛型
// 泛型就是類型的抽象表示(代表)
// 當(dāng)以下函數(shù)的參數(shù)與返回值類型一致,但是無法確定是什么具體類型時,用泛型
function fn(a: number): number {
return a;
}
// 能否使用any?不行,一是關(guān)掉了類型檢查,這將埋下隱患,二是無法體現(xiàn)兩者類型一致
function fn2
return a;
}
function fn3
return a;
}
function fn4
return c;
}
// 在調(diào)用含泛型函數(shù)時,一般會自動推定泛型的類型,我們也可手動指定
fn2(1);
fn4(1, "2", 3);
fn4
}
【附件】
https://github.com/heytheww/TSLenrning