TS入门006函数上

函数

1
2
3
4
5
6
7
8
9
10
11
function hi(){ // hi == address 100
console.log('hi')
}

let hi2 = function (){ // hi == address 390
console.log('hi')
}

let hi3 = ()=>{ // hi3 == address 401
console.log('hi')
}

函数参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 带参数
function hi(name: string,age: number){
console.log(`hi,${name},${age}`)

}

// 可选参数
function hi2(name: string,age?: number){
console.log(`hi,${name},${age}`)
}

// 默认值
// 为什么不声明 age的类型 因为这个类型可以推断
// age = 18 等价于 age: number = 18
function hi3(name: string,age = 18){
console.log(`hi,${name},${age}`)
}

hi('hjx',18) // hi,hjx,18
hi2('hjx') // hi,hjx,undefined
hi3('hjx') // hi,hjx,18

函数返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 什么都不返回
function hi4(name: string,age?: number):void {
console.log(`hi,${name},${age}`)
}

// 返回string
function hi4(name: string,age?: number):string {
if (age>18){
return 'hi'
}else{
// return 404 //报错 因为你指定了返回 string
return '404'
}
}

// 或者这样写
// 返回string 或 number
function hi5(name: string,age?: number):string | number{
if (age>18){
return 'hi'
}else{
return 404
}
}

函数内置的 this 和 argument

  • arguments
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function add(a: number,b: number): number{
//let a = arguments[0]
//let b = arguments[1]
// a,b是形式参数
return a + b;
// 可以这样
// return arguments[0] + arguments[1]
}

add(100,200) //实际参数
/*
调用add(100,200)时 会构建这样一个对象
arguments = {
0:100,
1:200,
length:2
}
*/
  • this

很难的一个东西,根源就是你不用call

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function add(a: number,b: number): number{
return arguments[0] + arguments[1]
}

add(100,200) //小白调用法
/*
调用add(100,200)时 会构建这样一个对象
this = ?
arguments = {
0:100,
1:200,
length:2
}
*/

add.call('fuck',100,200) //大师调用法
/*
调用add(100,200)时 会构建这样一个对象
this = 'fuck' //call的第一个参数
arguments = {
0:100,
1:200,
length:2
}
*/

this 问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function printThis(){
'use strict'
console.log(this);
console.log(arguments);
}

printThis.call('fuck',100,200)
/*
this = 'fuck'
arguments = {
0:100,
1:200,
length:2
}
*/

printThis(1,2);
/*
this = ?
如果是浏览器 this => window
如果是浏览器,并开启严格模式 this => undefined
如果是node是 global

arguments = {
0:100,
1:200,
length:2
}
*/

为啥this这么难? 因为 js设计错误

第二个情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let obj = {
fn(){
'use strict'
console.log(this);
},
obj2:{
fn2(){
console.log(this);
}
}
}

obj.fn();
// this是 .fn前面的东西 obj
obj.obj2.fn2();
// this是 .fn2 前面的东西 obj.obj2
obj.fn.call('fuck')
// this => 'fuck'

第三个情况

1
2
3
4
5
6
7
8
9
10
let obj = {
fn(){
'use strict'
console.log(this);
}
}
// 第三个情况
let fn3 = obj.fn;
console.log('---');
fn3(); // this => ? undefined

箭头函数

  • 干掉了 this,arguments 因为认为这是js的错误不该在 es6里出现
1
2
3
4
5
let fn = (a: number,b :number): number=>{
console.log(this) //外面的this
// 箭头函数内外 this 一致
return a + b; // 没有 this,arguments
}

TS入门005类

  • 接口是低配版的类.
  • 类是高配版的接口.

文档

类就是用来创造对象的东西。(同类事物的抽象描述)
有一些语言(如 Java,存疑)创建对象必须先声明一个类,而有的语言(JS)则不需要。

1
2
// java里是不允许没有类 直接创建对象的,而js可以
var jack = {name:'jack',age:18}

对于没有使用过 TS 的 JS 程序员来说,类看起来还挺无聊的

我需要什么属性随时加不就好了吗?

1
2
var jack = {name:'jack',age:18}
var aaa = {name:'aaa',age:18,gender:'man'}

对于使用过 TS 的 JS 程序员来说,类可以让你的系统更加「可预测」

这个对象不会出现一些我不知道的属性,一切都尽在我的掌握。

所以别废话,快学 TS 的类。

1.声明类

2.声明对象的非函数属性

3.声明对象的函数属性

4.使用 constructor

5.声明类的属性(static)

6.使用 this 代指当前对象(注意不要以为 this 永远都代指当前对象,JS 的 this 有更多功能,而且默认 this 为 window)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Human{
name:string; // 声明对象的非函数属性
age:number; // 声明对象的非函数属性
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
// 声明对象的函数属性
move(): void{
console.log('我动了')
}
}

let hjx = new Human('hjx',18);

console.log(JSON.stringify(hjx));

interface Human2{
name: string;
age: number;
move(): void;
}

let hjx2: Human2 = {
name: 'hjx2',
age: 18,
move(): void{
console.log('我动了2')
}
}
// 接口就要一个一个属性挨个写一遍
let hjx3: Human2 = {
name: 'hjx3',
age: 18,
move(): void{
console.log('我动了3')
}
}

类中两个方法之间可以调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Human{
name:string;
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
move(): void{
console.log('我动了')
}
say(): string{
this.move();
return 'hi'
}
}

let hjx = new Human('hjx',18);

请问对象有木有 constructor属性?

  • 有,这是js的特殊现象,只有js会这样
  • 在class里写的所有东西 对象都有
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Human{
name:string;
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
move(): void{
console.log('我动了')
}
say(): string{
this.move();
return 'hi'
}
}

let hjx = new Human('hjx',18);

console.log(hjx.name)
console.log(hjx.age)
console.log(hjx.move)
console.log(hjx.say)
console.log(hjx.constructor)

使用 constructor

1
let hjx = new Human('hjx',18);

static

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Human{
name:string;
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
move(): void{
console.log('我动了')
}
say(): string{
this.move();
return 'hi'
}
}

let hjx = new Human('hjx',18);

/*
name age move say constructor是 实例对象的属性,不是类的属性

如果你直接给 类加属性报错
Human.xxx = 1; // 报错
*/

// 正确姿势

class Human2{
static xxx = 1
name:string;
age:number;
constructor(name:string,age:number){
this.name = name;
this.age = age;
}
move(): void{
console.log('我动了')
}
say(): string{
this.move();
return 'hi'
}
}

console.log(Human2.xxx);

类继承类

  • 使用 super
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Animal{
constructor(){
console.log('动物出生了')
}
move():void{
console.log('move');
}
}

class Human extends Animal{
name: string;
age: number;
constructor(name:string, age:number){
super()
console.log('people 出生了')
this.name = name;
this.age = age;
}
say():string{
this.move();
return 'hi'
}
}

let hjx = new Human('hjx',18);

hjx.say();

/*
动物出生了
people 出生了
move
*/
  • 在子类constructor中必须调用 super

如果父类有属性呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Animal{
kind:string;
constructor(kind:string){
console.log('动物出生了')
this.kind = kind;
}
move():void{
console.log('move');
}
}

class Human extends Animal{
name: string;
age: number;
constructor(name:string, age:number){
super('哺乳动物');
console.log('people 出生了')
this.name = name;
this.age = age;
}
say():string{
this.move();
return 'hi'
}
}

let hjx = new Human('hjx',18);

hjx.say();

修饰符 public private 和 protected

  • private
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Human{
name: string;
age: number;
private secret:string;
constructor(name:string, age:number,secret:string){
this.name = name;
this.age = age;
this.secret = secret;
}
}

let hjx = new Human('hjx',18,'中了200w');


// 因为secret属性是 private 所有只有类内部可以访问 类外部不可以访问
console.log(hjx.secret);
  • public 默认
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Human{
name: string;
age: number;
constructor(name:string, age:number){
this.name = name;
this.age = age;
}
}


等价于

class Human{
public name: string;
public age: number;
constructor(name:string, age:number){
this.name = name;
this.age = age;
}
}
  • protected 当前类和子类中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Animal{
protected kind:string;
constructor(kind:string){
console.log('动物出生了')
this.kind = kind;
}
move():void{
console.log('move');
}
}

class Human extends Animal{
name: string;
age: number;
constructor(name:string, age:number){
super('哺乳动物');
console.log('people 出生了')
this.name = name;
this.age = age;
}
say():string{
this.move();
return 'hi'
}
}

let hjx = new Human('hjx',18);
// 访问不到 protected 修饰的属性
console.log(hjx.kind); // 报错

访问器

  • get
  • set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Human{
name: string;
private _age: number; //把你真正用到的值藏起来
get age(){
return this._age;
}
set age(value:number){
if(value < 0){
this._age = 0;
}else{
this._age = value;
}
}
constructor(name:string, age:number){
this.name = name;
this.age = age;
}
}

let hjx = new Human('hjx',18);


// 其他人恶意篡改 age 属性
// hjx.age = -1
// hjx.age = 10000

hjx.age = -1
console.log(hjx.age); // 0

抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
也可以叫做「爸爸类」:专门当别的类的爸爸的类。
也可以叫做「没有写完的类」:只描述有什么方法,并没有完全实现这些方法。

