New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

ewm

Package Overview
Dependencies
Maintainers
1
Versions
226
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ewm

小程序原生插件

latest
npmnpm
Version
0.8.9-dev.20220411
Version published
Weekly downloads
2
-81.82%
Maintainers
1
Weekly downloads
 
Created
Source

Commitizen friendly

概述

EWM(Enhanced-Wechat-Miniprogram的缩写) 是微信小程序原生开发插件,提供了新的实例构造器(DefineComponent),并加入了新的TS类型系统,让'小'程序拥有"大"能力。gitee地址

  • 增强的实例构造器(DefineComponent)

    新的实例构建函数(DefineComponent)相比于原生构造器(Page/Component),字段逻辑更清晰,功能更齐全,类型更完善。

  • 更规范的书写规则

    为了让代码逻辑更清晰,容易阅读,EWM加入了新的(很少的)配置字段和规则。例如,原生中methods字段下可以书写组件事件函数,内部方法 生命周期函数。EWM规则中,events字段书写组件事件函数,methods只书写内部方法,页面生命周期写在pageLifetimes字段下。这些规则是靠ts的类型来约束的。 如果您使用js开发,这些规则都是可选的。

  • 独立的子组件

    当组件中包含多个子组件时,把所有组件数据和方法都写在一起很不方便阅读和维护,小程序提供的Behavior存在字段重复和隐式依赖等问题,这都可以认为是js的原罪。EWM 提供的子组件构建函数(CreateSubComponent) 配合 TS 类型系统解决以上问题,让复杂组件更易写易维护。

  • 强大的类型系统

    EWM 拥有强大的类型推导系统,智能的字段提示、重复字段检测、类型检查,让错误被发现于书写代码时。

  • 支持任何第三方组件

    当你引入第三方UI库组件(无组件类型)时,您只要为引入的组件书写一个组件类型(IComponentDoc),即可引入到EWM体系中。EWM提供了内置的泛型CreateDoc,协助您书写第三方组件类型。

  • 完美兼容

    EWM 提供的API和类型系统基于原生,所以不存在兼容性,想用即用。

  • 对js友好 虽然TS开发下更能发挥EWM的特性,但只要您熟悉了EWM规则,使用js开发也是不错的选择,EWM中很多运行时检测是专为js而写的。

安装

  • 依赖安装(ts开发下)

    • typescript npm i --save-dev typescript@^4.6.0 配置tsconfig.json
    {
    	"compilerOptions": {
    		//"lib": ["esnext"],最低es2015
    		"module": "ES6",
    		"strict": true,
    		"moduleResolution": "node",
    		"exactOptionalPropertyTypes": true
    		//...
    	}
    }
    
    • 官方ts类型

      npm i --save-dev @types/wechat-miniprogram

  • 安装 ewm

    • npm安装: npm i ewm

    • 配置文件: ewm.config.js(书写在node_modules同级目录下,配置规则)

    //内部默认配置
    module.exports = {
    	env: 'development',
    	language: 'ts',
    };
    

    ⚠️ 不书写为内部默认配置,更改配置后,需要重新npm构建并清除缓存后生效。

  • mobx(可选)

    如果您不使用状态管理,可忽略安装

    安装 mobx npm i --save mobx

    当前mobx最新版本(当前为mobx@6),若要兼容旧系统(不支持proxy 比如ios9),请安装mobx@4 npm i -save mobx@4 注意: 因为小程序坏境无 process变量 在安装mobx@6 时 会报错process is not defined 需要在npm构建前更改 node_modules\mobx\dist\index.js如下

    原文件

    // node_modules\mobx\dist\index.js
    
    'use strict';
    
    if (process.env.NODE_ENV === 'production') {
    	module.exports = require('./mobx.cjs.production.min.js');
    } else {
    	module.exports = require('./mobx.cjs.development.js');
    }
    

    开发环境可更改为

    // node_modules\mobx\dist\index.js
    module.exports = require('./mobx.cjs.development.js');
    

    生产环境可更改为

    // node_modules\mobx\dist\index.js
    module.exports = require('./mobx.cjs.development.js');
    

    与EWM配置文件关联写法如下

    let IsDevelopment = true;
    try {
    	IsDevelopment = require('../../../../ewm.config').env === 'development';
    } catch (error) {
    }
    if (IsDevelopment) {
    	module.exports = require('./mobx.cjs.development.js');
    } else {
    	module.exports = require('./mobx.cjs.production.min.js');
    }
    
  • 构建npm 开发者工具菜单——工具——构建npm

    详情见官方 npm 介绍

    tips:更改配置文件后,需要重新npm构建并清除缓存后生效

