MpKit与本文章均在不断更新,为保障您获取到的内容是最新的,请关注:https://imingyu.github.io/2020/mpkit/
前言
近年来,多个公司都开发出了小程序这样的“微利用”计划,其在生态扩大、性能加强等方面都施展着重要的作用;
作为开发者却要同时面对多个小程序平台,实现类似的性能,如果不思考应用框架的话,在底层的一些根底技术解决方案上,有没有一个轻量级的抉择?
咱们在开发一个小程序时,往往会有以下技术需要:
- 全局事件总线
- 优化小程序的setData函数
- 心愿将App/Page/Component上的共有性能拆分,并能够有效性的反复利用,甚至组建为App/Page/Component基类
- 能够全局拦挡执行某些操作,如:异样报错、网络申请、性能禁止、App/Page/Component的生命周期等
基于以上需要,都是我以往实现过的性能,所以我将我的经验总结成了一个开源我的项目MpKit,外面就蕴含对以上需要的性能实现,且不止于此;
MpKit的次要性能都通过单元测试,可放心使用,我的项目主页:https://github.com/imingyu/mpkit;
上面我来介绍下它的具体用法和性能列表。
简介
MpKit公布到了NPM平台,以模块化的形式划分为多个包,某些包不止能用在小程序平台,还能够用到h5等平台;某些包却无奈在小程序上应用,包列表及相干性能如下:
以下包均反对TypeScript语言
包 | 实用平台 | 简介 | |||||
---|---|---|---|---|---|---|---|
小程序 | H5 | Node.js | |||||
微信 | 支付宝 | 百度 | 字节跳动 | ||||
@mpkit/inject | ● | ● | ● | ● | 提供小程序环境实用的多种实用函数或组件,如setData优化、Mixin、事件总线等。查看文档 | ||
@mpkit/ebus | ● | ● | ● | ● | ● | ● | 提供事件触发、监听等性能。查看文档 |
@mpkit/mixin | ● | ● | ● | ● | 为小程序提供混入性能。查看文档 | ||
@mpkit/util | ● | ● | ● | ● | 工具函数查看文档 |
装置
如果你的小程序我的项目中反对引入npm包,那么你间接依据本人的须要装置对应包即可,如:
npm i @mpkit/ebus -S
然而如果你是原生开发,不反对引入npm包,你最大的可能须要用到@mpkit/inject
包,此包中的性能根本蕴含了其余包的所有性能,且反对按需插件化装置,可节俭你的字节空间;应用@mpkit/inject
:
- 在磁盘任意地位创立长期目录如
xx/temp
; - 在此目录中执行命令:
npm i @mpkit/inject
- 找到
xx/temp/node_modules/@mpkit/inject
下的dist
目录,将其下内容拷贝到你的小程序我的项目目录下; - 找到
dist/config.js
文件,将你须要的性能插件引入,如:
// 提供事件类性能import { plugin as EbusPlugin } from './plugins/ebus';// 提供混入性能import { plugin as MixinPlugin } from './plugins/mixin';// 提供setData优化import { plugin as SetDataPlugin } from './plugins/set-data';var config = { // 是否重写全局变量 rewrite: { App: false, // 重写全局变量App为 plugins/mixin 中的MkApp 即 MpKit.App Page: false, // 重写全局变量Page为 plugins/mixin 中的MkPage 即 MpKit.Page Component: false, // 重写全局变量Component为 plugins/mixin 中的MkComponent 即 MpKit.Component Api: false, // 重写全局api变量wx/my/tt为 plugins/mixin 中的MkApi 即 MpKit.Api setData: false // 重写Page/Component中的setData为优化后的setData 即 MpKit.setData }, plugins: [ EbusPlugin, MixinPlugin, SetDataPlugin ]};export default config;
- 如果配置了
config.rewrite
项,请在小程序我的项目的app.js
的第一行处引入@mpkit/inject/dist
中的index.js
文件,否则无奈实现全局变量重写;如果没有配置该项,则须要在应用时引入,如:
import MpKit from '@mpkit/inject/dist/index';MpKit.on(...);
- 装置实现。
- 小提示:不须要的插件js文件能够间接删掉,插件不间接相互依赖。
应用
这里着重介绍@mpkit/inject
包的应用形式和细节,其余包可自行参考对应文档;
@mpkit/inject
包提供下列性能,
办法/变量 | 作用 | 依赖插件 |
---|---|---|
on(eventName:string, handler:Function) | 为某全局事件增加监听函数 | ebus |
off(eventName:string, handler:Function) | 为某全局事件移除监听函数 | ebus |
emit(eventName:string, data:any) | 触发某全局事件并传递数据 | ebus |
App(...mixins:MpAppSpec[]) : MpAppSpec | 接管多个对象,对象构造与小程序App 函数接管的对象构造统一,并将这些对象合并,返回一个新对象,蕴含所有对象的性能和数据,合并策略下文形容。 | mixin |
Page(...mixins:MpPageSpec[]) : MpPageSpec | 接管多个对象,对象构造与小程序Page 函数接管的对象构造统一,并将这些对象合并,返回一个新对象,蕴含所有对象的性能和数据,合并策略下文形容。 | mixin |
Component(...mixins:MpComponentSpec[]) : MpComponentSpec | 接管多个对象,对象构造与小程序Component 函数接管的对象构造统一,并将这些对象合并,返回一个新对象,蕴含所有对象的性能和数据,合并策略下文形容。 | mixin |
Api | 与小程序上的wx my swan tt 等对象的属性和办法统一,只不过在其办法上增加了钩子函数,不便拦挡解决 | mixin |
MixinStore.addHook(type:string, hook:MpMethodHook) | MpKit 中内置了很多全局钩子函数,方可实现了全局拦挡,setData重写等性能,而如果你也想在全局增加本人的钩子函数,那么能够调用此函数 | mixin |
setData(view:any, data:any, callback:Function) : Promise<DiffDataResult> | 以优化的形式向某个Page/Component设置数据,仅设置变动的数据,并以Promise的形式返回diff后的数据后果 | set-data |
依赖
从下面的性能列表中能够看到,某些办法或变量是依赖插件的,如果没有装置相干插件,则无奈应用对用办法;
App/Page/Component
当应用MpKit.App/Page/Component
时,可传递多个对象,如:
import MpKit from '@mpkit/inject/dist/index';// 如果在config中配置了rewrite.App=true,则调用App等同于调用了[未重写的App(MpKit.App)]App(MpKit.App({ globalData: { name: 'Tom', age: 10 }, onShow() { console.log('onShow1') }, add(a, b) { return a + b; }}, { globalData: { age: 20 }, onShow() { console.log(`onShow2, ${this.add(2, 4)}`); console.log(this.globalData); }}));// 输入:onShow1// 输入:onShow2, 6// 输入:{ name: 'Tom', age: 20 }Component(MpKit.Component({ data: { name: 'Alice', products: [ { name: '苹果', price: 6 }, { name: '香蕉', price: 5 } ] }, created() { console.log('created1') }, methods: { sayhi() { console.log(`hi ${this.data.name}`); } }}, { data: { products: [ { price: 8 } ] }, created() { console.log('created2'); this.sayhi(); console.log(this.data.list); }, methods: { sayhi() { console.log(`你好 ${this.data.name}`); } }}));// 输入:created1// 输入:created2// 输入:hi Alice// 输入:你好 Alice// 输入:[ { name: '苹果', price: 8 }, { name: '香蕉', price: 5 } ]
合并策略
从下面的例子能够看出合并策略是:
- 属性进行深度合并
- 办法会保留所有mixin中的办法体,依照程序全副执行
钩子函数
为能够进行全局拦挡App/Page/Component/Api
上的办法,MpKit做了钩子函数的机制,具体为:
- 每个办法执行前会调用
before
钩子 如果
before
钩子函数存在,且有返回值(如果有多个before
钩子则取最初一个不为undefined|true
的后果)时- 对于
App/Page/Component
如果返回值为false
,则不会持续向下执行 - 对于
Api
如果返回值为false
,则不会持续向下执行;同时如果返回值不为true
和undefined
时,会间接将后果返回进来,且不会持续向下执行
- 对于
- 而后执行
办法体
,如果有多个,则顺次全副执行,并返回最初一个不为空的后果 - 执行
after
钩子,并传入办法体
的执行后果 - 如果是
Api
上的异步办法,还会依据后果回调(success|fail)在执行complete
钩子
MpKit内置了很多钩子,用于全局事件触发、办法重写等,同时你能够增加本人的钩子函数,调用:
MpKit.MixinStore.addHook(type:MpViewType.App|MpViewType.Page|MpViewType.Component|'Api', hook:MpMethodHook)
可为 App/Page/Component/Api 增加全局钩子函数;MpMethodHook
的定义如下:
interface MpMethodHookLike { before?( methodName: string, methodArgs: any[], methodHandler: Function, funId?: string ); after?( methodName: string, methodArgs: any[], methodResult: any, funId?: string ); catch?( methodName: string, methodArgs: any[], error: Error, errType?: string, funId?: string ); complete?( methodName: string, methodArgs: any[], res: any, success?: boolean, funId?: string );}interface MpMethodHook extends MpMethodHookLike { [prop: string]: Function | MpMethodHookLike;}
示例1:
import MpKit from "@mpkit/inject/dist/index";import { MpViewType } from "@mpkit/inject/dist/types";MpKit.MixinStore.addHook(MpViewType.App, { before(methodName, methodArgs) { console.log(`before methodName=${methodName}`); }, after(methodName, methodArgs, methodResult) { console.log(`after methodName=${methodName}, ${methodResult}`); }, catch(methodName, methodArgs, error) { console.log(`catch err=${error.message}`); },});App( MpKit.App({ onLaunch() { this.add(1, 2); }, onShow() { throw new Error("test"); }, add(a, b) { return a + b; }, }));// 输入:before methodName=onLaunch// 输入:before methodName=add// 输入:after methodName=add, 2// 输入:after methodName=onLaunch,// 输入:before methodName=onShow,// 输入:catch err=testMpKit.MixinStore.addHook("Api", { before(methodName, methodArgs, methodHandler, funId) { console.log(`before api=${methodName}`); }, after(methodName, methodArgs, methodResult, funId) { console.log(`after api=${methodName}, ${methodResult}`); }, complete(methodName, methodArgs, res, isSuccess, funId) { console.log(`complete api=${methodName}, ${isSuccess}, ${res}`); },});MpKit.Api.request({ url: "...",});// 输入:before api=request// 输入:after api=request, [RequestTask Object]// 假如申请胜利且返回字符串“1”,则输入:complete api=request, true, 1// 假如申请失败,则输入:complete api=request, false, { errMsg:'...' }
示例2:当在before
钩子中返回false
会具体值时:
MpKit.MixinStore.addHook(MpViewType.App, { onShow: { before(methodName, methodArgs) { console.log("hook onShow"); return false; }, },});App( MpKit.App({ onLaunch() {}, onShow() { console.log("self onShow"); }, }));// 仅输入:hook onShowconst store = {};MpKit.MixinStore.addHook("Api", { before(methodName, methodArgs, methodHandler, funId) { console.log(`before methodName=${methodName}`); if (methodName === "setStorageSync") { store[methodArgs[0]] = methodArgs[1]; } if (methodName === "getStorageSync") { // 并不会真正执行(wx|my|tt|..).getStorageSync return store[methodArgs[0]]; } }, after(methodName) { console.log(`after methodName=${methodName}`); },});MpKit.Api.setStorageSync("name", "Tom");const name = MpKit.Api.getStorageSync("name");console.log(name === store.name);// 输入:before methodName=setStorageSync// 输入:after methodName=setStorageSync// 输入:before methodName=getStorageSync// 输入:true
结语
心愿MpKit能够为你带来便捷欢快的开发体验,祝大家工作顺心,家庭美满,加油!
可关注作者的其余开源我的项目:
- 小松鼠:实用于JavaScript平台的数据上报工具
- 小程序调试辅助工具:小程序控制台调试辅助工具