由于这个类没有写完,所以不能创建出对象。(会报错)

abstract class Animal {
//抽象方法加 abstract 修饰,不写实现
abstract makeSound(): void;
move(): void {
console.log('roaming the earch...');
}
}
abstract 就是抽象的意思。我建议你不用管抽象是什么意思,就死记硬背:抽象类就是爸爸类。

是不是很像接口的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
interface Animal{
move():void;
}

class Animal{
move():void{
console.log('move')
}
}

abstract class Animal {
abstract move(): void;
}
  • 抽象方法必须出现在抽象类中
  • 抽象类无法实例化(因为这个类没写完)
  • 为啥可以当父类呢?
    1
    2
    3
    因为你可能有梦想没有实现
    你就拜托你的儿子实现,此时你的儿子也是抽象类
     你儿子还没有实现,他就继续拜托他的儿子实现
1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class Animal {
abstract move():void;
}

class Human extends Animal{
move():void{
console.log('move');
}
}

let hjx = new Human();

hjx.move()

高级技巧

构造函数(以后回头看)

参考链接 https://zhuanlan.zhihu.com/p/23987456

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
由于 TS 的 class 其实就是 JS 里的 class,JS 里的 class 其实就是一个构造函数。
换句话说,类就是一个函数……
同时,函数在JS里,是一种对象。
所以类其实是一种对象。
我知道这听起来很奇怪,但是 JS 就是这么奇怪。

class Greeter {
static standardGreeting = "Hello, there";
greeting: string;
greet() {
if (this.greeting) {
return "Hello, " + this.greeting;
}
else {
return Greeter.standardGreeting;
}
}
}

let greeter1: Greeter;
greeter1 = new Greeter();
console.log(greeter1.greet());

let greeterMaker: typeof Greeter = Greeter; // 注意这句话
greeterMaker.standardGreeting = "Hey there!";

let greeter2: Greeter = new greeterMaker();
console.log(greeter2.greet());

把类当做接口使用

1
2
3
4
5
6
7
8
9
10
11
12
13
接口是低配版的类。
类是高配版的接口。

class Point {
x: number;
y: number;
}

interface Point3d extends Point {
z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

TS入门004接口02

接口

就是用代码描述一个对象必须有什么属性(包括方法),但是有没有其他属性就不管了

理解 函数和方法的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
当一个函数是一个对象的属性的时候,我们把这个函数称为该对象的方法


------------
// 这是一个函数
function fn(){
console.log(1)
}

let obj = {
fn:function(){
console.log(2)
}
}

// obj.fn 就是一个方法

------------------------
如果这样呢?
function fn(){
console.log(1)
}
let obj = {
fn:fn
}

// fn实质就是 一个形如 addr = 2311111 的地址的内存
// 如果是普通编程角度 fn 是函数
// 如果是面向对象角度 fn 是方法

定义一个接口 人

1
2
3
4
5
6
7
interface Human{
name:string,
age:number
}

let p1:Human = {name:'hjx',age:18};
// 不加 name 报错,不加age也报错

需求添加:人有身体

1
2
3
4
5
6
7
8
9
10
11
12
interface Shape{
head:string,
body:string
}

interface Human{
name:string,
age:number,
shape:Shape
}

let p1:Human = {name:'hjx',age:18,shape:{head:'头',body:'方的'}}

需求添加:人不仅有身体,还要走路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface Shape{
head:string,
body:string
}

interface Human{
name:string,
age:number,
shape:Shape,
say(word: string):void;
}

let p1:Human = {
name:'hjx',
age:18,
shape:{head:'头',body:'方的'},
say(word: string){
console.log(word)
}
}

p1.say('I\'m hjx')

只读属性。需求添加:人不能改名字

1
2
3
4
5
6
7
8
9
10
interface Human{
readonly name:string,
age:number
}

let p1:Human = {name:'hjx',age:18};

p1.name = 'aaa'
// 报错
// Cannot assign to 'name' because it is a constant or a read-only property.

可选属性

  • 有些人喜欢玩游戏,有些人什么都不玩
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
interface Shape{
head:string,
body:string
}

interface Human{
readonly name :string,
age:number,
shape:Shape,
likedGame?:Array<string>;
say(word: string):void;
}

let p1:Human = {
name:'hjx',
age:18,
shape:{head:'头',body:'方的'},
likedGame:['王者荣耀','lol'],
say(word: string){
console.log(`${this.name}-${word}`);
}
}

let p2:Human = {
name:'hjx2',
age:20,
shape:{head:'头',body:'方的'},
say(word: string){
console.log(`${this.name}-${word}`);
}
}

接口应用在函数里

1
2
3
4
5
6
7
8
9
10
11
12
interface RectConfig{
height?: number,
width?: number
}

function createRect(config:RectConfig):void{

}

// 由于可选 所以可以传递一个属性
let mySquare = createRect({width:100})
let mySquare2 = createRect({height:100})
  • 如果在参数里传递多余的是不允许的
1
let mySquare3 = createRect({width:100,height:100,color:'red'})
  • 如果声明在变量里就允许
1
2
3
4
5
6
7
8
9
let config = {
height:100,
width:100,
color:'red'
}
let mySquare4 = createRect(config);


let config2 = {} //这样也可以 ,因为接口定义的参数都是可选的

都可选,同时受你控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface RectConfig{
height?: number;
width?: number;
[propName: string]: number;
}

let config = {
height:100,
width:100,
}

/*
[propName: string] 代表 key 是一个string
[propName: string]: number; 代表 key 对应的 value值必须是 number
*/

给函数定义接口

1
2
3
4
5
6
7
interface 二则运算{
(a: number,b: number): number;
}

let add:二则运算 = function(c: number, d: number): number{
return c + d;
}

- 接口里定义一个函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface 二则运算{
(a: number,b: number): number;
逆运算(a: number,b: number): number;
}

let add:二则运算=(
(): 二则运算 =>{
let x: any = function(a: number,b: number){
return a + b;
}
x.逆运算 = function(a: number,b: number): number{
return a - b;
}
return x;
}
)();

console.log(add(1,2)); // 3

接口的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface Animal{
move(): void;
}

interface Human extends Animal{
name: string;
age: number;
}

let p: Human = {
name:'hjx',
age:18,
move(){
console.log('我在动')
}
}

p.move();
  • 多重继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
interface 有机物{
c:boolean;
h:boolean;
o:boolean,
}

interface Animal{
move(): void;
}


interface Human extends Animal,有机物{
name: string;
age: number;
}

let p: Human = {
c:true,
h:true,
o:true,
name:'hjx',
age:18,
move(){
console.log('我在动')
}
}

p.move();
  • 串行的继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
interface 有机物{
c:boolean;
h:boolean;
o:boolean,
}

interface Animal extends 有机物{
move(): void;
}


interface Human extends Animal{
name: string;
age: number;
}

let p: Human = {
c:true,
h:true,
o:true,
name:'hjx',
age:18,
move(){
console.log('我在动')
}
}

p.move();

一个接口继承了两个接口,顺序重要吗?

  • 不重要
  • 但是如果那两个接口拥有一个共同的属性就麻烦了,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface 有机物{
c:boolean;
h:boolean;
o:boolean;
xxx:string;
}

interface Animal{
move(): void;
xxx:number
}

// 继承的两个接口存在相同属性,没有覆盖关系,直接报错
interface Human extends Animal,有机物{
name: string;
age: number;
}

串行也报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface 有机物{
c:boolean;
h:boolean;
o:boolean;
xxx:string;
}

interface Animal extends 有机物{
move(): void;
xxx:number;
}

interface Human extends Animal{
name: string;
age: number;
}

除非类型一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface 有机物{
c:boolean;
h:boolean;
o:boolean;
xxx:string;
}

interface Animal extends 有机物{
move(): void;
xxx:string;
}

interface Human extends Animal{
name: string;
age: number;
}

接口的顺序写在后面可以吗?

  • 最好不要瞎写
  • ts最烦的就是 你先用,后面在声明
  • 可不可以把 interface 写后面——你有病吗?
  • 可以写在后面,但是建议写前面

TS入门004接口01

初始化运行环境

