TypeScript基礎
數據類型
ECMAScript標准定義了8種數據類型
- Boolean
- Null
- Undefined
- Number
- BigInt
- String
- Symbol
- Object
interface接口
interface的主要作用如下:
- 對對象的形狀(
shape
)進行描述 - 對類(
class
)進行抽象 Duck Typing
(鴨子類型)
interface Person {
readonly id: number;
name: string;
age?: number;
}
函數表達式
const add: (x: number, y: number, z: number) => number = function (x, y, z): number {
return x + y + z;
}
類
面向對象的三大特性:封裝,繼承,多態
- 封裝:當我們使用類中的某個方法時,我們無需知道類中的具體實現細節
- 繼承:子類可以繼承父類,讓子類具有父類的屬性以及方法
- 多態:子類可以覆蓋父類中的方法,從而讓子類和父類的實例表現出不同的特性
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
run() {
console.log(`${this.name} is running`)
}
}
class Cat extends Animal {
constructor(name: string) {
super(name);
// 重寫父類的構造方法,首先需要使用super調用父類的構造函數
console.log(this.name);
}
run () {
console.log('miao~');
// 當在子類中需要調用父類的方法時,也可以使用super來調用
super.run();
}
}
類中屬性或者方法的訪問修飾符
- private: 僅僅能夠在當前類的內部訪問,在子類或者實例中都無法訪問
- protected: 可以在當前類內部和子類中訪問,無法在實例中訪問
- public: 默認的訪問修飾符,能夠在類,子類,實例中都可以訪問到
- readonly: 用來修飾類中不可變的屬性
- static: 可以直接用類名來訪問其修飾的屬性和方法
接口interface
當多個class需要都需要實現某些相同的方法時,我們可以使用interface來實現
interface Radio {
switchRadio() :void;
}
class Car implements Radio {
switchRadio() {}
}
class Phone implements Radio {
switchRadio() {}
}
常量枚舉
const enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
泛型約束
可以使用extends關鍵字來對泛型參數進行限制
interface IWithLengh {
length: number;
}
function echoWithLength<T extends IWithLengh>(arg: T): T {
console.log(arg.length);
return arg;
}
const str = echoWithLength('abc');
const obj = echoWithLength({ length: 1 });
const arr = echoWithLength([1, 2])
類也可以使用泛型來約束
class Queue<T> {
private data: T[] = [];
push(item: T): void {
this.data.push(item);
}
pop(): T {
return this.data.pop();
}
}
const queue = new Queue<number>();
queue.push(1);
console.log(queue.pop().toFixed(2));
const queue2 = new Queue<string>()
接口也可以使用泛型來約束
interface KeyPair<T, U> {
key: T;
value: U;
}
使用泛型來約束函數
interface IPlus<T> {
(a: T, b: T): T;
}
function plus(a: number, b: number): number {
return a + b;
}
const a: IPlus<number> = plus
類型別名
類型別名多用於聯合類型中
type NameResolver = () => string;
type NameOrResolver = string | NameResolver;
function getName(n: NameOrResolver): string {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
類型斷言
function getLength(input: string | number): number {
if ((<string>input).length) {
return (<string>input).length;
} else {
return input.toString().length;
}
}
React基礎
使用腳手架工具搭建項目
首先使用create-react-app搭建項目
npx create-react-app ts-with-react --typescript
簡單的函數式組件
interface IHelloProps {
message: string;
}
const Hello: React.FC<IHelloProps> = (props) => {
return <h2>{ props.message }</h2>
}
Hello.defaultProps = {
message: 'Hello World'
}
React Hooks解決的問題
- 組件很難復用狀態邏輯
- 復雜組件難以理解,尤其是生命周期函數
useState
該hook相當於組件內的狀態
const LikeButton: React.FC = () => {
const [like, setLike] = useState(0)
const [status, setStatus] = useState(true)
return (
<>
<button onClick={() => { setLike(like + 1)}}>
{like}
</button>
<button onClick={() => { setStatus(!status) }}>
{String(status)}
</button>
</>
)
}
useEffect
該hook默認會在第一次渲染完成和每次界面更新時執行,相當於class組件中的componentDidMount
和componentDidUpdate
useEffect(() => {
document.title = `點擊了${like}次`
})
使用useEffect的返回值清除副作用
const LikeButton: React.FC = () => {
const [position, setPosition] = useState({ x: 0, y: 0 })
useEffect(() => {
const updatePostion = (e: MouseEvent) => {
setPosition({
x: e.clientX,
y: e.clientY
})
}
document.addEventListener('click', updatePostion)
return () => {
// 會在下一次更新界面之后,重新添加此effect之前執行
document.removeEventListener('click', updatePostion)
}
})
return <p>X: {position.x}, Y: {[position.y]}</p>
}
控制useEffect執行時機,這里需要使用到useEffect的第二個參數。第二個參數是一個數組,可以填入依賴項,當這些依賴項發生變化時,才會去執行useEffect。當第二個參數為空數組,顯然這種情況是沒有依賴可以變化的,因此這種情況的useEffect僅僅會在組件加載和卸載時執行一次。
useEffect(() => {
document.title = `點擊了${like}次`
}, [like])
自定義hook
自定義hook需要以use開頭
const useMousePosition = () => {
const [position, setPosition] = useState({ x: 0, y: 0 })
useEffect(() => {
const updatePostion = (e: MouseEvent) => {
setPosition({
x: e.clientX,
y: e.clientY
})
}
document.addEventListener('click', updatePostion)
return () => {
document.removeEventListener('click', updatePostion)
}
}, [])
return position
}
使用一個自定義hook
const position = useMousePosition()
使用自定義hooks封裝一個請求公共hooks
import { useState, useEffect } from 'react'
import axios from 'axios'
const useURLLoader = (url: string, deps: any[] = []) => {
const [data, setData] = useState<any>(null)
const [loading, setLoading] = useState(false)
useEffect(() => {
setLoading(true)
axios.get(url).then(result => {
setData(result.data)
setLoading(false)
})
}, deps)
return data
}
export default useURLLoader
完成組件庫
完成一個組件庫需要考慮的問題
- 代碼結構
- 樣式解決方案
- 組件需求分析和編碼
- 組件測試用例分析和編碼
- 代碼打包輸出和發布
- CI/CD,文檔生成等
CSS解決方案
- inline css
- css in js
- styled component
- sass/less
未完待續....2020年06月15日19:18:23