思想

  • 类型为先

    EWM 在设计各个配置字段或 API 时优先考虑的是能否契合TS的类型系统,这可能导致个别字段对于运行时来说是无意义的(生产环境会去掉)。因此相比js,使用ts开发更能发挥EWM的能力。比如 DefineComponent的path 字段,在js开发中可以忽略,但在ts开发下,此字段具有重要意义。

  • 类型即文档

    EWM中,实例构建函数(DefineComponent)返回的类型好比传统意义的组件文档,为引用组件时提供类型支持。EWM内置了原生(Wm)组件类型(暂不完善),对于第三方ui库组件,EWM会逐步拓展,为其提供类型支持(欢迎您的PR)。组件类型书写简单,您完全可以为自己的项目书写组件类型

    示例1

    示例中用到的类型可前往重要类型查看

    // 自定义组件Demo
    import { AuxType, DefineComponent } from 'ewm';
    export interface User {
    	name: string;
    	age?: number;
    }
    const demoDoc = DefineComponent({
    	properties: {
    		/**
    		 * @description num描述
    		 */
    		num: Number,
    		/**
    		 * @description str描述。
    		 */
    		str: {
    			type: String as AuxType<'male' | 'female'>,
    			value: 'male',
    		},
    		/**
    		 * @description union描述
    		 */
    		union: {
    			type: Array as AuxType<User[]>,
    			value: { name: 'zhao', age: 20 },
    			optionalTypes: [Object as AuxType<User>],
    		},
    	},
    	customEvents: { //字段书写规则请看 API——DefineComponent——customEvent。
    		/**
    		 * @description 自定义事件customeEventA描述
    		 */
    		customeEventA: String as AuxType<'male' | 'female'>, // detailType为string类型 => 'male' | 'female'
    		/**
    		 * @description 自定义事件customeEventB描述
    		 */
    		customeEventB: [String, Number], // detailType为联合类型 => string | number
    		/**
    		 * @description 自定义事件customeEventC描述
    		 */
    		customeEventC: {
    			detailType: Object as AuxType<User>, // detailType为对象类型=> User
    			options: { bubbles: true }, //同原生写法
    		},
    		/**
    		 * @description 自定义事件customeEventD描述
    		 */
    		customeEventD: {
    			detailType: Array as unknown as AuxType<[string, number]>, // detailType为元组类型 => [string,number]
    			options: { bubbles: true, composed: true }, //同原生写法
    		},
    		//...
    	},
    	// ...
    });
    
    export type Demo = typeof demoDoc; // 导出组件类型
    
    // Demo 等效于
    // type Demo = {
    //     properties: {
    //         num: number;
    //         str?: {
    //             type: "male" | "female";
    //             default: "male";
    //         };
    //         union?: {
    //             type: User | User[];
    //             default: {
    //                 name: "zhao";
    //                 age: 20;
    //             };
    //         };
    //     };
    //     events: {
    //         customeEventA: 'male' | 'female';
    //         customeEventB: string | number;
    //         customeEventC: {
    //				detailType:{name: string; age?: number },
    // 				options:{ bubbles: true }
    // 		   };
    //		   customeEventD: {
    //				detailType:[string, number],
    // 				options:{ bubbles: true; composed: true }
    // 		   };
    //     };
    // };
    

    示例1中导出的类型 Demo 好比如下书写的组件描述文档

    properties 属性描述默认值类型是否必传
    numnum描述number
    strstr描述"male""male" |"female"
    unionunion描述{ name: "zhao",age: 20 }User | User[]
    自定义事件描述传递数据类型options 配置
    customeEventA自定义事件customeEventA描述'male' | 'female'
    customeEventB自定义事件customeEventB描述string | number
    customeEventC自定义事件customeEventC描述{name: string, age?: number }{ bubble: true }
    customeEventD自定义事件customeEventD描述[string, number]{ bubble: true, composed: true }
  • 关键数据和方法必须预声明

    原生开发时,子组件给父组件传值经常使用实例方法triggerEvent,这种写法不经意间把自定义事件名和配置隐藏在一些方法逻辑当中。不便重复调用,不易阅读,也无法导出类型。DefineComponent构建组件时中增加了customEvents字段用来书写自定义事件配置,方便代码阅读和类型导出。有些其他字段也基于此思想。例如DefineComponent构建页面时的publishEvents字段。

  • 严格的数据管控

    js开发或原生TS类型中,this.setData方法可以书写任何字段配置(或许data中原本没有声明的键名),不利于阅读,也不符合严格的单向数据流控制(组件应只能控制自身data字段),为避免造成数据混乱,EWM重写了setData的类型定义,要求输入配置时只能书写实例配置中data字段下(且非响应式字段)已定义的字段(除非使用as any 忽略TS类型检查),这也符合上面谈到思想————关键数据必须预声明。

    示例2

    import { AuxType, DefineComponent } from 'ewm';
    export interface User {
    	name: string;
    	age?: number;
    }
    DefineComponent({
    	properties: {
    		str: String,
    		user: Object as AuxType<User>,
    	},
    	data: {
    		num: 100,
    	},
    	computed: {
    		name(data) {
    			return data.user.name;
    		},
    	},
    	events: {
    		onTap(e) {
    			const str = this.data.str;
    			const num = this.data.num;
    			const user = this.data.user;
    			this.setData({
    				num: 200, // ok
    				str: 'string', //error  properteis属于父组件控制数据
    				name: 'zhang', // error 计算属性随内部依赖改变,不应在此修改。
    			});
    			//不推荐做法
    			this.setData({
    				xxx: 'anyType',
    			} as any); // 跳过类型约束 不推荐
    		},
    	},
    });
    

