知行合一:基于JS Proxy和function.call为小程序提供全局数据管理和model数据层

334 阅读5分钟

写了这么多小程序,从刚开始的一无所知,到现在经过无数次重构使用TS搭建出小程序的基础架构,以及提供相关helper函数弥补小程序在功能上的不足,最终我提炼了不少解决方案,以前的文章也有提到过一些,但相对来说,还不是最优解,于是最近刚好抽得出一点时间来把方案整合一下,开源出来,回馈给社区。

学了(背诵)那么多JS高级技巧,但事实上(在面试过一些前端开发者之后),很少有人能去利用这些高级技巧去产出一些东西,这就是传统教育的“考试模式”的最大弊端:不会举一反X。

举一反三可能大多数人都会,比如说到js中call函数,你可能会联想到apply和bind,但这并没有意义,现在的面试体制根本无法触及到人的能力最核心的那一部分——创造力。

不要觉得只有大牛才有创造力,大牛也是从普通人成长起来的,创造力这东西,得人去挖掘,不然可能像大脑中的一些区域一样,一生都没有被点亮过,但它确实存在。

之前使用Object.defineOwnProperty写过一个数据双向绑定的小工具用于构建小程序的全局数据中心,去年年中的时候听说Vue3.0要用Proxy代替defineOwnProperty双向绑定这一块,于是去MDN上查了一下相关资料。

了解到基本上还是一样的用法,只不过表达的东西变了,性能也有了不错的提升,于是我就使用Proxy重构了我的moonjs中的数据中心模块。

后来一直在使用umi+dva写几个段商家后台管理系统,从dva上西区到了许多关于前端架构上的灵感,于是一直都想要写一个connect函数为小程序提供model数据逻辑层的能力,研究了一下前人的实现,觉得过于重,我想要的是一个足够纯粹的函数,用来提供相应的功能,于是结合一些JS的特性魔改了一下,重造了小程序版本的connect函数。

在重造的过程当中,只利用了JS的三个知识点。

一个是函数调用this传递,通过this.dispatch将小程序Page对象中的this传递到dispatch函数中去。

一个是function声明函数获取this,使用function对dispatch函数进行声明,就可以在函数内部获取到外部调用的this,详细请百度箭头函数和function声明的函数有何不同。

另一个是call/apply/bind,绑定this到指定方法,用于model内的方法获取Page对象的this,以便使用this.setData进行数据变更。

有兴趣的同学可以去看一下源代码,源代码很少,相信会对一些同学有所启发。

什么启发呢,就是我们为什么要学(背诵)那么多web相关的姿势,而不是需要用的时候再去做研究,事实上,我一直认为现在的传统教育体制和面试以及考核体制完全是本末倒置,是在压抑人的天性以及创造力,反而死记硬背的人会混的更好。

但死记硬背是没有后续的。什么是成长,成长就是能够不断地复用之前学过的知识,实践得到的经验,并在此基础上提升认知水平以及扩展解决问题的思路、提高解决问题的能力。

废话不多说,下面是关于moonjs的一些介绍。

moon

可能是世界上最小巧的小程序功能增强库. github.com/MatrixAge/m…

install

  1. npm i @matrixage/moon

  2. 微信开发者工具 -> 工具 -> 构建npm

usage

moon (全局状态管理)

  1. 在app.js中定义全局变量
import { moon } from '@matrixage/moon'

App({
	onLaunch () {
		moon(wx).define({
            		userinfo:{},
			loaded: false
		})
	}
})
  1. 在A页面修改已经定义的值
wx.$set({
      loaded:true
})
  1. 在B页面监听字段的变化
wx.$watch('loaded',(new_val,old_val)=>{
      this.setData({
            visible_ads: new_val
      })

      console.log(old_val)
})

connect (支持dva model)

页面的js文件 (index.js)

import { connect } from '@matrixage/moon'
import model from './model'

const page = {
	onLoad () {
		this.dispatch({
                  type: 'query',
                  payload:{
                        shop_id: 1
                  }
		})
	}
}

connect(page, model)

页面的model文件 (model.js)

import { Service_getContent, Service_getUserinfo } from './services'

export default {
	data: {
		list: [],
		userinfo: {},
		loading: false
	},

	effects: {
		async query ({ payload }) {
			const { result, success } = await Service_getContent(Object.assign(payload,{ type: 1 }))

			if (!success) return

			this.setData({
				list: result
			})
		},
		async getUserinfo () {
             const {result, success} = await Service_getUserinfo()
                  
            if (!success) return
                  
			this.setData({
				userinfo: result
			})
		}
	},

	reducers: {
		showLoading () {
			this.setData({ loading: true })
		},
		hideLoading () {
			this.setData({ loading: false })
		}
	}
}

model只支持三种对象:

  • data (同小程序data)
  • effects (异步方法,通常用于获取异步数据)
  • reducers (同步方法,通常用于控制模态框或是loading的显示隐藏)

在index.js中使用this.dispatch({type,payload})调用model.js中的函数,在model.js中直接使用this.setData({data})对数据进行变更。

index.html负责视图展现 (view),index.js负责页面逻辑 (controller),model.js负责数据逻辑 (model)。基于“0入侵”的原则,简化了dva的一些概念,尽可能地让经验较少的开发者也能体会到标准MVC编程的快乐。

modelExtend (扩展model)

import { modelExtend } from '@matrixage/modelExtend'
import model from 'path/to/public_model'

export default modelExtend(model,{
      ...
})

promisifyAll (Promise化wxapi )

  1. 在app.js onLaunch函数的第一行执行绑定
import { promisifyAll } from '@matrixage/moon'

App({
	onLaunch () {
		promisifyAll(wx, wx.$)
	}
})
  1. 在其他地方直接使用
try {
      await wx.$.requestPayment(data)
} catch ( e ) {
      wx.$messageInfo( '支付失败' )

      return
}

wx.$messageSuccess( '预约成功' )

//wx.$messageInfo和wx.$messageSuccess是项目中封装的方法

typescript使用

小程序使用typescript打包出来的文件有点问题,先暂时手动复制粘贴声明文件

declare namespace WechatMiniprogram {
      interface Wx {
            $: any
            $set: () => void
            $watch: () => void
            $getData: object
      }
}

interface IModel {
      data:object
      effects:object
      reducers:object
}

declare module '@matrixage/moon'{
      export const moon:(wx:any)=>void
      export const connect:(page:object,model:object)=>any
      export const modelExtend:(page_model:IModel,public_model:IModel)=>IModel
      export const promisifyAll:(wx:any,wx$:any)=>void
}

理念

moon将始终保持小而美,要保证使用moon不会对现有代码造成任何侵入式破坏.