  • 进入目录建立配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 使用 vscode,不然没法继续了
# 新建目录 ts004
mkdir ts004

# 进入目录
cd ts004

# 建立 .vscode 目录
# 在 .vscode 目录里新建 lanuch.json
# program 位置 填写你本地 ts-node 命令的路径
# which ts-node

{
"configurations": [
{
"name": "ts-node",
"type": "node",
"request": "launch",
"program": "/usr/local/bin/ts-node",
"args": ["${relativeFile}"],
"cwd": "${workspaceRoot}",
"protocol": "inspector"
}
]
}

# ts004/001.ts
let a:string = 'hi';
console.log(a);

# 选中 001.ts 点击 侧边栏的小虫子
打印 hi 代表你的运行环境就好了

TS 数据类型

JS七种数据类型 + 枚举 + Any + void + never

那七种呢?

1
2
3
4
5
6
7
8
let aa: null = null;
let bb: undefined = undefined;

let cc: boolean = false;
let dd: string = 'hello';
let ee: number = 1.23;
let ff: Object = {name:'hjx'};
let ss = Symbol('key'); // 不支持打印,很少用这里不进行解决。

any 类型

让喜欢动态类型的人有一个退路

1
2
3
4
5
6
7
8
9
// ts里有个规定——你声明了 类型就不能更改,但是在js里是可以的
/*
let n:number = 1;
n = 'hello'
*/

// 如何在 ts 里给一个可以改变类型的变量怎么办?
let n2: any = 1;
n2 = 'hello'

枚举

1
2
3
4
5
6
7
8
// 以前想给员工信息进行统计,用这样一个字段, 男的就是 man 女的就是 woman
let gender = 'man'

gender = 'woman'

// 有时你写错了个字母也发现不了
gender = 'men'
gender = 'women'
  • 用字符串来代表状态是存在——手误导致状态变多了
  • 人总是会出错的
  • 答案就是用枚举
1
2
3
4
5
6
7
8
9
10
11
12
13
enum Gender{
Man,
Woman
}

//此时在声明类型
let gender:Gender = Gender.Man
console.log(gender) // 0
// 万一要修改就
gender = Gender.Woman
console.log(gender) // 1

// 根据打印结果来看 enum 实际是根据你传递的枚举顺序返回索引

如果我想指定别的呢?

  • 枚举的值是对应一个东西,如果你不给就按照索引顺序
1
2
3
4
5
6
7
8
9
10
11
enum Gender2{
Man = 3,
Woman = 'hello'
}

//此时在声明类型
let gender2:Gender2 = Gender2.Man
console.log(gender2) // 3
// 万一要修改就
gender2 = Gender2.Woman
console.log(gender2) // hello

void

1
2
3
4
5
6
7
8
9
10
// void 明确的告诉别人 没有返回值
function print(x: any):void{
console.log(x);
}

// let a:number = print(1) //报错 因为已经说了没有返回值

// 如果a 不声明类型呢?
let a = print(1);
a === undefined // a 就是 undefined

undefined

  • 你可以给任何类型的值赋值为 undefined
  • 默认情况下,undefined 是所有类型的子类型,意思就是你可以把 null / undefined 赋值给所有类型
1
2
3
let a1:number = undefined
let a2:string = undefined
let a3:boolean = undefined

类型转换

1
2
3
4
5
6
7
8
9
10
11
12
13
// 类型转换
let n: number = 123;
// n.split('') // 报错
// 你只能转换之后在用
let str = n.toString()
console.log(str.split(''));



// ts 如何进行转化呢?
let m: number = 123;
// 还是报错
// console.log(<string>m.split(''))
  • 因为类型转换必须从一个宽泛的类型才可以转换
  • 也就是说 声明的时候应该是 any
1
2
3
let n1: any = 123
console.log(n1.split('')) // 这样写是有安全隐患的
// 会在运行的时候报错
  • 你必须确保它是 string 才可以
1
2
let n2: any = '123'
console.log(n2.split(''))

实际用法是这样

1
2
3
4
5
// 明确告诉程序它是什么类型, 意思就是 你不用担心 n3是什么,我能确保它是 string ——只是主观判断 
// 如果你实际这样 let n3: any = 123 声明 还是会运行报错,那会是谁的责任,写代码的人
// 这样的意义有什么用,以后会有用——这个概念叫做 断言
let n3: any = '123'
console.log(<string>n3.split(''))

断言两种方式

1
2
(<string>someValue).length;
(someValue as string).length;

类型转换的反例

1
2
3
4
5
6
7
8
let a = '123'
a = a - 0
// 在 js 里 可以 把 a 变成 number
// 在 ts 里 是绝对禁止 这样的类型转换
// 但是 js 缺喜欢 偷偷的 类型转换

let b = 123
b = b + '' // '123'

正确做法

1
2
3
4
5
6
7
8
9
10
11
12
13
// 正确姿势
let a:number = 123;
let b:string = a.toString();


let c:string = '123'
let d:number = parseFloat(c);

let s1:number = 1;
// 非常不推荐这样写
let b1:boolean = !!s1;
//应该这样
let b2:boolean = Boolean(s1);

为什么用 Boolean 来转换呢?

1
2
3
4
5
6
7
8
9
10
11
// node 里 0是false 非0是true
!!0 // false
!!1 // true
!!2 // true
!!-1 // true

// ruby 里 数字都是 true
!!0 // true
!!1 // true
!!2 // true
!!-1 // true

字符串和对象的转换

1
2
3
4
5
6
7
8
9
10
11
12
let o1 = {name:'hjx',age:18}
let str1 = JSON.stringify(o1);
console.log(o1);
console.log(str1);

let str2 = `{"name": "aaa", "age": 18}`;
let o2 = JSON.parse(str2);


// 结果如下
Object {name: "hjx", age: 18}
{"name":"hjx","age":18}

变量声明

1
尽量不用 var,使用 let 和 const
  • 变量用 let
  • 常量用 const
1
2
3
4
5
6
7
for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i);
},100*i)
}

// 打印十次 10

类型转换的一个问题

1
2
3
4
5
6
7
8
9
10
11
12
let a1 = [1,2,3]
let a2 = [4,5,6]
let a3 = a1 + a2 // [1,2,34,5,6]

// 正确姿势
let a4 = [...a1,...a2]


let o1 = {name:'hjx'}
let o2 = {age:18}

let o3 = {...o1,...o2}

TS入门003之ts操作dom

做一个计算器功能

  • 安装 本地server
  • 新建目录 ts003
1
2
3
4
npm i http-server -g

# 开启服务在目录 ts003
http-server -c-1
  • 初始化代码

ts003/index.html

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<script src="./caculator.js"></script>
</body>
</html>

ts003/caculator.ts

1
2
3
4
{
let a:number = 1;
console.log(a);
}
  • 本地实时编译ts
1
2
# 注意是当前目录  ts003
tsc -w caculator.ts

这样你把 http-server 开启的端口,在浏览器打开,就能看到打印1

你期望这样

1
2
3
4
5
html里直接运行 ts   但是这样是走不通的

所以

html里只能运行js, 把ts编译为js

开始做一个计算器

1.用ts生成 button 添加到页面

caculator.ts

1
2
3
4
5
6
7
8
{   
// 这是js
// let button = document.createElement('button');
// 使用 ts
let button:HTMLButtonElement = document.createElement('button');
button.textContent = '0';
document.body.appendChild(button);
}

2.函数提取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{   
function createButton(text:string){
let button:HTMLButtonElement = document.createElement('button');
button.textContent = text;
document.body.appendChild(button);
}

createButton('0')
createButton('1')
createButton('2')
createButton('3')
createButton('4')
createButton('5')
createButton('6')
createButton('7')
createButton('8')
createButton('9')

// '0' 表示 字符串
// 0 表示 数字
// 如果你瞎搞 ts 就弄死你
}

3.继续优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 造出所有按钮
{
function createButton(text:string){
let button:HTMLButtonElement = document.createElement('button');
button.textContent = text;
document.body.appendChild(button);
}

`clear,/,
7,8,9,*,
4,5,6,-,
1,2,3,+,
0,.,=`.split(',').forEach(string=>{
createButton(string);
})
}

4.我们希望按钮换行 就跟计算器那样排列

  • 二维数组
  • TS如何定义二维数组呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//二位数组 ——  Array<Array<string>>

{
function createButton(text:string,container:HTMLElement){
let button:HTMLButtonElement = document.createElement('button');
button.textContent = text;
container.appendChild(button);
}

let keys:Array<Array<string>> = [
["clear", "/"],
["7", "8", "9", "*"],
["4", "5", "6", "-"],
["1", "2", "3", "+"],
[ "0", ".", "="]
];

keys.forEach( ( textList:Array<string> )=>{
let div:HTMLDivElement = document.createElement('div');
textList.forEach( (text:string)=>{
createButton(text,div);
})
document.body.appendChild(div);
});
}

5.优化一下样式

  • 改变一下层级结构
  • 添加一些类名

caculator.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 添加样式
{
function createButton(text:string,container:HTMLElement,className:string){
let button:HTMLButtonElement = document.createElement('button');
button.textContent = text;
if(className){
button.className = className;
}
container.appendChild(button);
}

// 让计算器有个容器而不是直接塞到 body里
let container:HTMLDivElement = document.createElement('div');
container.classList.add('caculator');
document.body.appendChild(container);
// 显示结果的地方
let output:HTMLDivElement = document.createElement('div');
output.classList.add('output');
let span:HTMLSpanElement = document.createElement('span');
span.textContent = '0';
output.appendChild(span);
container.appendChild(output);

let keys:Array<Array<string>> = [
["clear", "/"],
["7", "8", "9", "*"],
["4", "5", "6", "-"],
["1", "2", "3", "+"],
[ "0", ".", "="]
];

keys.forEach( ( textList:Array<string> )=>{
let div:HTMLDivElement = document.createElement('div');
div.classList.add('row');
textList.forEach( (text:string)=>{
// 添加样式
createButton(text,div,`button text-${text}`);
})
container.appendChild(div);
});
}

caculator.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.caculator{
/* 让元素内缩 不然output就占满了 */
display: inline-block;
}
.caculator .button{
height: 6em;
width:6em;
}
.caculator .button.text-clear{
width:18em;
}

.caculator .button.text-0{
width:12em;
}

