初衷
- 最主要还是为了解决代码重复问题。
有时候在项目中可能每个 Page 页的 options 属性中都会有一些重复的代码
例如:
Page({
options: {
multipleSlots: true,
pureDataPattern: /^_/
},
behaviors: [behavior], //引入computed和watch
data:{
xxx:'xxx'
}
computed:{
yyy(data){
return 'xxx'
}
},
onLoad() {
this.globalData = getApp().globalData; //为页面实例上加全局数据
this.storeBindings = createStoreBindings(this, {
store: store,
fields: ["theme"] //为每个page绑定主题theme
});
},
onUnload() {
this.storeBindings.destroyStoreBindings();
}
});
- 其次是为了满足对类型检查的依赖(js 开发对记忆的要求太高了)
如上面的例子,在实例 this 上加入了 globalData,并在 this.data 上加入了计算属性 yyy 和 store 中的 theme 属性
但由于历史问题,小程序的 Page 的类型声明中不会对 computed 中 data 参数指定类型,这导致了你在计算属性中通过 data.
等不到任何类型提示,在使用 this 的时候页不会出现 computed 中的属性提示,以及在使用 this.的收无法显示自己在 this 中
加入的自定义属性类型。对于我这种记忆不好的来说,有些帮助。
-
加入一些官方没有及时更新的类型
比如 this 上没有 storeBindings 属性 (好多,记不住,刚好想起这个)
-
以后更方便注入更多数据
可能当前项目中每个 Page 页中代码较少,即使重复一些也无所谓,但对以后的拓展还是很有帮助的。一劳永逸
完成效果
实现逻辑
- 最开是我使用的自定义的一个 mixinPage 函数 通过保持原 Page 再重新定义 Page 的方式,把要复用的代码合并为一个 options 对象
这种方式其实很不错,更改混入配置的时候很方便。只是因为混入类型实在头疼,最好是修改 miniprogram-api-typings 中的原声明文件,但在模块更新或者更换电脑重新 clone 项目时候,需要重新修改声明文件,有点小烦。
const mixinPage: WechatMiniprogram.Page.Constructor = function (mixinOpt) {
//保存原始 Page
const originPage = Page;
//重新定义Page
Page = function (curOpt) {
//把建立Page的配置对象和混入的配置对象合并
const newOpt = mergeOpt(curOpt, mixinOpt);
//返回 原始Page 参数为混合完毕的新配置对象
return originPage(newOpt);
};
};
mixinPage({
options: {
//为每个页面混入options配置,避免代码重复
multipleSlots: true,
pureDataPattern: /^_/,
},
behaviors: [behavior], //为每个页面引入computed和watch
});
- 现在用的方法是建立一个 HPage 函数,代替 Page 函数,创建页面。更改混入配置比上面稍微麻烦一些。不过这种方式方便避免修改模块中的原声明文件,类似官方 behavior 的写法。
//要混入this中的类型
type PageMixinThis = { globalData: Custom.IGlobalData } & {
storeBindings?: { destroyStoreBindings: AnyFunction };
} & { data: { theme: Custom.TTheme } };
//仿造官方写法实例类型
type HInstance<
TData extends Record<string, any>,
TCustom extends Record<string, any>,
TComputed extends Record<string, any>
> = WechatMiniprogram.OptionalInterface<WechatMiniprogram.Page.ILifetime> &
WechatMiniprogram.Page.InstanceProperties &
WechatMiniprogram.Page.InstanceMethods<TData> &
WechatMiniprogram.Page.Data<
TData & { [K in keyof TComputed]: ReturnType<TComputed[K]> }
> &
TCustom &
PageMixinThis;
type HOptions<TData, TCustom, TComputed> = (TCustom &
Partial<WechatMiniprogram.Page.Data<TData>> &
Partial<WechatMiniprogram.Page.ILifetime> & {
options?: WechatMiniprogram.Component.ComponentOptions;
}) & {
//-------------注入官方options中未写的类型-------
behaviors?: string[];
watch?: Record<string, AnyFunction>;
computed?: TComputed;
//-------------注入完毕---------
} & ThisType<HInstance<TData, TCustom, TComputed>>;
export function HPage<
TData extends Record<string, any>,
TCustom extends Record<string, any>,
TComputed extends Record<string, (data: TData) => any>//官方没有这个计算属性类型
>(options: HOptions<TData, TCustom, TComputed>): void {
// 注入options配置
options.options = options.options || {};
options.options.multipleSlots = true;
options.options.pureDataPattern = /^_/;
//注入computed和watch
options.behaviors = options.behaviors || [];
options.behaviors.unshift(behavior);
//this注入globalData
const originOnload = options.onLoad;
options.onLoad = function (this: any) {
this.globalData = globalData;
originOnload?.call(this);
if (!this.storeBindings) {
//没有绑定任何store的情况下,绑定theme
this.storeBindings = createStoreBindings(this, {
store: store,
fields: ["theme"], //为每个page绑定主题theme
});
}
};
//注入destroyStoreBindings
const originOnUnload = options.onUnload;
options.onUnload = function () {
this.storeBindings.destroyStoreBindings();
originOnUnload?.call(this);
};
return Page(options);
}
总结
之所以遇到这些问题,根本原因是因为小程序框架不是原生ts,设计初衷和还有一些历史因素造成的(比如page页面属性层级造成不敢在添加属性,page页面鸡肋,component组件又缺少一些page页面功能,无法完全取代Page页)。总之和其他框架比起来,不完善的地方太多了。期待小程序做一个ts重写版。把计算属性,监控器,等如vue react成熟框架思想加进去。腾讯的一小步,程序员的一大步。
在此也希望大神给与指导,如思想或代码有错误,请不吝赐教。我会回复的。
凌晨3点半,头发又掉了不少。