文章目录
前言
前面三篇的源码在gitee上可以查看common-template,1.0.1版本是规范篇结束,1.0.2版本是vite配置篇结束,后面都是一些细节补充,各位可以根据自己需要阅读。
1、log工具函数
在src目录下新建utils文件,里面新建log.ts文件,用来输出warn和error的工具函数。
// log.ts
const projectName = import.meta.env.VITE_GLOB_APP_TITLE;
export function warn(message: string) {
console.warn(`[${projectName} warn]:${message}`);
}
export function error(message: string) {
throw new Error(`[${projectName} error]:${message}`);
}
比较简单,没什么可说的。
2、env环境相关函数
新建env.ts文件,这里存放环境变量的工具函数。
// env.ts
import type { GlobEnvConfig } from '/#/config';
import { warn } from '/@/utils/log';
import pkg from '../../package.json';
import { getConfigFileName } from '../../build/getConfigFileName';
/**
* 缓存秘钥前缀
*/
export function getCommonStoragePrefix() {
const { VITE_GLOB_APP_SHORT_NAME } = getAppEnvConfig();
return `${VITE_GLOB_APP_SHORT_NAME}__${getEnvMode()}`.toUpperCase();
}
/**
* 根据版本生成缓存密钥
*/
export function getStorageShortName() {
return `${getCommonStoragePrefix()}${`__${pkg.version}`}__`.toUpperCase();
}
/**
* 获取全局配置(打包时将独立提取配置)
*/
export function getENV(): GlobEnvConfig {
const isMode = isDevMode();
const ENV_NAME = getConfigFileName(import.meta.env);
return isMode ? (import.meta.env as unknown as GlobEnvConfig) : window[ENV_NAME];
}
/**
* @description: 获取环境变量
* @example: development
*/
export function getEnvMode(): string {
return import.meta.env.MODE;
}
/**
* @description: 是否是开发模式
*/
export function isDevMode(): boolean {
return import.meta.env.DEV;
}
/**
* @description: 是否是生产模式
*/
export function isProdMode(): boolean {
return import.meta.env.PROD;
}
export function getAppEnvConfig() {
const ENV = getENV();
const {
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL,
VITE_GLOB_APP_SHORT_NAME,
VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL,
} = ENV;
if (!/^[a-zA-Z\_]*$/.test(VITE_GLOB_APP_SHORT_NAME)) {
warn(`VITE_GLOB_APP_SHORT_NAME变量只能是字符/下划线,请在环境变量中修改并重新运行.`);
}
return {
VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL,
VITE_GLOB_APP_SHORT_NAME,
VITE_GLOB_API_URL_PREFIX,
VITE_GLOB_UPLOAD_URL,
};
}
每个函数的作用都在注释里写着。
这里涉及到一个/#/config路径文件,代表在types目录下的config文件,在src/types目录下新建config.d.ts文件全局类型文件。
// config.d.ts
export interface GlobEnvConfig {
// 站点名称
VITE_GLOB_APP_TITLE: string;
// 服务接口url
VITE_GLOB_API_URL: string;
// 服务接口url前缀
VITE_GLOB_API_URL_PREFIX?: string;
// 项目简称
VITE_GLOB_APP_SHORT_NAME: string;
// 上传url
VITE_GLOB_UPLOAD_URL?: string;
}
3、cipher加密解密函数
这个工具函数用到了crypto-js插件,安装一下。
// package.json
"crypto-js": "^4.1.1",
"@types/crypto-js": "^4.1.1",
新建cipher.ts文件
// cipher.ts
import { encrypt, decrypt } from 'crypto-js/aes';
import { parse } from 'crypto-js/enc-utf8';
import pkcs7 from 'crypto-js/pad-pkcs7';
import ECB from 'crypto-js/mode-ecb';
import md5 from 'crypto-js/md5';
import UTF8 from 'crypto-js/enc-utf8';
import Base64 from 'crypto-js/enc-base64';
export interface EncryptionParams {
key: string;
iv: string;
}
export class AesEncryption {
private key;
private iv;
constructor(opt: Partial<EncryptionParams> = {}) {
const { key, iv } = opt;
if (key) {
this.key = parse(key);
}
if (iv) {
this.iv = parse(iv);
}
}
get getOptions() {
return {
mode: ECB,
padding: pkcs7,
iv: this.iv,
};
}
// 加密
encryptByAES(cipherText: string) {
return encrypt(cipherText, this.key, this.getOptions).toString();
}
// 解密
decryptByAES(cipherText: string) {
return decrypt(cipherText, this.key, this.getOptions).toString(UTF8);
}
}
// UTF8解密
export function encryptByBase64(cipherText: string) {
return UTF8.parse(cipherText).toString(Base64);
}
// Base64解密
export function decodeByBase64(cipherText: string) {
return Base64.parse(cipherText).toString(UTF8);
}
// md5解密
export function encryptByMd5(password: string) {
return md5(password).toString();
}
4、is判断函数
新建is.ts函数,用来判断数据类型
// is.ts
const toString = Object.prototype.toString;
// 判断是否符合[object type],是返回true
export function isType(val: unknown, type: string) {
return toString.call(val) === `[object ${type}]`;
}
// 判断数据类型是否为undefined,是返回false
export function isDef<T = unknown>(val?: T): val is T {
return typeof val !== 'undefined';
}
// 判断数据类型是否为undefined,是返回true
export function isUnDef<T = unknown>(val?: T): val is T {
return !isDef(val);
}
// 判断数据类型是否为Object且不为null,是返回true
export function isObj(val: any): val is Record<any, any> {
return val !== null && isType(val, 'Object');
}
// 判断数据是否为空,是返回true
export function isBlank<T = unknown>(val: T): val is T {
if (isArr(val) || isStr(val)) {
return val.length === 0;
}
if (val instanceof Map || val instanceof Set) {
return val.size === 0;
}
if (isObj(val)) {
return Object.keys(val).length === 0;
}
return false;
}
// 判断数据是否为Date类型,是返回true
export function isTime(val: unknown): val is Date {
return isType(val, 'Date');
}
// 判断数据是否为Null类型,是返回true
export function hasNull(val: unknown): val is null {
return val === null;
}
// 判断数据是否为null或者undef,是返回ture
export function isNullOrUnDef(val: unknown): val is null | undefined {
return isUnDef(val) || hasNull(val);
}
// 判断数据是否是Number类型,是返回true
export function isNum(val: unknown): val is number {
return isType(val, 'Number');
}
// 判断是否是promise类型,是返回true
export function isPromise<T = any>(val: unknown): val is Promise<T> {
return isType(val, 'Promise') && isObj(val) && isFn(val.then) && isFn(val.catch);
}
// 判断数据是否是String类型,是返回true
export function isStr(val: unknown): val is string {
return isType(val, 'String');
}
// 判断是否是function,是返回true
export function isFn(val: unknown): val is Function {
return typeof val === 'function';
}
// 判断数据是否是Boolean,是返回true
export function isBol(val: unknown): val is boolean {
return isType(val, 'Boolean');
}
// 判断数据是否是RegExp,是返回true
export function isRegEx(val: unknown): val is RegExp {
return isType(val, 'RegExp');
}
// 判断是否是数组,是返回true
export function isArr(val: any): val is Array<any> {
return val && Array.isArray(val);
}
// 判断数据是否是Window类型,是返回true
export function isWindow(val: any): val is Window {
return typeof window !== 'undefined' && isType(val, 'Window');
}
// 判断是否为Dom元素,是返回true
export function isEle(val: unknown): val is Element {
return isObj(val) && !!val.tagName;
}
// 判断是否为Map类型,是返回true
export function hasMap(val: unknown): val is Map<any, any> {
return isType(val, 'Map');
}
// 判断是否为网址,是返回true
export function isUrl(path: string): boolean {
const reg = /^http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
return reg.test(path);
}
4、cache缓存函数
新建cache文件,里面存放如下几个文件。
直接上代码
// index.ts
import { getStorageShortName } from '/@/utils/env';
import { createStorage as create, CreateStorageParams } from './storageCache';
import { enableStorageEncryption } from '/@/settings/encryptionSetting';
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
export type Options = Partial<CreateStorageParams>;
const createOptions = (storage: Storage, options: Options = {}): Options => {
return {
// 调试模式下没有加密
hasEncrypt: enableStorageEncryption,
storage,
prefixKey: getStorageShortName(),
...options,
};
};
export const WebStorage = create(createOptions(sessionStorage));
export const createStorage = (storage: Storage = sessionStorage, options: Options = {}) => {
return create(createOptions(storage, options));
};
export const createSessionStorage = (options: Options = {}) => {
return createStorage(sessionStorage, { ...options, timeout: DEFAULT_CACHE_TIME });
};
export const createLocalStorage = (options: Options = {}) => {
return createStorage(localStorage, { ...options, timeout: DEFAULT_CACHE_TIME });
};
export default WebStorage;
// memory.ts
export interface Cache<V = any> {
value?: V;
timeoutId?: ReturnType<typeof setTimeout>;
time?: number;
alive?: number;
}
const NOT_ALIVE = 0;
export class Memory<T = any, V = any> {
private cache: { [key in keyof T]?: Cache<V> } = {};
private alive: number;
constructor(alive = NOT_ALIVE) {
// 单位秒
this.alive = alive * 1000;
}
get getCache() {
return this.cache;
}
setCache(cache) {
this.cache = cache;
}
get<K extends keyof T>(key: K) {
return this.cache[key];
}
set<K extends keyof T>(key: K, value: V, expires?: number) {
let item = this.get(key);
if (!expires || (expires as number) <= 0) {
expires = this.alive;
}
if (item) {
if (item.timeoutId) {
clearTimeout(item.timeoutId);
item.timeoutId = undefined;
}
item.value = value;
} else {
item = { value, alive: expires };
this.cache[key] = item;
}
if (!expires) {
return value;
}
const now = new Date().getTime();
/**
* 防止setTimeout最大延迟值溢出
* 最大延迟值2,147,483,647 ms
* https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#maximum_delay_value
*/
item.time = expires > now ? expires : now + expires;
item.timeoutId = setTimeout(
() => {
this.remove(key);
},
expires > now ? expires - now : expires,
);
return value;
}
remove<K extends keyof T>(key: K) {
const item = this.get(key);
Reflect.deleteProperty(this.cache, key);
if (item) {
clearTimeout(item.timeoutId!);
return item.value;
}
}
resetCache(cache: { [K in keyof T]: Cache }) {
Object.keys(cache).forEach((key) => {
const k = key as any as keyof T;
const item = cache[k];
if (item && item.time) {
const now = new Date().getTime();
const expire = item.time;
if (expire > now) {
this.set(k, item.value, expire);
}
}
});
}
clear() {
Object.keys(this.cache).forEach((key) => {
const item = this.cache[key];
item.timeoutId && clearTimeout(item.timeoutId);
});
this.cache = {};
}
}
// persistent.ts
import type { ProjectConfig } from '/#/config';
import { createLocalStorage, createSessionStorage } from '/@/utils/cache';
import { Memory } from './memory';
import { PROJ_CFG_KEY, APP_LOCAL_CACHE_KEY, APP_SESSION_CACHE_KEY } from '/@/enums/cacheEnum';
import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
import { toRaw } from 'vue';
interface BasicStore {
[PROJ_CFG_KEY]: ProjectConfig;
}
type LocalStore = BasicStore;
type SessionStore = BasicStore;
export type BasicKeys = keyof BasicStore;
type LocalKeys = keyof LocalStore;
type SessionKeys = keyof SessionStore;
const ls = createLocalStorage();
const ss = createSessionStorage();
const localMemory = new Memory(DEFAULT_CACHE_TIME);
const sessionMemory = new Memory(DEFAULT_CACHE_TIME);
function initPersistentMemory() {
const localCache = ls.get(APP_LOCAL_CACHE_KEY);
const sessionCache = ss.get(APP_SESSION_CACHE_KEY);
localCache && localMemory.resetCache(localCache);
sessionCache && sessionMemory.resetCache(sessionCache);
}
export class Persistent {
static getLocal<T>(key: LocalKeys) {
return localMemory.get(key)?.value as Nullable<T>;
}
static setLocal(key: LocalKeys, value: LocalStore[LocalKeys], immediate = false): void {
localMemory.set(key, toRaw(value));
immediate && ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache);
}
static removeLocal(key: LocalKeys, immediate = false): void {
localMemory.remove(key);
immediate && ls.set(APP_LOCAL_CACHE_KEY, localMemory.getCache);
}
static clearLocal(immediate = false): void {
localMemory.clear();
immediate && ls.clear();
}
static getSession<T>(key: SessionKeys) {
return sessionMemory.get(key)?.value as Nullable<T>;
}
static setSession(key: SessionKeys, value: SessionStore[SessionKeys], immediate = false): void {
sessionMemory.set(key, toRaw(value));
immediate && ss.set(APP_SESSION_CACHE_KEY, sessionMemory.getCache);
}
static removeSession(key: SessionKeys, immediate = false): void {
sessionMemory.remove(key);
immediate && ss.set(APP_SESSION_CACHE_KEY, sessionMemory.getCache);
}
static clearSession(immediate = false): void {
sessionMemory.clear();
immediate && ss.clear();
}
static clearAll(immediate = false) {
sessionMemory.clear();
localMemory.clear();
if (immediate) {
ls.clear();
ss.clear();
}
}
}
function storageChange(e: any) {
const { key, newValue, oldValue } = e;
if (!key) {
Persistent.clearAll();
return;
}
if (!!newValue && !!oldValue) {
if (APP_LOCAL_CACHE_KEY === key) {
Persistent.clearLocal();
}
if (APP_SESSION_CACHE_KEY === key) {
Persistent.clearSession();
}
}
}
window.addEventListener('storage', storageChange);
initPersistentMemory();
// storageCache.ts
import { cacheCipher } from '/@/settings/encryptionSetting';
import type { EncryptionParams } from '/@/utils/cipher';
import { AesEncryption } from '/@/utils/cipher';
import { isNullOrUnDef } from '/@/utils/is';
export interface CreateStorageParams extends EncryptionParams {
prefixKey: string;
storage: Storage;
hasEncrypt: boolean;
timeout?: Nullable<number>;
}
export const createStorage = ({
prefixKey = '',
storage = sessionStorage,
key = cacheCipher.key,
iv = cacheCipher.iv,
timeout = null,
hasEncrypt = true,
}: Partial<CreateStorageParams> = {}) => {
if (hasEncrypt && [key.length, iv.length].some((item) => item !== 16)) {
throw new Error('当hasEncrypt为true时,密钥或iv必须为16位!');
}
const encryption = new AesEncryption({ key, iv });
/**
* 缓存Class
* 构造参数可以传递到sessionStorage、localStorage,
* @class Cache
* @example
*/
const WebStorage = class WebStorage {
private storage: Storage;
private prefixKey?: string;
private encryption: AesEncryption;
private hasEncrypt: boolean;
/**
*
* @param {*} storage
*/
constructor() {
this.storage = storage;
this.prefixKey = prefixKey;
this.encryption = encryption;
this.hasEncrypt = hasEncrypt;
}
private getKey(key: string) {
return `${this.prefixKey}${key}`.toUpperCase();
}
/**
* 设置缓存
* @param {string} key
* @param {*} value
* @param {*} expire 过期时间(秒)
* @memberof Cache
*/
set(key: string, value: any, expire: number | null = timeout) {
const stringData = JSON.stringify({
value,
time: Date.now(),
expire: !isNullOrUnDef(expire) ? new Date().getTime() + expire * 1000 : null,
});
const stringifyValue = this.hasEncrypt
? this.encryption.encryptByAES(stringData)
: stringData;
this.storage.setItem(this.getKey(key), stringifyValue);
}
/**
* 读取缓存
* @param {string} key
* @param {*} def
* @memberof Cache
*/
get(key: string, def: any = null): any {
const val = this.storage.getItem(this.getKey(key));
if (!val) return def;
try {
const decVal = this.hasEncrypt ? this.encryption.decryptByAES(val) : val;
const data = JSON.parse(decVal);
const { value, expire } = data;
if (isNullOrUnDef(expire) || expire >= new Date().getTime()) {
return value;
}
this.remove(key);
} catch (e) {
return def;
}
}
/**
* 基于密钥删除缓存
* @param {string} key
* @memberof Cache
*/
remove(key: string) {
this.storage.removeItem(this.getKey(key));
}
/**
* 删除此instance的所有缓存
*/
clear(): void {
this.storage.clear();
}
};
return new WebStorage();
};
在解释cache缓存函数之前,先新建几个文件。
在src目录下新建settings文件夹,里面用来存放项目配置等,先新建一个encryptionSetting.ts加密配置文件。
// encryptionSetting.ts
import { isDevMode } from '/@/utils/env';
// 系统默认缓存时间(秒)
export const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7;
// aes加密密钥
export const cacheCipher = {
key: '_11111000001111@',
iv: '@11111000001111_',
};
// 系统缓存是否使用aes加密
export const enableStorageEncryption = !isDevMode();
注意在非开发模式下会对缓存进行aes加密。
接着在src/enums目录下新建一个cacheEnum.ts缓存枚举文件。
// cacheEnum.ts
// project config key
export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__';
// base global local key
export const APP_LOCAL_CACHE_KEY = 'COMMON__LOCAL__KEY__';
// base global session key
export const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY__';
在src/types/config.d.ts文件里添加下面代码。
// src/types/config.d.ts
export interface ProjectConfig {
// 会话超时处理
sessionTimeoutProcessing: SessionTimeoutProcessingEnum;
// 使用错误处理程序插件
useErrorHandle: boolean;
// 切换接口时是否删除未关闭的消息并通知
closeMessageOnSwitch: boolean;
// 切换接口时是否取消已发送但未响应的http请求
removeAllHttpPending: boolean;
}
cache缓存函数主要使用Persistent类和createLocalStorage、createSessionStorage两个函数,用来做整个项目的缓存功能。token的本地存储也可用这个缓存函数。
5、domUtils元素操作函数
// domUtils.ts
// 指定元素添加事件监听器
export function on(
element: Element | HTMLElement | Document | Window,
event: string,
handler: EventListenerOrEventListenerObject,
): void {
if (element && event && handler) {
element.addEventListener(event, handler, false);
}
}
// 指定元素移除事件监听器
export function off(
element: Element | HTMLElement | Document | Window,
event: string,
handler: Fn,
): void {
if (element && event && handler) {
element.removeEventListener(event, handler, false);
}
}
// 指定元素只添加一次事件监听器
export function once(el: HTMLElement, event: string, fn: EventListener): void {
const listener = function (this: any, ...args: unknown[]) {
if (fn) {
fn.apply(this, args);
}
off(el, event, listener);
};
on(el, event, listener);
}
这里Fn类型报错,在types文件夹下新建index.d.ts文件。
// index.d.ts
declare interface Fn<T = any, R = T> {
(...arg: T[]): R;
}
6、dateUtils时间操作工具
// dateUtils.ts
/**
* 独立的时间操作工具
*/
import dayjs from 'dayjs';
const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
const DATE_FORMAT = 'YYYY-MM-DD';
// 初始化日期时间
export function formatToDateTime(date?: dayjs.ConfigType, format = DATE_TIME_FORMAT): string {
return dayjs(date).format(format);
}
// 初始化时间
export function formatToDate(date?: dayjs.ConfigType, format = DATE_FORMAT): string {
return dayjs(date).format(format);
}
export const dateUtil = dayjs;
7、mitt事件传递工具
// mitt.ts
/**
* copy to https://github.com/developit/mitt
* 展开clear方法
*/
export type EventType = string | symbol;
// 事件处理程序可以采用可选的事件参数
// 并且不应返回值
export type Handler<T = any> = (event?: T) => void;
export type WildcardHandler = (type: EventType, event?: any) => void;
// 一个类型的所有当前注册的事件处理程序的数组
export type EventHandlerList = Array<Handler>;
export type WildCardEventHandlerList = Array<WildcardHandler>;
// 事件类型及其对应的事件处理程序的映射.
export type EventHandlerMap = Map<EventType, EventHandlerList | WildCardEventHandlerList>;
export interface Emitter {
all: EventHandlerMap;
on<T = any>(type: EventType, handler: Handler<T>): void;
on(type: '*', handler: WildcardHandler): void;
off<T = any>(type: EventType, handler: Handler<T>): void;
off(type: '*', handler: WildcardHandler): void;
emit<T = any>(type: EventType, event?: T): void;
emit(type: '*', event?: any): void;
clear(): void;
}
/**
* Mitt: 小型功能事件传递工具.
* @name mitt
* @returns {Mitt}
*/
export default function mitt(all?: EventHandlerMap): Emitter {
all = all || new Map();
return {
/**
* 事件名称到已注册处理程序函数的映射.
*/
all,
/**
* 为给定类型注册事件处理程序.
* @param {string|symbol} type 要侦听的事件类型,或所有事件的“*”类型
* @param {Function} handler 响应给定事件而调用的函数
* @memberOf mitt
*/
on<T = any>(type: EventType, handler: Handler<T>) {
const handlers = all?.get(type);
const added = handlers && handlers.push(handler);
if (!added) {
all?.set(type, [handler]);
}
},
/**
* 删除给定类型的事件处理程序.
* @param {string|symbol} type 要从中注销`handler`的事件类型,或`“*”`
* @param {Function} handler 要删除的处理程序函数
* @memberOf mitt
*/
off<T = any>(type: EventType, handler: Handler<T>) {
const handlers = all?.get(type);
if (handlers) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
},
/**
* 调用给定类型的所有处理程序.
* 如果存在,则在类型匹配的处理程序之后调用“*”处理程序.
*
* 注意:不支持手动启动“*”处理程序.
*
* @param {string|symbol} type 要调用的事件类型
* @param {Any} [evt] 传递给每个处理程序的任何值(推荐使用且功能强大的对象)
* @memberOf mitt
*/
emit<T = any>(type: EventType, evt: T) {
((all?.get(type) || []) as EventHandlerList).slice().map((handler) => {
handler(evt);
});
((all?.get('*') || []) as WildCardEventHandlerList).slice().map((handler) => {
handler(type, evt);
});
},
/**
* 全部清除
*/
clear() {
this.all.clear();
},
};
}
实际上就是兄弟组件传参的逻辑。
使用mitt除了可以做兄弟组件传参,还可以用来监视路由。
8、index基本工具函数
引入lodash-es函数库
// package.json
"lodash-es": "^4.17.21",
"@types/lodash-es": "^4.17.7",
// index.ts
import { type App, type Component } from 'vue';
import { isArr, isObj } from '/@/utils/is';
import { cloneDeep, mergeWith } from 'lodash-es';
export const noop = () => {};
/**
* 将对象作为参数添加到URL
* @param baseUrl url
* @param obj
* @returns {string}
* eg:
* let obj = {a: '3', b: '4'}
* setObjToUrlParams('www.baidu.com', obj)
* ==>www.baidu.com?a=3&b=4
*/
export function setObjToUrlParams(baseUrl: string, obj: any): string {
let parameters = '';
for (const key in obj) {
parameters += key + '=' + encodeURIComponent(obj[key]) + '&';
}
parameters = parameters.replace(/&$/, '');
return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters;
}
/**
递归合并两个对象。
@param target 目标对象,合并后结果存放于此。
@param source 要合并的源对象。
@returns 合并后的对象。
*/
export function deepMerge<T extends object | null | undefined, U extends object | null | undefined>(
target: T,
source: U,
): T & U {
return mergeWith(cloneDeep(target), source, (objValue, srcValue) => {
if (isObj(objValue) && isObj(srcValue)) {
return mergeWith(cloneDeep(objValue), srcValue, (prevValue, nextValue) => {
return isArr(prevValue) ? prevValue.concat(nextValue) : undefined;
});
}
});
}
// https://github.com/vant-ui/vant/issues/8302
// 用来修复tsx组件缺少事件的typescript定义
type EventShim = {
new (...args: any[]): {
$props: {
onClick?: (...args: any[]) => void;
};
};
};
export type WithInstall<T> = T & {
install(app: App): void;
} & EventShim;
export type CustomComponent = Component & { displayName?: string };
/**
* 全局注册函数
* @param component 需要全局注册的模块
* @returns 注册的模块
*/
export const withInstall = <T extends CustomComponent>(component: T) => {
(component as Record<string, unknown>).install = (app: App) => {
const compName = component.name || component.displayName;
if (!compName) return;
app.component(compName, component);
};
return component as WithInstall<T>;
};
里面有一些常用函数。
结语
本篇内容是1.0.3的内容。未完待续…