---
###### tags: `TypeScript`
---
# 10/21
## 登入功能撰寫
- 安裝 cookie-session
`npm install cookie-session`
```javascript=
// index.ts
import cookieSession from "cookie-session"
app.use(cookieSession({
name: 'session',
keys: ['teacher dell'],
maxAge: 24 * 60 * 60 * 1000 // 24 小時
}))
// router.ts
// 判斷是否登入
const isLogin = req.session ? req.session.login : false;
// 展示資料
import fs from "fs";
import path from "path";
router.get("/showData", (req: RequestWithBody, res: Response) => {
const isLogin = req.session ? req.session.login : false;
if (!isLogin) {
res.send("請登入後查看");
return;
}
try {
const position = path.resolve(__dirname, "../data/course.json");
const result = fs.readFileSync(position, "utf-8");
res.json(JSON.parse(result));
} catch (e) {
res.send(`尚未爬取到內容`);
}
});
```

---
### 定義統一結果:返回 json 格式
- 將確認登入邏輯抽離成中間件
```javascript=
const checkLogin = (req: Request, res: Response, next: NextFunction) => {
const isLogin = req.session ? req.session.login : false;
if (isLogin) {
next();
} else {
res.json(getResponseData(null, "請先登入"));
}
};
// ----- 獲取資料 -----
router.get("/getData", checkLogin, (req: BodyRequest, res: Response) => {
//...
}
// ----- 展示資料 -----
router.get("/showData", checkLogin, (req: BodyRequest, res: Response) => {
}
```
- 建立 utils 資料夾將 爬蟲功能移入
- 修改 Crawler 內的路徑
```javascript=
class Crawler {
private filePath = path.resolve(__dirname, "../../data/course.json");
}
```
- 創建 util.ts 定義統一結果格式
```javascript=
interface Result {
success: boolean;
errMsg?: string;
data: any;
}
export const getResponseData = (data: any, errMsg?: string): Result => {
if (errMsg) {
return { success: false, errMsg, data };
}
return { success: true, data };
};
```
```javascript=
// 在 router.ts 引用
import { getResponseData } from "./utils/util";
// 使用格式
res.json(getResponseData(true));
res.json(getResponseData(null, "請先登入"));
res.json(getResponseData(false, "登入失敗"));
```
- 完成爬蟲 API 撰寫,但沒有使用介面通過class的形式
- ==並沒有發揮 TS 類的優勢,需使用裝飾器==
## 裝飾器 Decorator ( @ )
### 類的裝飾器
- 裝飾器本身是一個函數
- 裝飾器接收的參數是構造函數
- 裝飾器透過 @ 符號來使用
```javascript=
// 工廠類型包裝
function testDecorator(flag: boolean) {
if (flag) {
return function (constructor: any) {
constructor.prototype.getName = () => {
console.log("testDecorator");
};
};
} else {
return function (constructor: any) {};
}
}
@testDecorator(false)
class Test {}
const test = new Test();
(test as any).getName();
```
==問題:語法提示並不完善==
- 透過泛型<T>改寫
```typescript=
// 這是一個函數,這個函數接收很多參數,參數的每一項都是 any 類型
// new 代表這是構造函數
new (...args: any[]) => any
function testDecorator<T extends new (...args: any[]) => any>(constructor: T) {
return class extends constructor {
name = "lee";
};
}
@testDecorator
class Test {
name: string;
constructor(name: string) {
this.name = name;
}
}
const test = new Test("dell");
```
- 工廠模式運用
```typescript=
function testDecorator() {
return function <T extends new (...args: any[]) => any>(constructor: T) {
return class extends constructor {
name = "lee";
getName() {
return this.name;
}
};
};
}
const Test = testDecorator()(
class {
name: string;
constructor(name: string) {
this.name = name;
}
}
);
const test = new Test("dell");
console.log(test.getName());
```
==這樣寫 getName() 就會有語法提示== (聽不是很懂)
### 方法的裝飾器
```typescript=
// 普通方法 target 對應的是類的 prototype
// 靜態方法 target 對應的是類的構造函數
function getNameDecorator(
target: any,
key: string,
// 可以對方法進行修改?
descriptor: PropertyDescriptor
) {
// descriptor.writable = false;
descriptor.value = function () {
return "descriptor";
};
}
// 定義類的時候 就會對方法進行裝飾
class Test {
name: string;
constructor(name: string) {
this.name = name;
}
@getNameDecorator
getName() {
return "123";
}
}
const test = new Test("dell");
console.log(test.getName()); // 會顯示 descriptor
```
### 訪問器的裝飾器
```typescript=
function visitDecorator(
target: any,
key: string,
descriptor: PropertyDescriptor
) {
descriptor.writable = false;
}
// 定義類的時候 就會對方法進行裝飾
class Test {
private _name: string;
constructor(name: string) {
this._name = name;
}
get name() {
return this._name;
}
@visitDecorator
set name(name: string) {
this._name = name;
}
}
const test = new Test("dell");
test.name = "12345678";
console.log(test.name); // 會報錯無法被修改
```
### 屬性裝飾器
```typescript=
// 修改的並不是實例上的 name,而是原型上的 name
function nameDecorator(target: any, key: string): any {
target[key] = "lee"; // 這樣改會放在 Test.prototype, "name"
}
// name 放在實例上
class Test {
@nameDecorator
name = "Dell"; // 實際上放在類的實例中
}
const test = new Test();
console.log((test as any).__proto__.name); // 會先找 實例上的 name
```