nest.js + Tailwind 搭建图床

1,126 阅读2分钟

(一) 装饰器

  • 类装饰器---应用于类构造函数上。
@sealed
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}

function sealed(constructor: Function) {
    Object.seal(constructor);
    Object.seal(constructor.prototype);
}
  • 方法装饰器---应用到方法的属性描述符上
class Greeter {
    greeting: string;
    constructor(message: string) {
        this.greeting = message;
    }

    @enumerable(false)
    greet() {
        return "Hello, " + this.greeting;
    }
}

function enumerable(value: boolean) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.enumerable = value;
    };
}
  • 访问器装饰器---应用于访问器的 属性描述符
  • 属性装饰器
  • 参数装饰器---明在一个参数声明之前
class Greeter {
    greeting: string;

    constructor(message: string) {
        this.greeting = message;
    }

    @validate
    greet(@required name: string) {
        return "Hello " + name + ", " + this.greeting;
    }
}

import "reflect-metadata";

const requiredMetadataKey = Symbol("required");

function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
    let existingRequiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
    existingRequiredParameters.push(parameterIndex);
    Reflect.defineMetadata(requiredMetadataKey, existingRequiredParameters, target, propertyKey);
}

function validate(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<Function>) {
    let method = descriptor.value;
    descriptor.value = function () {
        let requiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyName);
        if (requiredParameters) {
            for (let parameterIndex of requiredParameters) {
                if (parameterIndex >= arguments.length || arguments[parameterIndex] === undefined) {
                    throw new Error("Missing required argument.");
                }
            }
        }

        return method.apply(this, arguments);
    }
}

www.tslang.cn/docs/handbo…

(二) Reflect Metadata

namespace Reflect {
 // 用于装饰器
 metadata(k, v): (target, property?) => void
 
 // 在对象上面定义元数据
 defineMetadata(k, v, o, p?): void
 
 // 是否存在元数据
 hasMetadata(k, o, p?): boolean
 hasOwnMetadata(k, o, p?): boolean
 
 // 获取元数据
 getMetadata(k, o, p?): any
 getOwnMetadata(k, o, p?): any
 
 // 获取所有元数据的 Key
 getMetadataKeys(o, p?): any[]
 getOwnMetadataKeys(o, p?): any[]
 
 // 删除元数据
 deleteMetadata(k, o, p?): boolean
}

原理: WeakMap<any, Map<any, Map<any, any>>> weakMap.get(o).get(p).get(k)

(三) Nest

Controllers

docs.nestjs.com/first-steps

(四) Tailwind CSS

样式简写 响应式

tailwindcss.com/docs/font-s…

@apply 方法

tailwindcss.com/docs/functi…

配置文件自定义样式

github.com/tailwindlab…

编译注意的地方 purge

(五) 图床

数据库: typeorm.io/#/many-to-o…

亮点:删除上传途中无用的图片

  • cachePic: 存储所有的图片
  • dbPic:存储已经缓存到数据库的图片
  • oldPic:存储将要被删除的图片

在业务层,用户上传图片后,将图片key值塞进 cachePic,在用户将表单信息持久化后,将图片key值 塞进 dbPic。

  • 设置一个定时任务

		const { bucketManager, bucket } = await this.qiNiuService.getManageToken()

		// 清空交集
		const nowDelSet = await this.cacheService.sinter(['cachePic', 'dbPic'])

		for (let key of nowDelSet.values() ) {
			this.cacheService.srem('cachePic', key)
			this.cacheService.srem('dbPic', key)
			this.cacheService.srem('oldPic', key)
		}

		const oldDelSet = await this.cacheService.smembers('oldPic')

		// 先把旧的要删的集合的图片删了
		if (oldDelSet) {
			for (let key of oldDelSet.values() ) {
				this.cacheService.srem('oldPic', key)
				this.cacheService.srem('cachePic', key)

				bucketManager.delete(bucket, key, function(err, respBody, respInfo) {
					if (err) {
						console.log(err);
						//throw err;
					} else {
						console.log(respInfo.statusCode);
						console.log(respBody);
					}
				});
			}
		}

		// 比较缓存和数据库图片,找出要删的图片(包括用户可能即将用的),取补集
		const newDelSet = await this.cacheService.sdiff('cachePic', 'dbPic')

		// 如果有新的要删的图片,就存到旧的里,等待下一回删
		if (newDelSet) {
			for (let key of newDelSet.values() ) {
				this.cacheService.sadd('oldPic', key)
			}
		}