背景
众所周知es6为我们带来了元操作界新一任大佬——Proxy
与Object.defineProperty相比的优点:
- 初始量少
- 数组可用
- 字段增删敏感
- 多种拦截
对于proxy还不是很了解的同学可以参考以下链接瞅瞅
懒惰的Promise
接下来我们找点栗子来实现下这个懒惰的Promise。
微信生态今时今日还是有很多人默默地用着回调,控制不好的话,很容易就会掉进地狱回调,也用不了真香async。
虽然现在也出了不少方案来实现,但是这次就让我们忘记它,用我们的方式去实现
我们主要目标是为这位不会化妆的handle打扮成一线潮流的promise
wx.showToast({
title: '成功',
success () {
console.log('success')
},
fail () {
console.log('fail')
}
})
// 这里把wx的api代理到wxq变量
wxq.showToast({title: '成功'})
.then(() => console.log('success'))
.catch(() => console.log('fail'))
.finally(() => console.log('finally'))
单个promise
基础一点的直接封装一个函数,然后每次使用就放一个api进去,但是使用的时候,没有美感,操作变形,多了一层壳
function wxPromise(fn, param){
return new Promise((resolve, reject) => {
const data = Object.assign({
success(res) {
resolve(res)
},
fail(res){
reject(res)
}
}, param)
fn(data);
})
}
wxPromise(wxq.showToast, {title: '成功'})
.then(() => console.log('success'))
.catch(() => console.log('fail'))
.finally(() => console.log('finally'))
Proxy方式
在写之前,我们假设下,如果下次再碰到类似的场景。换成了uni-app或者其他一些类似的框架,我们还可以继续复用部分代码吗?应该如何去设计这份代码呢
所以在下决定将代码分成3部分
- proxy生成函数:根据promise模板函数来代理
- promise模板函数:根据环境来补充
- 出口函数:屏蔽掉一些生成的细节操作
// proxy生成函数
function createProxyAll (obj, fn) {
return new Proxy(obj, {
get(target, key, receiver) {
// 操作receiver会循环
if (!target[key]) target[key] = fn(key)
return Reflect.get(target, key, receiver);
}
})
}
// promise模板函数
function wxPromise(key){
// 缓存留key生成一个promise闭包
return function (param) {
return new Promise((resolve, reject) => {
const data = Object.assign({
success(res) {
resolve(res)
},
fail(res){
reject(res)
}
}, param)
wx[key](data);
})
}
}
// 出口函数
function wxPromiseAll () {
return createProxyAll(wx, wxPromise)
}
优雅降级
好东西,或多或少都会不被世人理解。在下之前就用过这个方案,风和日丽的那天,运营急冲冲告诉我有位客户的手机打开是全空白的,26°气温下我微微出了几珠汗。
while(true){思考}.png
排查到是proxy的兼容问题。
- 那简单了,让客户换台手机(驳回)。
- 垫片库?proxy和Object.defineProperty这种元操作是无法垫片的,环境不支持就是不支持。这也就是vue2和vue3无法为低版本浏览器提供兼容的直接原因(×)
- 用defineProperty做降级√
function make (obj, fn) {
// 判断Proxy,是否需要降级
if (typeof Proxy === 'function') {
return createProxyAll(obj, fn)
} else {
return createdefinePropertyAll(obj, fn)
}
}
function createdefinePropertyAll (obj, fn) {
const _obj = {}
const result = {}
Object.keys(obj).forEach(key => {
Object.defineProperty(result, key, {
get() {
// 这里的判断和proxy一样,防止引用循环
if (!_obj[key]) _obj[key] = fn(key)
return _obj[key]
},
})
})
return result
}
defineProperty版本实现大同小异,而且从中可以看出defineProperty版本初始化的时候就要把所有的字段都直接代理,这也是使用proxy实现的vue3的性能比使用defineProperty实现的vue2好的原因。同样,为什么我会说这个是懒惰的Promise,只有当你用到的时候,才会去进行拦截操作更改成Promise,而不用一股脑的初始化
剧终
多想拿起手机,就看到你们的评论,比女神都好使