.caculator .output{
border:1px solid grey;
height: 6em;
position: relative;
}

.caculator .output span{
position: absolute;
right:.4em;
bottom:.4em;
font-size: 40px;
}

6.事件监听

  • 放弃监听每一个button
  • 用事件委托,监听容器
1
2
3
container.addEventListener('click',function(event){
console.log(event.target);
});

7.处理计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
{   
// 声明创建函数
function createButton(text:string,container:HTMLElement,className:string){
let button:HTMLButtonElement = document.createElement('button');
button.textContent = text;
if(className){
button.className = className;
}
container.appendChild(button);
return button;
}

// 让计算器有个容器而不是直接塞到 body里
let container:HTMLDivElement = document.createElement('div');
container.classList.add('caculator');
document.body.appendChild(container);

// 显示结果的地方
let output:HTMLDivElement = document.createElement('div');
output.classList.add('output');

// 创建 output 里的 span
let span:HTMLSpanElement = document.createElement('span');
span.textContent = '0';
output.appendChild(span);
container.appendChild(output);

// 三个变量 n1 n2 数字 operator 操作符
let n1:number
let n2:number
let operator:string

container.addEventListener('click',function(event){
if( event.target instanceof HTMLButtonElement){
let button:HTMLButtonElement = event.target;
let text:string = button.textContent
//看 text 是不是0-9的数字
if('0123456789'.indexOf(text)>=0){
console.log('数字')
// 如果已经有操作符就操作n2
if(operator){
if(n2){
n2 = parseInt(n2.toString() + text);
}else{
n2 = parseInt(text);
}
span.textContent = n2.toString();
}else{
if(n1){
n1 = parseInt(n1.toString() + text);
}else{
n1 = parseInt(text);
}
span.textContent = n1.toString();
}

}else if('+*-/'.indexOf(text)>=0){
console.log('操作符')
operator = text;
}else if('='.indexOf(text)>=0){
let result:number;
if(operator === '+'){
result = n1 + n2
}else if(operator === '-'){
result = n1 - n2
}else if(operator === '*'){
result = n1 * n2
}else if(operator === '/'){
result = n1 / n2
}

span.textContent = result.toString();
}else{
console.log('不知道')
}
console.log(n1,operator,n2)
}
});

let keys:Array<Array<string>> = [
["clear", "/"],
["7", "8", "9", "*"],
["4", "5", "6", "-"],
["1", "2", "3", "+"],
[ "0", ".", "="]
];

keys.forEach( ( textList:Array<string> )=>{
let div:HTMLDivElement = document.createElement('div');
div.classList.add('row');
textList.forEach( (text:string)=>{
// 添加样式
createButton(text,div,`button text-${text}`);
})
container.appendChild(div);
});
}

8.class使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// class 使用
{
class Caculator{
public container:HTMLDivElement;
private output:HTMLDivElement;
private span:HTMLSpanElement;
public n1:number;
public n2:number;
public operator:string;
public keys:Array<Array<string>> = [
["clear", "/"],
["7", "8", "9", "*"],
["4", "5", "6", "-"],
["1", "2", "3", "+"],
[ "0", ".", "="]
];

constructor(){
this.createContainer();
this.createOutput();
this.createButtons();
this.bindEvents();
}
createButton(text:string,container:HTMLElement,className:string){
let button:HTMLButtonElement = document.createElement('button');
button.textContent = text;
if(className){
button.className = className;
}
container.appendChild(button);
return button;
}
createContainer(){
let container:HTMLDivElement = document.createElement('div');
container.classList.add('caculator');
document.body.appendChild(container);
this.container = container;
}
createOutput(){
let output:HTMLDivElement = document.createElement('div');
output.classList.add('output');

let span:HTMLSpanElement = document.createElement('span');
span.textContent = '0';
output.appendChild(span);

this.output = output;
this.span = span;
this.container.appendChild(output);
}
createButtons(){
this.keys.forEach( ( textList:Array<string> )=>{
let div:HTMLDivElement = document.createElement('div');
div.classList.add('row');
textList.forEach( (text:string)=>{
// 添加样式
this.createButton(text,div,`button text-${text}`);
})
this.container.appendChild(div);
});
}
bindEvents(){
this.container.addEventListener('click',(event)=>{
if( event.target instanceof HTMLButtonElement){
let button:HTMLButtonElement = event.target;
let text:string = button.textContent
//看 text 是不是0-9的数字
if('0123456789'.indexOf(text)>=0){
console.log('数字')
// 如果已经有操作符就操作n2
if(this.operator){
if(this.n2){
this.n2 = parseInt(this.n2.toString() + text);
}else{
this.n2 = parseInt(text);
}
this.span.textContent = this.n2.toString();
}else{
if(this.n1){
this.n1 = parseInt(this.n1.toString() + text);
}else{
this.n1 = parseInt(text);
}
this.span.textContent = this.n1.toString();
}

}else if('+-*/'.indexOf(text)>=0){
console.log('操作符')
this.operator = text;
}else if('='.indexOf(text)>=0){
let result:number;
if(this.operator === '+'){
result = this.n1 + this.n2;
}else if(this.operator === '-'){
result = this.n1 - this.n2;
}else if(this.operator === '*'){
result = this.n1 * this.n2;
}else if(this.operator === '/'){
result = this.n1 / this.n2;
}

this.span.textContent = result.toString();
}else{
console.log('不知道')
}
console.log(this.n1,this.operator,this.n2);
}
});
}
}

new Caculator();
}

优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
{   
class Caculator{
public container:HTMLDivElement;
private output:HTMLDivElement;
private span:HTMLSpanElement;
public n1:string = null; // 用 number 导致 1.0 会变成 1
public n2:string = null;
public operator:string = null;
public result:string = null;
public keys:Array<Array<string>> = [
["clear", "/"],
["7", "8", "9", "*"],
["4", "5", "6", "-"],
["1", "2", "3", "+"],
[ "0", ".", "="]
];

constructor(){
this.createContainer();
this.createOutput();
this.createButtons();
this.bindEvents();
}
createButton(text:string,container:HTMLElement,className:string){
let button:HTMLButtonElement = document.createElement('button');
button.textContent = text;
if(className){
button.className = className;
}
container.appendChild(button);
return button;
}
createContainer(){
let container:HTMLDivElement = document.createElement('div');
container.classList.add('caculator');
document.body.appendChild(container);
this.container = container;
}
createOutput(){
let output:HTMLDivElement = document.createElement('div');
output.classList.add('output');

let span:HTMLSpanElement = document.createElement('span');
span.textContent = '0';
output.appendChild(span);

this.output = output;
this.span = span;
this.container.appendChild(output);
}
createButtons(){
this.keys.forEach( ( textList:Array<string> )=>{
let div:HTMLDivElement = document.createElement('div');
div.classList.add('row');
textList.forEach( (text:string)=>{
// 添加样式
this.createButton(text,div,`button text-${text}`);
})
this.container.appendChild(div);
});
}
bindEvents(){
this.container.addEventListener('click',(event)=>{
if( event.target instanceof HTMLButtonElement){
let button:HTMLButtonElement = event.target;
let text:string = button.textContent
this.updateNumberOrOperator(text);

}
});
}
updateNumber(name:string,text:string):void{
if(this[name]){
this[name] += text;
}else{
this[name] = text;
}
this.span.textContent = this[name].toString();
}
updateNumbers(text:string):void{
if(this.operator){
this.updateNumber('n2',text);
}else{
this.updateNumber('n1',text);
}
}
updateResult():void{
let result;
let n1:number = parseFloat(this.n1);
let n2:number = parseFloat(this.n2);

if(this.operator === '+'){
result = n1 + n2;
}else if(this.operator === '-'){
result = n1 - n2;
}else if(this.operator === '*'){
result = n1 * n2;
}else if(this.operator === '/'){
result = n1 / n2;
}
this.span.textContent = result.toString();
// 结果出来后重置 n1 n2
this.n1 = null;
this.n2 = null;
this.operator = null;
this.result = result;
}
updateOperator(text:string):void{
// 点符号时候 没点过数字
if(this.n1 === null){
this.n1 = this.result;
}
this.operator = text;
}
updateNumberOrOperator(text:string){
if('0123456789.'.indexOf(text)>=0){
this.updateNumbers(text);
}else if('+*-/'.indexOf(text)>=0){
this.updateOperator(text);
}else if('='.indexOf(text)>=0){
this.updateResult();
}else if(text==='clear'){
this.n1 = null;
this.n2 = null;
this.operator = null;
this.result = null;
this.span.textContent = '0';
}else{
console.log('不知道')
}
}
}

new Caculator();
}

TS入门002用TS开发命令行程序

自学知识的误区

  • 不要怕(不要因为没学过而不学了,没学才应该学啊)
  • 不要妄图系统学习,了解常用功能
  • 有目的的学,不要没目的的看书 看10本书

带着目的学习

  • 写一个命令行工具
  • 再写一个
  • 回到第一步,直到你觉得你学的不错了

不要去看书,不要去看文档,遇到问题再去看文档,再去google

通过解决一个个的小问题来学习 TS

最简单的命令行程序

1.新建 001.ts

1
2
#!/usr/bin/env ts-node
console.log('hello world');