特色预览

API

MainData

js开发可以忽略

书写复杂组件时,为了给单独书写的子组件模块提供主数据类型,需要将主数据抽离书写。 MainData函数只接受三个配置字段(properteis,data,computed)。

返回类型为IMainData:

interface IMainData {
	properties?: Record<string, any>; //实际类型较复杂,这里简写了
	data?: Record<string, any>; //实际类型较复杂,这里简写了
	computed?: Record<string, any>; //实际类型较复杂,这里简写了
	allMainData?: Record<string, any>; //实际类型较复杂,这里简写了
}

示例 3

import { AuxType, DefineComponent } from 'ewm';
interface User {
	name: string;
	age?: number;
}

const demoA = DefineComponent({
	properties: {
		a: String,
		user: Object as AuxType<User>,
	},
	data: {
		b: 123,
	},
	computed: {
		name(data) {
			return data.user.name;
		},
	},
});

export type DemoA = typeof demoA;

示例 4

import { AuxType, DefineComponent, MainData } from 'ewm';

const mainData = MainData({
	properties: {
		a: String,
		user: Object as AuxType<{ name: string; age?: number }>,
	},
	data: {
		b: 123,
	},
	computed: {
		name(data) {
			return data.user.name;
		},
	},
});

const demoB = DefineComponent({
	mainData,
	//...
});

export type DemoB = typeof demoB;

DemoA和DemoB的类型完全一致,但在示例4中 主数据类型(typeof mainData)被单独提了出来,方便传递。

这是EWM中最遗憾的地方,暂时还没有更佳的实现方案,期待您给与指点。

DefineComponent

在EWM中实例(页面或组件)都是由DefineComponent函数构建的。 以下是对各个配置字段与原生规则不同之处的说明。在阅读说明前您可能需要了解官方 Component 文档

createSubComponent

用于组件中包含多个子组件时,构建独立的子组件模块。

⚠️由于当前ts内部和外部泛型共用时有冲突,createSubComponent设计为高阶函数,需要两次调用,在第二次调用中书写配置,切记。

CreateSubComponent接受三个泛型(以下提到的泛型即这里的泛型),类型分别为 IMainData(MainData函数返回类型,可输入'{}'占位),IComponentDoc(DefineComponent返回类型(IComopnentDoc),可输入{}占位),Prefix(字符串类型,省缺为空串)。

当输入Prefix时(例如'aaa'),若第二个泛型为中无字段前缀,则要求配置内部字段前缀为'aaa_',若第二个泛型有前缀字段(例如:'demoA'),则要求配置内部字段前缀为 'demoA_aaa_'

