商城笔记----自定义Page
发布于 3 年前 作者 daiguiying 3911 次浏览 来自 分享

初衷

  1. 最主要还是为了解决代码重复问题

有时候在项目中可能每个 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();
    }
});
  1. 其次是为了满足对类型检查的依赖(js 开发对记忆的要求太高了)

如上面的例子,在实例 this 上加入了 globalData,并在 this.data 上加入了计算属性 yyy 和 store 中的 theme 属性
但由于历史问题,小程序的 Page 的类型声明中不会对 computed 中 data 参数指定类型,这导致了你在计算属性中通过 data.
等不到任何类型提示,在使用 this 的时候页不会出现 computed 中的属性提示,以及在使用 this.的收无法显示自己在 this 中
加入的自定义属性类型。对于我这种记忆不好的来说,有些帮助。

  1. 加入一些官方没有及时更新的类型

    比如 this 上没有 storeBindings 属性 (好多,记不住,刚好想起这个)

  2. 以后更方便注入更多数据

可能当前项目中每个 Page 页中代码较少,即使重复一些也无所谓,但对以后的拓展还是很有帮助的。一劳永逸

完成效果

实现逻辑

  1. 最开是我使用的自定义的一个 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
});
  1. 现在用的方法是建立一个 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点半,头发又掉了不少。

回到顶部