第一行不要问为什么之间照抄!——其实就是 shebang 跟python 文件首行一样 代表指定那个命令来执行此脚本

2.添加执行权限并执行

1
2
3
4
5
6
7
8
# 添加权限
chmod +x 001.ts

# 执行
./001.ts

# 打印
hello world

脚本升级 可以在后面跟参数

1
2
3
4
5
6
7
vue --version

curl -L https://www.baidu.com

git add xx

git commit -m 'sss'

process.argv参数

这个变量可以获取到用户输入的这一行内容

新建 002.ts

1
2
3
#!/usr/bin/env ts-node
console.log(process.argv);
console.log('hello world');
1
2
3
4
chmod +x 001.ts
./002.ts

# 报错了

新人在学习最怕什么,最怕的不是知识点复杂,而是你告诉我这样可以运行,但是我运行之后呢?——运行不了,这就进行不下去了

我们先看报错信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/usr/local/lib/node_modules/ts-node/src/index.ts:261
return new TSError(diagnosticText, diagnosticCodes)
^
TSError: ⨯ Unable to compile TypeScript:
002.ts(2,13): error TS2304: Cannot find name 'process'.

at createTSError (/usr/local/lib/node_modules/ts-node/src/index.ts:261:12)
at getOutput (/usr/local/lib/node_modules/ts-node/src/index.ts:367:40)
at Object.compile (/usr/local/lib/node_modules/ts-node/src/index.ts:557:11)
at Module.m._compile (/usr/local/lib/node_modules/ts-node/src/index.ts:439:43)
at Module._extensions..js (internal/modules/cjs/loader.js:700:10)
at Object.require.extensions.(anonymous function) [as .ts] (/usr/local/lib/node_modules/ts-node/src/index.ts:442:12)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)

如果你看不懂

  • 一个一个字的查字典
1
2
3
4
5
6
# ts 没有能力 编译
TSError: ⨯ Unable to compile TypeScript:
# 再看 第二行 第13个字有问题 报错编号是 TS2304 意思是找不到 process
002.ts(2,13): error TS2304: Cannot find name 'process'.

# 后面是细节

怎么办?

怎么办?

怎么办?

程序员最6的查 bug 方式就是复制这行错误到 google

1
2
3
4
5
6
7
error TS2304: Cannot find name 'process'.

实际上这是 Node.js 的全局变量,不可能找不到。

这就是 TS 的厉害之处:如果你不告诉我 process 是什么,我就不允许你用 process。

那么如何告诉 TS process 是什么呢?

得到内容挨个点进去看

过程:

  1. 复制 error TS2304: Cannot find name ‘process’. 到 Google
  2. 找到 Stackoverflow 上的一篇问答
  3. 得知要安装 Typings,于是点开他给的链接
  4. 看到页面上方显示 Typings is deprecated in favor of NPM @types
  5. 得知 Typings 已经被弃用了,但是这货不告诉我新版在哪
  6. 猜测新版是 @types/node (纯经验)
  7. 运行 npm install @types/node 然后运行 ./2.ts 发现成功
  8. 去 Stackoverflow 回复一下,以防别人也遇到不知道新版名称
  9. 回复的时候发现已经有人回复了 with typescript 2, its now npm i -D @types/node
  10. 发现他用了 -D 选项但是我没有使用,根据我对 npm 的了解,加不加 -D 都行,加上更好一点。
1
2
# 复制到刚才的目录里执行
npm i -D @types/node

结果又警告

1
2
3
4
5
6
7
8
9
10
11
npm WARN saveError ENOENT: no such file or directory, open '/Users/huangjiaxi/Desktop/typescript-demo/ts002/package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open '/Users/huangjiaxi/Desktop/typescript-demo/ts002/package.json'
npm WARN ts002 No description
npm WARN ts002 No repository field.
npm WARN ts002 No README data
npm WARN ts002 No license field.

+ @types/node@10.12.19
added 1 package from 33 contributors and audited 1 package in 2.191s
found 0 vulnerabilities

意思是没有 package.json,我这目录确实没有,那就创建一个

1
2
3
4
5
6
7
8
9
10
11
12
13
# 最快创建 package.json 的方法
npm init -y

# 重新运行
npm i -D @types/node

# 此时就单独 警告 没有描述 没有远程仓库
npm WARN ts002@1.0.0 No description
npm WARN ts002@1.0.0 No repository field.

+ @types/node@10.12.19
updated 1 package and audited 1 package in 1.66s
found 0 vulnerabilities

再次执行 002.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
./002.ts


# 打印如下信息
[ 'node',
'/Users/huangjiaxi/Desktop/typescript-demo/ts002/002.ts' ]
hello world

/*
[ 'node','/Users/huangjiaxi/Desktop/typescript-demo/ts002/002.ts' ]
这个 node 就是 002.ts 的第一行 告诉这个程序用什么来运行
第二个就是 当前文件的绝对路径
*/

添加自己的参数

1
2
3
4
5
6
7
8
9
10
./002.ts  xxx yyy zzz


# 打印
[ 'node',
'/Users/huangjiaxi/Desktop/typescript-demo/ts002/002.ts',
'xxx',
'yyy',
'zzz' ]
hello world

两数之和的程序

新建 003add.ts

1
2
3
4
5
6
#!/usr/bin/env ts-node
console.log( process.argv );
let a = process.argv[2];
let b = process.argv[3];

console.log( a + b );

执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
chomd +x 003add.ts
./003add.ts 1 2

# 得到结果
[ 'node',
'/Users/huangjiaxi/Desktop/typescript-demo/ts002/003add.ts',
'1',
'2' ]
12

# 为啥是 12
你可以打印 process.argv 来看内容 数组里的内容是 字符串啊

# 所以我们要解决这个类型问题

004add.ts

1
2
3
4
5
#!/usr/bin/env ts-node
let a:number = parseInt( process.argv[2] );
let b:number = parseInt( process.argv[3] );

console.log( a + b );

执行

1
2
3
4
5
6
7
8
9
chmod +x 004add.ts
./004add.ts 1 2

# 打印
3

# 但是如果这样执行呢?
./004add.ts 1 s
竟然得到了 NaN

然后聪明的你肯定用 Number.isNaN() 判断

  • 005add.ts
1
2
3
4
5
6
7
8
9
#!/usr/bin/env ts-node
let a:number = parseInt( process.argv[2] );
let b:number = parseInt( process.argv[3] );

if(Number.isNaN(a)||Number.isNaN(b)){
console.log('出错啦!')
}

console.log( a + b );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
chmod +x 005add.ts
./005add.ts 1 s

# 又报错了

return new TSError(diagnosticText, diagnosticCodes)
^
TSError: ⨯ Unable to compile TypeScript:
005add.ts(5,11): error TS2339: Property 'isNaN' does not exist on type 'NumberConstructor'.
005add.ts(5,28): error TS2339: Property 'isNaN' does not exist on type 'NumberConstructor'.

at createTSError (/usr/local/lib/node_modules/ts-node/src/index.ts:261:12)
at getOutput (/usr/local/lib/node_modules/ts-node/src/index.ts:367:40)
at Object.compile (/usr/local/lib/node_modules/ts-node/src/index.ts:557:11)
at Module.m._compile (/usr/local/lib/node_modules/ts-node/src/index.ts:439:43)
at Module._extensions..js (internal/modules/cjs/loader.js:700:10)
at Object.require.extensions.(anonymous function) [as .ts] (/usr/local/lib/node_modules/ts-node/src/index.ts:442:12)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)

-----------------------------------------------
# 复制这行 error 到 google

error TS2339: Property 'isNaN' does not exist on type 'NumberConstructor'.

在给出的链接里 看信息
# 我搜到一个信息是
These are part of the es2015 library. make sure you have --lib es2015

意思是 确保你又 es2015

最终我找到的结果就是 添加一个 tsconfig.json 文件 在里面进行 es语法的配置

里面内容如何填写呢?

直接google tsconfig.json

得到如下链接
https://www.typescriptlang.org/docs/handbook/tsconfig-json.html

里面有一个 tsconfig.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true
},
"files": [
"core.ts",
"sys.ts",
"types.ts",
"scanner.ts",
"parser.ts",
"utilities.ts",
"binder.ts",
"checker.ts",
"emitter.ts",
"program.ts",
"commandLineParser.ts",
"tsc.ts",
"diagnosticInformationMap.generated.ts"
]
}

添加一个 lib 参数

1
2
3
4
5
6
7
{
"compilerOptions": {
"lib":[
"es2015"
]
},
}

再次运行刚才的出错文件

1
2
3
4
5
./005add.ts 1 s

# 打印
出错啦
NaN

为防止以后出错,多写几个 es 版本

1
2
3
4
5
{
"compilerOptions": {
"lib":["es2015","dom","es2016","es2018"]
},
}

即使这样你发现 Number.isNaN 下面还是用红线,那就 重新进入 vscode

再看我们代码存在的问题

1
2
3
4
5
6
# 打印
出错啦
NaN

# 已经出错啦,就没必要往后执行了,直接退出程序
但是你不能用 return (return用在函数内部才行)
  • 006add.ts
1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/env ts-node
let a: number = parseInt( process.argv[2] );
let b: number = parseInt( process.argv[3] );