CreateSubComponent 还可以用以制作相同逻辑代码的抽离(behaviors),此时第一个泛型与第二个泛型均为{},输入第三个泛型(逻辑名称)做前缀避免与其他behavior字段重复。

不用担心书写的复杂,因为EWM配置字段都有字段提示的,甚至在加了前缀的情况下比无前缀情况下,更便于书写。

示例22 前缀规则

 <!-- parentDemo.wxml -->
<view  >

	<button id='0' str="{{button_0_str}}" str="{{button_0_str}}"/>	
	<button id='1' str="{{button_1_str}}" str="{{button_1_num}}"/>	
	<tabbar str="{{tabbar_str}}" num="{{tabbar_num}}" />
<view />

示例 23

//components/demo/demo.ts
import { CreateSubComponent, DefineComonent, MainData } from 'ewm';
import { Tabbar } from './components/tabbar/tabbar'; //  示例 20
import { Button } from './components/button/button'; //  示例 21
const tabbar = CreateSubComponent<typeof mainData, Tabbar, 'tabbar'>()({ //第二泛型Tabbar无前缀,第三泛型为'tabbar',最终配置字段前缀为tabbar_
	data: {
		// str: 'string', //  error ⚠️此字段要求前缀为tabbar⚠️       前缀检测
		// tabbar_str: 123, // error 不能将"number"赋值给"string"     类型检测
		tabbar_str: 'string', // ok
	},
	computed: {
		tabbar_num(data) { //data中包含自身数据、主数据和注入数据  ok
			return data.user.name;
		},
		// tabbar_xxx(data) { // error  xxx不属于子组件字段          超出字段检测
		// 	return data.user.name;
		// },
	},
});
const button0 = CreateSubComponent<typeof mainData, Button, '0'>()({ //第二泛型Button有前缀"button",第三泛型为'0'最终配置字段前缀为 button_0_
	data: {
		button_0_str: 'string', // ok
	},
	computed: {
		// button_num(data) { // error ⚠️此字段要求前缀为button_0_⚠️
		// 	return data.user.age;
		// },
		button_0_num(data) { // ok
			return data.user.age;
		},
	},
});
const button1 = CreateSubComponent<typeof mainData, Button, '1'>()({ //第二泛型DemoB有前缀"button",第三泛型为'1'最终配置字段前缀为 button_1_
	data: {
		button_1_str: 'string', //ok
	},
	computed: {
		button_1_num(data) { // ok
			return data.user.age;
		},
	},
});
const ViewA = CreateSubComponent<{}, {}, 'viewIdA'>()({ // 第二泛型无前缀, 第三泛型前缀为"viewIdA" 最终配置字段前缀为 viewIdA_
	data: {
		viewIdA_xxx: 'string',
		viewIdA_yyy: 123,
	},
});
const mainData = MainData({
	properties: {
		user: Object as PorpType<{ name: string; age: number }>,
	},
	data: {
		age: 123,
	},
	computed: {
		name(data) {
			return data.user.name;
		},
	},
});
const demo = DefineComponent({
	mainData,
	subComopnent: [tabbar, button0, button1, ViewA],
	events: {
		tabbar_eventA(e) {
			console.log(e.detail); // number
		},
		button_0_eventA(e) {
			console.log(e.detail); // number
		},
		button_1_eventB(e) {
			console.log(e.detail); // string
		},
	},
	//...
});
export type Demo = typeof demo;
  • properties

    当希望子组件类型的properties字段由当前组件调用者(爷爷级)提供数据时书写此字段。类型的索引为子组件类型索引字段,值类型可更改必选或可选,值类型为子组件类型的子集。字段会被主组件继承导出。

    若给子组件传值为wxml提供时(比如子组件数据由wxml上层组件循环产生子数据提供时) 值类型应写为wxml,此字段不会被主组件继承,运行时会忽略此字段。

    <!-- /components/home/home -->
    <view  >
          <tabbar str="{{tabbar_str}}" num="{{tabbar_num}}" />
    		<block wx:for="{{[1,2,3,4]}}}" wx:key="index">
      								<!-- num值并非.ts提供,而有wxml提供 -->
    		<button  str="{{button_str}}" num="{{item}}" />
    	</block>
    <view />
    
     // components/home/home 
    import { CreateSubComponent, DefineComonent, MainData } from 'ewm';
    import { Tabbar } from './components/tabbar/tabbar'; //  示例 20
    import { Button } from './components/button/button'; //  示例 21
    const tabbar = CreateSubComponent<typeof mainData, Tabbar,'tabbar'>()({
    properties: {
    	tabbar_str: { //给子组件传了一个string,并继续交由上级控制。必传变为了可选
    		type:String,
    		value:'string'
    	}
      tabbar_num: Number, //直接交由上级控制赋值。 还是必传字段
      // demoA_xxx:"anyType" // error 不属于子组件proerties范围内  超出字段检测     
      },
     });
    const button = CreateSubComponent<typeof mainData, Button>()({
      properties: {
      button_num: 'wxml', //表示 子组件的num字段由wxml提供。
      },
      data: {
      	//  button_num:123 // error 字段重复因为在properteis中已有了button_num字段  重复字段检测。
      	button_str: 'string', // ok
      }
    });
    const home = DefineComponent({
     subComponet:[tabbar,button]
    });
    export type Home = typeof home
    
  • data

    类型为 子组件字段排除properties中已写字段的其他字段。有重复字段检测和前缀检测。

  • computed

    类型为 子组件字段排除properties和data中已写字段的其他字段。有重复字段检测和前缀检测和超出字段检测。

  • externalMethods

    暴漏给主逻辑调用的接口,主逻辑控制子模块的通道。前缀检测,重复字段检测

    import { CreateSubComponent, DefineComonent, MainData } from 'ewm';
    import { Tabbar } from './components/tabbar/tabbar'; //  示例 20
    const tabbar = CreateSubComponent<typeof mainData, tabbar, 'tabbar'>()({
    	properties: {
    		tabbar_str: { //给子组件tabbar_str传了一个默认值string,并继续交由上级控制。
    			type: String,
    			value: 'string',
    		},
    	},
    	data: {
    		tabbar_num: 123, // 给子组件初始值为 123
    	},
    	externalMethods: {
    		tabbar_changeNum(num: number) { //由主模块调用的接口,添加在主模块this方法上
    			this.setData({
    				tabbar_num, //456
    			});
    		},
    	},
    });
    const demo = DefineComponent({
    	subComponet: [tabbar],
    	lifetimes: {
    		attached() {
    			this.tabbar_changeNum(456); //通过子组件暴漏接口给子组件传递数据。
    		},
    	},
    });
    export type Demo = typeof demo;
    