if(Number.isNaN(a)||Number.isNaN(b)){
console.log('出错啦!');
process.exit(1);
}

console.log( a + b );
process.exit(0);
  • 为啥退出要传递 数字呢

进程退出的潜规则——程序员的约定

  • 返回 0 代表正常退出
  • 返回 非 0 代表错误退出

幸福的家庭都是相似的,不幸的家庭各有各的不幸

1
2
3
4
5
process.exit(1);
process.exit(2);
process.exit(3);

process.exit(0);

自行完成

  • 加减乘除代码逻辑

族谱

100tree.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env ts-node
function createTabs(n: number): string {
return '----'.repeat(n);
}
class Person {
public children: Person[] = [];
constructor(public name) {}
addChild(child: Person): void {
this.children.push(child);
}
introduceFamily(n?: number): void {
n = n || 0;
console.log(`${createTabs(n)}${this.name}`);
this.children.forEach(person => {
person.introduceFamily(n + 1);
});
}
}

const grandPa = new Person('王麻子');
const person1 = new Person('王大锤');
const person2 = new Person('王者');
const child11 = new Person('王毛');
const child12 = new Person('王水');
const child21 = new Person('王荣耀');
const child22 = new Person('王农药');

grandPa.addChild(person1);
grandPa.addChild(person2);

person1.addChild(child11);
person1.addChild(child12);
person2.addChild(child21);
person2.addChild(child22);

grandPa.introduceFamily();

王麻子
----王大锤
--------王毛
--------王水
----王者
--------王荣耀
--------王农药

坑点

  • Person[] 规定数组类型要 进行初始化
1
2
3
public children: Person[];

# 如果这样声明 只是声明了 Person 的数组,但是没有实例化,也就无法调用 push()
  • 可选参数 introduceFamily(n?: number): void {…}
  • 无返回值 void
1
2
// n 可以不传递
introduceFamily(n?: number): void {...}

vultr搭建ss

一、挑选vps服务商

  • vultr:支持支付宝微信付款、操作简单
  • bandwagon:俗称”搬瓦工”,支持支付宝付款,价格便宜,套餐种类多,性价比高。
    套餐种类:KVM架构方案包括 CN2 GT线路、CN2 GIA 线路、香港机房等。
    地址:搬瓦工中文网

二、注册vultr账号

1.创建vultr账户

注册地址:Vultr The Infrastructure Cloud™

Vultr优惠注册链接:https://www.vultrblog.com/

进入首页后填写邮箱地址,密码(需要数字大小写字母且十位以上),然后点击create account按钮

然后验证你的邮箱地址,如果没收到验证邮件的话去垃圾箱翻找一下。

2.充值

进入我的账户,选择billing,然后选择alipay或者wechat,选择金额并发起付款,最少支付10$

3.搭建服务

1.首先选择机房地址,我选择的是东京,这个以后还可以更改

2.选择系统,推荐选用CentOS

3.选择套餐,推荐选用3.5$每月这款套餐,2.5$每月的只支持ipv6,可能连接不上

4.最后这几个选项不用动,点击deploy now即可

5.等待服务器状态变为running,点击服务器名

6.跳转到以下页面后,查看服务器ip地址,用户名默认为root,记住密码

完成以上步骤,服务就搭建完毕了,记住ip地址以及密码,接下来进行ssh连接

4.使用ssh终端连接服务器

这里我使用的是mac的终端进行ssh连接

1.首先进行ssh登录,将用户名和ip地址替换为你的服务器的用户名与ip地址

1
2
3
ssh root@ip
此时如果成功会让你输入密码
复制就好

当出现[root@vultr ~]#时说明已经登录VPS成功,可进行以下步骤。

2.分别执行以下三个命令,安装ss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 我选择进入一个本地目录,这样下载的东西更加可控
cd /usr/local/src
mkdir shadowsocks
cd shadowsocks


# 下载命令
wget --no-check-certificate https://raw.githubusercontent.com/teddysun/shadowsocks_install/master/shadowsocks.sh

# 可执行权限
chmod +x shadowsocks.sh

# 这个过程最后会让你输入 port 和 password
./shadowsocks.sh 2>&1 | tee shadowsocks.log

此时你只有一个账号,就是你刚刚输入的端口号和密码

5.配置多用户

有时候想给小伙伴一起分享这个ss,但又不想一起用一个号,那么我们最好配置多个用户

1.在登录的状态下(登录服务器的前提下),输入以下命令,查看配置文件

1
cat  /etc/shadowsocks.json

默认配置

1
2
3
4
5
6
7
8
9
10
{
"server":"0.0.0.0",
"server_port":443,
"local_address":"127.0.0.1",
"local_port":1080,
"password":"aabbcc",
"timeout":300,
"method":"aes-256-cfb",
"fast_open":false
}

2.修改默认配置

我就默认你是mac了,如果你是 window请安装gitbash

1
vi /etc/shadowsocks.json

点击i键进行编辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"server":"0.0.0.0",
"local_address":"127.0.0.1",
"local_port":1080,
"port_password":{
"9001":"pwd123",
"9002":"pwd123",
"9003":"pwd123",
"9004":"pwd123"
},
"timeout":300,
"method":"aes-256-cfb",
"fast_open": false
}

编辑完毕后,点击esc键,再输入:wq保存

3.开放端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看已开放端口
# 此时可能会导致你退出远程服务器
firewall-cmd --list-ports

# 开启端口 以开启9001端口为例
# 就是shadowsocks.json里 你设置的账号端口
# 如果是多账号就执行多次(因为多个端口)
firewall-cmd --zone=public --add-port=9001/tcp --permanent

# 重启防火墙
firewall-cmd --reload


# 上述命令很有可能会导致你退出远程服务器
# 如果退出后,你远程链接都受限了,那就请 restart 你的服务器

在这里将新建的端口全部打开,如果不执行这部操作,很可能连接不上

4.重启ss

1
ssserver -c/etc/shadowsocks.json -d restart

重启成功后,就可以将地址与密码分享给你的小伙伴咯。

6.使用BBR加速

1
2
3
wget –no-check-certificate https://github.com/teddysun/across/raw/master/bbr.sh
chmod +x bbr.sh
./bbr.sh

至此 Vultr(VPS)搭建SS教程已经结束,望大家都能自由上网。

7.如何测试是否成功了

  • 我的是mac 安装好 ShadowsocksX-NG,客户端
  • 服务器设置点击 +

    1
    2
    3
    地址:你的服务器ip:你刚才添加的端口 如 9001
    加密算法:在你的 shadowsocks.json 里写着
    密码:就是刚才设置的如 pws123
  • 点击确定

  • 打开 https://www.google.cn/

如果是chrome 推荐你安装一个插件

  • Proxy SwitchyOmega 能帮你自动切换(是否fq)

分享不易

如果你打算买 vultr 的服务器

参考链接

如果它们还没被和谐

TS入门001安装与调试

TypeScript

前置知识

  • 命令行知识(熟悉)

每门语言都有一些错误,而 TS 就是为了解决 JS 的错误

  • 安装,请保证版本一致(出了问题自己解决)

1
2
# 安装 typescript 命令行环境
npm install typescript@2.9.2 -g

安装成功后 会得到两个命令

  • tsc
  • tsserver
1
2
3
4
/usr/local/bin/tsc -> /usr/local/lib/node_modules/typescript/bin/tsc
/usr/local/bin/tsserver -> /usr/local/lib/node_modules/typescript/bin/tsserver
+ typescript@2.9.2
added 1 package from 1 contributor in 7.583s

安装 ts-node 让 node 支持 ts

1
2
3
4
5
6
npm install ts-node@7.0.0 -g

# 安装成功后 提示如下
/usr/local/bin/ts-node -> /usr/local/lib/node_modules/ts-node/dist/bin.js
+ ts-node@7.0.0
added 11 packages from 42 contributors in 1.49s

注意!请记住上述命令的路径,后面要用到

注意!请记住上述命令的路径,后面要用到

注意!请记住上述命令的路径,后面要用到

1
2
3
4
5
/usr/local/bin/tsc

/usr/local/bin/tsserver

/usr/local/bin/ts-node

调试

  1. 下载 vscode
    1. mac 按 command + K 之后在按 command + S
    2. 将格式化文件的快捷键绑定到自己喜欢的按键(我用的是 ctrl + L)
  2. 创建文件夹 tsdemo
  3. 用 vscode 打开 tsdemo 目录
  4. 创建 tsdemo/1.ts 作为我们的第一个 TS 文件
  5. 在文件里写一句 console.log(1) 保存
  6. Windows 用户注意了,这里需要单独运行一些命令(Linux 用户和 macOS 用户不用执行)
    • npm init -y
      • npm i -D ts-node typescript
  7. 创建 tsdemo/.vscode/launch.json 文件,内容如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    {
    "configurations": [
    {
    "name": "ts-node",
    "type": "node",
    "request": "launch",
    "program": "注意看这里,要写成ts-node对应的可执行文件,Windows 用户注意了,你应该写成 ${workspaceRoot}/node_modules/ts-node/dist/bin.js",
    "args": ["${relativeFile}"],
    "cwd": "${workspaceRoot}",
    "protocol": "inspector"
    }
    ]
    }

    复制刚才的 ts-node 路径 /usr/local/bin/ts-node
    {
    "configurations": [
    {
    "name": "ts-node",
    "type": "node",
    "request": "launch",
    "program": "/usr/local/bin/ts-node",
    "args": ["${relativeFile}"],
    "cwd": "${workspaceRoot}",
    "protocol": "inspector"
    }
    ]
    }
  8. 打开 tsdemo/1.js,找到调试选项(小虫子图标),选择 ts-node,然后点击调试 在点击那个绿色箭头

    1
    2
    但是此时报错了,因为你没选中那个1.ts文件
    此时切换到 1.ts的文件 再次点击调试

参考文章: https://segmentfault.com/a/1190000011935122

至此我们运行了第一个 ts程序

tsc的使用

greeter.ts

1
2
3
4
5
6
7
function greeter(person) {
return "Hello, " + person;
}

let user = "Jane User";

document.body.innerHTML = greeter(user);

在命令行,注意当前 greeter.ts 所在路径

1
2
3
4
5
6
7
8
tsc greeter.ts

# 此时生成了 greeter.js
function greeter(person) {
return "Hello, " + person;
}
var user = "Jane User";
document.body.innerHTML = greeter(user);

你会说跟原来没啥区别啊

  • 只不过去除了空行吗?
  • 如果你写的仅仅是 JS 那编译后就是 JS

为什么 要tsc 编译一下呢?

1
2
3
4
5
6
7
8
1. 浏览器可以直接运行 JS
2. 浏览器不可以直接运行 TS
⬇️
⬇️
3. 要把 TS 变成 JS ,才能运行

==> tsc
==> type script compiler typescript编译器

再看文档示例

  • 他想规定 greeter函数接受的参数是一个字符串
1
2
3
4
5
6
7
function greeter(person: string) {
return "Hello, " + person;
}

let user = "Jane User";

document.body.innerHTML = greeter(user);

编译后竟然还跟之前的内容一样

解析 function greeter(person: string)

修改 greeter.ts

1
2
3
4
5
6
7
function greeter(person: string) {
return "Hello, " + person;
}

let user = [1,2,3];

document.body.innerHTML = greeter(user);
  • 再次运行 tsc greeter.ts,此时报错了
1
2
3
4
5
6
7
 Argument of type 'number[]' is not assignable to parameterof type 'string'.

7 document.body.innerHTML = greeter(user);

/*
意思 你的 user 是一个数组,但是函数里说要一个字符串,所以无法变成js
*/

中间发生了什么

1
2
3
4
5
         通过 tsc
TS -------------------> JS
1.类型检查

如果你 TS 中期待的类型传入不会 就无法编译为 JS

为什么做类型检查?

如果不用 ts 你就要这样写

1
2
3
4
5
6
7
8
function greeter(person) {
if(typeof person !== 'string'){
throw new Error('person 必须是一个字符串')
}
return "Hello, " + person;
}
var user = [1, 2, 3];
document.body.innerHTML = greeter(user);

此时有什么问题吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
程序必须运行到
if(typeof person !== 'string'){...}
才能提示用户


而 TS 不一样,它是在你编译的时候就告诉你 写错了

----------------------------------------
通过 tsc
TS -------------------> JS
1.类型检查
用 JS 检查:执行到 greeter(user) 报错 运行时报错
用 TS 检查:编译时报错

有区别吗?

执行的 greeter 时候 代码已经到服务器了,用户感知不到的,程序执行到哪里报错了,你难道让用户改吗?

所以如果我们在 上传代码之前就知道这个错是不是可以 及时改过来

接口 interface

1
2
3
4
5
6
7
8
9
10
11
12
interface Person{
姓:string,
名:string
}

function greeter(person: Person) {
return "Hello, " + person.姓 +person.名;
}

let user = {姓:'漩涡',名:'鸣人'};

console.log( greeter(user) );

只有姓 没有名 行不行?

1
2
3
4
5
6
7
8
9
10
11
12
13
let user = {姓:'漩涡'};

报错
error TS2345: Argument of type '{ 姓: string; }' is not assignable to parameter of type 'Person'.
Property '名' is missing in type '{ 姓: string; }'.
--------------------------------
如果你用传统 js
function greeter(person) {
return "Hello, " + person.姓 +person.名;
}

如果 person没有 姓/名
就会返回 Hello undefined undefined

类 class

继续抄文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Student {
全名: string;
constructor(public 姓, public 名) {
this.全名 = 姓 + 名;
}
}

interface Person {
姓: string;
名: string;
}

function greeter(person : Person) {
return "Hello, " + person.姓 + " " + person.名;
}

let user = new Student("漩涡", "鸣人");

console.log(greeter(user));

public 的含义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Student {
全名: string;
constructor(public 姓, public 名) {
this.全名 = 姓 + 名;
}
}

相当于
class Student {
全名: string;
姓:string;
名:string;
constructor(姓, 名) {
this.姓 = 姓;
this.名 = 名;
this.全名 = 姓 + 名;
}
}

此时我能在 greeter 里获取 全名 吗?

1
2
3
4
5
6
7
8
9
10
11
12
function greeter(person : Person) {
return "Hello, " + person.全名;
}

报错
Property '全名' does not exist on type 'Person'.


如果你想要全名唯一的办法就是改参数类型
function greeter(person : Student) {
return "Hello, " + person.全名;
}

如何学TS

  • CRM
  • 单纯 既然学TS 就不要去搞 webpack / vue

    1
    2
    什么时候去整 webpack / vue
    你已经入门了 知道大概的错如何解决。再去结合其他东西
  • 不要看书(它不够新)——看文档和博客

打开 ts 文档

https://www.tslang.cn/docs/home.html

挨个看,哪里没见过就重点看

函数部分

要求输入两个参数,类型都为 number,返回值为 number

1
2
3
function add(a:number,b:number):number{
return a+b;
}
  • 如何实现重载
1
2
3
4
5
6
7
function add(a:number,b:number):number;
function add(a:string,b:string):string;
// 最后一种一定要兼容上面几种
// any 就是任何类型
function add(a:any,b:any):any{
return a+b;
}
  • 返回数组类型
1
2
3
4
function getArr(arr:number[]) : number[]{
...
return [1,2,3,4,5];
}

tuple 元组

元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。

1
2
3
4
5
6
// Declare a tuple type
let x: [string, number];
// Initialize it
x = ['hello', 10]; // OK
// Initialize it incorrectly
x = [10, 'hello']; // Error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Person{
gender:string
}

function merry(a:Person,b:Person):[Person,Person]{
if(a.gender !== b.gender){
return [a,b];
}else{
throw new Error('同性不能结婚')
}
}

let a = {gender:'男'};
let b = {gender:'女'};

console.log(merry( a , b ));

代码此时有个问题就是 如果它写 let b = {gender:’狗’} 也成功了

枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
enum Gender{
Male,
Female
}
interface Person{
gender:Gender
}

function merry(a:Person,b:Person):[Person,Person]{
if(a.gender !== b.gender){
return [a,b];
}else{
throw new Error('同性不能结婚')
}
}

let a = {gender:Gender.Male};
let b = {gender:Gender.Female};

console.log(merry( a , b ));

React入门012之React组件示例

React组件

1
2
3
4
5
6
7
8
cd ~/Desktop

create-react-app react-button-demo


cd react-button-demo

yarn start

styles.css

1
2
3
4
5
6
7
8
9
10
11
12
13
.App {
text-align: center;
}

.App button{
border:1px solid red;
background: red;;
color:#fff;
padding:3px 10px;
box-shadow: 0 0 3px rgba(0,0,0,0.2);
margin:4px;
border-radius: 4px;
}

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { Component } from 'react';
import './App.css';

class App extends Component {
render() {
return (
<div className="App">
<button>Hello</button>
<button>Hello</button>
</div>
);
}
}

export default App;

此时如果 另一个组件里想用这个 button 唯一的办法就是把 样式copy一份 button

第一个组件 BoxOne

BoxOne.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React, { Component } from 'react';
import './BoxOne.css';

class BoxOne extends Component {
render() {
console.log(this.props.firstLine)
return (
<div className="BoxOne">
<div> {this.props.firstLine || 'Box 1'} </div>
</div>
);
}
}

export default BoxOne;

BoxOne.css

1
2
3
.BoxOne{
border:1px solid green;
}

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React, { Component } from 'react';
import './App.css';

import BoxOne from './BoxOne'

class App extends Component {
render() {
return (
<div className="App">
<button className="btn">Hello</button>
<button className="btn">Hello</button>
<BoxOne firstLine="Hello" />
<BoxOne/>
</div>
);
}
}

export default App;

组件的好处是什么?

  • 当你的组件不复杂时 你直接写一遍就好了
  • 但如果组件重复的使用了 100次呢?
  • 如果后期改样式 你是不是得改1000次?
  1. 不需要 ctrl + c / ctrl + v
  2. 多处用 一个 标签
  3. 要改,只改一个地方
  4. 个性化只需要传递参数

写一个button组件

  • src下新建 components 目录
  • src/components/button.js

button.js

1
2
3
4
5
6
7
8
9
10
11
import React, { Component } from 'react';
import './button.css'
class Button extends Component {
render() {
return (
<button className="button2">{this.props.value}</button>
);
}
}

export default Button;