InstanceInject

全实例注入类

  • 书写注入文件

    // inject.ts
    import { observable, runInAction } from 'mobx';
    import { InstanceInject } from './src/core/instanceInject';
    
    // 注入全局数据
    const globalData = { user: { name: 'zhao', age: 20 } };
    // 注入的响应式数据
    const themeStore = observable({ theme: wx.getSystemInfoSync().theme }); //记得开启主题配置(app.json  "darkmode": true),不然值为undefined
    
    wx.onThemeChange((Res) => {
    	runInAction(() => {
    		themeStore.theme = Res.theme;
    	});
    });
    
    // 注入的方法
    function injectMethod(data: string) {
    	console.log(data);
    }
    // 书写注入配置
    InstanceInject.InjectOption = {
    	data: {
    		injectTheme: () => themeStore.theme,
    		injectGlobalData: globalData,
    	},
    	options: {
    		addGlobalClass: true,
    		multipleSlots: true,
    		pureDataPattern: /^_/,
    	},
    	methods: {
    		injectMethod,
    	},
    };
    // 声明注入类型 js开发可以忽略
    declare module 'ewm' {
    	interface InstanceInject {
    		data: {
    			injectTheme: () => NonNullable<typeof themeStore.theme>;
    			injectGlobalData: typeof globalData;
    		};
    		methods: {
    			injectMethod: typeof injectMethod;
    		};
    	}
    }
    
    • 导入注入文件
    // app.ts
    import './path/inject';
    App({});
    
    • 使用注入数据
    //ComponentA.ts
    import {DefineComponent} from "ewm";
    DefineComponent({
    	methods:{
    		onTap(){
    			console.log(this.data.globalData); //{ user: { name: "zhao", age: 20 } }
    			console.log(this.data.theme); // "dark" | "light"  响应式数据
    			console.log(this.injectMethod) //(data:string)=>void
    		}
    	},
    	lifetimes: {
    		attached() {
    			console.log(this.data.globalData); //{ user: { name: "zhao", age: 20 } }
    			console.log(this.data.theme); // "dark" | "light"  响应式数据
    			console.log(this.injectMethod) //(data:string)=>void
    		}
    	};
    })
    