button.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.button2{
box-sizing: border-box;
height: 32px;
background: #fff;
border:1px solid rgba(153,153,153,1);
border-radius: 4px;
padding:4px 1em;
margin:10px;
}

.button2:focus{
outline: none;
border-color: black;
}

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React, { Component } from 'react';
import './App.css';
import Button from './components/button'

class App2 extends Component {
render() {
return (
<div className="App">
<Button value="按钮"/>
<Button value="按钮"/>
<Button value="按钮"/>
</div>
);
}
}

export default App2;

做个涟漪效果

button.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
.button2{
box-sizing: border-box;
height: 32px;
background: #fff;
border:1px solid rgba(153,153,153,1);
border-radius: 4px;
padding:4px 1em;
margin:10px;
position: relative;
overflow: hidden;
}

.button2:focus{
outline: none;
border-color: black;
}

.button2 > .circle{
border-radius: 50%;
height: 10px;
width: 10px;
background: red;
display: inline-block;
animation: 1s big;
position: absolute;
}

@keyframes big{
0%{
transform: scale(0.1);
}
100%{
transform: scale(10);
}
}

button.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import React, { Component } from 'react';
import './button.css'

class Button extends Component {
constructor(props){
super(props);
this.state = {
active:false,
}
}
x(){
this.setState({
active:true,
})
}
render() {
return (
<button className="button2"
onClick={ this.x.bind(this) }
>
{ this.props.value }
{ (this.state.active === true)?(
<span className="circle"></span>
):''}
</button>
);
}
}

export default Button;

此时我希望涟漪出现的地方是我点击的地方

button.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import React, { Component } from 'react';
import './button.css'

class Button extends Component {
constructor(props){
super(props);
this.state = {
active:false,
deltaX:0,
deltaY:0
}
// 当前节点
this.myRef = React.createRef();
}
x(event){
// 事件源
console.log(event);
// 距离左边的距离
console.log(event.clientX);
// 距离上面的距离
console.log(event.clientY);
// 当前按钮的引用
console.log(this.myRef);
// 获取边缘属性
console.log(this.myRef.current.getBoundingClientRect())
//
let {x,y} = this.myRef.current.getBoundingClientRect();
let {clientX,clientY} = event;
let deltaX = clientX - x - 5;
let deltaY = clientY - y - 5;
// 得到点击的位置
console.log(deltaX,deltaY);

this.setState({
active:true,
deltaX:deltaX,
deltaY:deltaY
})
}
y(){
this.setState({
active:false
})
}
render() {
return (
<button ref={this.myRef} className="button2"
onClick={ this.x.bind(this) }
>
<span className="value">{ this.props.value }</span>

{ (this.state.active === true)?(
<span className="circle" onAnimationEnd={ this.y.bind(this) } style={{left:this.state.deltaX,top:this.state.deltaY}}></span>
):''}
</button>
);
}
}

export default Button;

button.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
.button2{
box-sizing: border-box;
height: 32px;
background: #fff;
border:1px solid rgba(153,153,153,1);
border-radius: 4px;
padding:4px 1em;
margin:10px;
position: relative;
overflow: hidden;
}

.button2:focus{
outline: none;
border-color: black;
}

.button2 > .circle{
border-radius: 50%;
height: 10px;
width: 10px;
background: #ddd;
opacity: 0.5;
display: inline-block;
/* 结束后不恢复原状 */
animation: 1s big forwards;
position: absolute;
z-index: 0;
}
.button2 > .value{
position: relative;
z-index: 1;
}

@keyframes big{
0%{
transform: scale(0.1);
}
100%{
transform: scale(10);
}
}

React入门011之React开发环境搭建

环境搭建

  1. 环境搭建
    • Node 8 以上(稳定版)
    • Npm 6 以上(不需要单独安装因为 Node 自带 npm)
    • 编辑器 VSCode
      • 终端 Git Bash(Windows 用户)、iTerm 2(macOS 用户)
      • 版本管理 Git + GitHub
      • JS 版本:ES 6
      • CSS 版本:兼容 IE 12+、Chrome、Firefox(CSS 3 往上走)
  2. Hello World
  3. 部署 React 应用到 GitHub

文档

安装本地 react 命令行环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 安装
npm i -g create-react-app

# 新建目录
cd ~/Desktop/
create-react-app my-react-app

# 依赖安装成功后显示如下信息
Success! Created my-react-app at /Users/huangjiaxi/Desktop/my-react-app
Inside that directory, you can run several commands:

yarn start
Starts the development server.

yarn build
Bundles the app into static files for production.

yarn test
Starts the test runner.

yarn eject
Removes this tool and copies build dependencies, configuration files
and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

cd my-react-app
yarn start

# 一般我们只用如下三个命令
- yarn start
- yarn build
- yarn test

# 不建议用 yarn eject 尤其是新人


--------------------------
# 进入你的项目目录 并启动项目
cd my-react-app
yarn start

# 此时自动打开了一个3000端口的react项目

目录讲解

1
2
3
4
5
6
7
8
9
10
--|build  打包后的文件
--|public 万年不改的目录
--|src
----|App.css
----|App.js
----|index.css
----|index.js
----|registerServiceWorker.js
--|package.json
--|yarn.lock

index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import React from 'react';
import ReactDOM from 'react-dom';
// 你学会了 引入样式
import './index.css';
/*
为什么叫app,因为 index.js叫做入口 入口是把整个程序启动起来的
所有逻辑卸载 App 里
*/
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));


// serviceWorker.unregister();
// 这个 serviceWorker 你不知道干啥的,你可以试着 删掉来看看
// 删除后依然可以运行

怎么使用js?

aa.js

1
2
3
4
5
function sayHello(){
console.log('hello');
}

export default sayHello

index.js

1
2
3
4
5
6
7
8
9
10
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

import sayHello from './aa';

sayHello(); // 打印 hello

ReactDOM.render(<App />, document.getElementById('root'));

这样你就知道如何引入 css 和 js 了

index.html是什么

  • 基本就是万年不变
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<!-- 网站图标 -->
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- 手机端适配 -->
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
<meta name="theme-color" content="#000000" />
<!-- 安卓应用相关 -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<!-- 浏览器不支持js -->
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import React, { Component } from 'react';
// app 里 引入了一个图片 svg 而且还能当作对象表示
import logo from './logo.svg';
// 引入样式
import './App.css';

class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
}

export default App;
  • 此时 App里的内容是不是很熟悉 JSX啊
  • 注意 JSX 使用的是 xml语法而不是 html语法
  • 注意 JSX 使用的是 xml语法而不是 html语法
  • 注意 JSX 使用的是 xml语法而不是 html语法

部署到github上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cd my-react-app

# 初始化git
git init

# 提交文件 到暂存区
git add .

# 所有代码提交到代码库
git commit -m '第一次提交'

# 打开你的github 新建仓库
react-demo-2019-1-26

# 选择上传本地仓库
git remote add origin git@github.com:slTrust/react-demo-2019-1-26.git
git push -u origin master

# 注意 千万别用 https 坑死你 用SSH

如何让别人预览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# 第一步 cd my-react-app 进入你的应用目录
# 打包你的应用
yarn build

/*
Creating an optimized production build...
Compiled successfully.

File sizes after gzip:

34.71 KB build/static/js/1.fa92c112.chunk.js
763 B build/static/js/runtime~main.229c360f.js
581 B build/static/js/main.419d3410.chunk.js
511 B build/static/css/main.d12a5979.chunk.css

//这个项目 默认认为你是在服务器的根目录
The project was built assuming it is hosted at the server root.
// 你可以控制这个东西 使用 homepage 参数 在 package.json.
You can control this with the homepage field in your package.json.
// 比如
For example, add this to build it for GitHub Pages:

"homepage" : "http://myname.github.io/myapp",

The build folder is ready to be deployed.
You may serve it with a static server:

yarn global add serve
serve -s build

Find out more about deployment here:

http://bit.ly/CRA-deploy

✨ Done in 7.99s.
*/


# 由于 .gitignore 忽略了 build 目录 所以我们要修改它 移除 /build
git add .
git commit -m '第一次 build'
git push


# 在你的远程仓库 点击 settings
# GitHub Pages 下 Source 选择 master branch ==> save
# 此时给你个地址 千万别点 因为是 404
# 因为它不是给你点的 是给你复制点 如 我的是 https://sltrust.github.io/react-demo-2019-1-26/

# 然后找刚才的build 目录 如我的是:
https://sltrust.github.io/react-demo-2019-1-26/build/index.html

# 但是为什么一片空白
# 看 network 发现资源 404
# 因为路径是 https://sltrust.github.io/static/css/main.d12a5979.chunk.css
# 而你没有 static 这个路径


# 回到刚才 build之后提示的 homepage
# 去 package.json 里
# 添加 "homepage" : "http://myname.github.io/myapp",
# 修改为你的 github
https://sltrust.github.io/react-demo-2019-1-26/build

# 重新 build
yarn build

# 重新提交
git add .
git commit -m '第二次 build'
git push

# 重新打开网址 清除缓存
https://sltrust.github.io/react-demo-2019-1-26/build/index.html

# 如果是高手就设置一下预览链接
仓库目录 Code下 点击那个 edit 按钮
这样别人进入你的仓库就能看到预览了