重要类型

AuxType

常用于辅助书写properties字段和customEvent字段类型

```ts
declare type AuxType<T = any> = {
	new (...arg: any[]): T;
} | {
	(): T;
};
```

IEwmConfig

EWM配置文件类型

export interface IEwmConfig {
	/**
	 * @default 'development'
	 * @description 生产环境会去掉运行时检测等功能。
	 */
	env?: 'development' | 'production';
	/**
	 * @default 'ts'
	 * @description ts环境会关闭一些运行时检测。
	 */
	language?: 'ts' | 'js';
}

CreateDoc

import { CreateDoc } from 'ewm';

type Color = `rgba(${number}, ${number}, ${number}, ${number})` | `#${number}`;
type ChangeEventDetail = {
	current: number;
	currentItemId: string;
	source: 'touch' | '' | 'autoplay';
};
type AnimationfinishEventDetail = ChangeEventDetail;
export type Swiper = CreateDoc<{
	properties: {
		/**
		 * 是否显示面板指示点
		 */
		indicator_dots?: {
			type: boolean;
			default: false;
		};
		/**
		 * 指示点颜色
		 */
		indicatorColor?: {
			type: Color;
			default: 'rgba(0, 0, 0, .3)';
		};
		/**
		 * 当前选中的指示点颜色
		 */
		indicatorActiveColor?: {
			type: Color;
			default: '#000000';
		};
		/**
		 * 是否自动切换
		 */
		autoplay?: {
			type: boolean;
			default: false;
		};
		/**
		 * 当前所在滑块的 index
		 */
		current?: {
			type: number;
			default: 0;
		};
		/**
		 * 自动切换时间间隔
		 */
		interval?: {
			type: number;
			default: 5000;
		};
		/**
		 * 滑动动画时长
		 */
		duration?: {
			type: number;
			default: 500;
		};
		/**
		 * 是否采用衔接滑动
		 */
		circular?: {
			type: boolean;
			default: false;
		}; /**
		 * 滑动方向是否为纵向
		 */
		vertical?: {
			type: boolean;
			default: false;
		};
		/**
		 * 前边距,可用于露出前一项的一小部分,接受 px 和 rpx 值
		 */
		previousMargin?: {
			type: string;
			default: '0px';
		};
		/**
		 * 后边距,可用于露出后一项的一小部分,接受 px 和 rpx 值
		 */
		nextMargin?: {
			type: string;
			default: '0px';
		};
		/**
		 * 当 swiper-item 的个数大于等于 2,关闭 circular 并且开启 previous-margin 或 next-margin 的时候,可以指定这个边距是否应用到第一个、最后一个元素
		 */
		snapToEdge?: {
			type: boolean;
			default: false;
		};
		/**
		 * 同时显示的滑块数量
		 */
		displayMultipleItems?: {
			type: number;
			default: 1;
		};
		/**
		 * 指定 swiper 切换缓动动画类型
		 */
		easingFunction?: {
			type: 'default' | 'linear' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic';
			default: 'default';
		};
	};
	events: {
		/**
		 * current 改变时会触发 change 事件,event.detail = {current, source}
		 */
		change: ChangeEventDetail;
		/**
		 * swiper-item 的位置发生改变时会触发 transition 事件,event.detail = {dx: dx, dy: dy}
		 */
		transition: { dx: number; dy: number };
		/**
		 * animationfinish 动画结束时会触发 animationfinish 事件,event.detail change字段
		 */
		animationfinish: AnimationfinishEventDetail;
	};
}, 'swiper'>;

提示: 强烈推荐使用组件名做为第二个泛型参数('swiper'),返回的子字段键类型会加入前缀("swiper_")

鸣谢

TSRPC 作者@k8w

@geminl @scriptpower @yinzhuoei的无私帮助

赞助

微信 支付宝

ewm探讨群

群名片 若失效可在官方论坛私信 Zhao ZW

Keywords

ewm

FAQs

Package last updated on 11 Apr 2022

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts