gio-miniprogram-sdk 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +4 -0
- package/.eslintignore +11 -0
- package/.eslintrc.js +120 -0
- package/.github/workflows/greetings.yml +13 -0
- package/.github/workflows/publish.yml +43 -0
- package/.prettierignore +19 -0
- package/.prettierrc.js +9 -0
- package/LICENSE +674 -0
- package/README.md +103 -0
- package/babel.config.js +6 -0
- package/bin/build/all.sh +38 -0
- package/bin/build/full.sh +32 -0
- package/bin/build/jd.sh +14 -0
- package/bin/build/ks.sh +14 -0
- package/bin/build/my.sh +14 -0
- package/bin/build/plugins.sh +5 -0
- package/bin/build/qq.sh +14 -0
- package/bin/build/quickapp.sh +14 -0
- package/bin/build/swan.sh +14 -0
- package/bin/build/taro.sh +18 -0
- package/bin/build/tb.sh +18 -0
- package/bin/build/tt.sh +14 -0
- package/bin/build/uniapp.sh +18 -0
- package/bin/build/wx.sh +14 -0
- package/dist/alipay.js +1 -0
- package/dist/baidu.js +1 -0
- package/dist/bytedance.js +1 -0
- package/dist/jingdong.js +1 -0
- package/dist/kuaishou.js +1 -0
- package/dist/plugins/gioABTest.js +1 -0
- package/dist/plugins/gioCompress.js +1 -0
- package/dist/plugins/gioEventAutoTracking.js +1 -0
- package/dist/plugins/gioImpressionTracking.js +1 -0
- package/dist/qq.js +1 -0
- package/dist/quickapp.js +1 -0
- package/dist/taobao.js +1 -0
- package/dist/taro.js +1 -0
- package/dist/uniapp.js +1 -0
- package/dist/wechat.js +1 -0
- package/package.json +144 -0
- package/rollup.config.js +92 -0
- package/rollup.plugins.config.js +76 -0
- package/src/constants/config.ts +136 -0
- package/src/constants/regex.ts +4 -0
- package/src/core/dataStore/eventContextBuilder.ts +70 -0
- package/src/core/dataStore/eventHooks/appEffects.ts +220 -0
- package/src/core/dataStore/eventHooks/index.ts +403 -0
- package/src/core/dataStore/eventHooks/minipPage.ts +132 -0
- package/src/core/dataStore/eventHooks/pageEffects.ts +168 -0
- package/src/core/dataStore/index.ts +543 -0
- package/src/core/growingIO.ts +433 -0
- package/src/core/plugins.ts +152 -0
- package/src/core/uploader.ts +219 -0
- package/src/core/userStore.ts +203 -0
- package/src/index.ts +68 -0
- package/src/minipInstance/base.ts +287 -0
- package/src/minipInstance/framework.ts +22 -0
- package/src/minipInstance/jd.ts +11 -0
- package/src/minipInstance/ks.ts +12 -0
- package/src/minipInstance/my.ts +53 -0
- package/src/minipInstance/qq.ts +12 -0
- package/src/minipInstance/quickapp.ts +230 -0
- package/src/minipInstance/swan.ts +20 -0
- package/src/minipInstance/tb.ts +53 -0
- package/src/minipInstance/tt.ts +12 -0
- package/src/minipInstance/wx.ts +12 -0
- package/src/platformConfig/base.ts +86 -0
- package/src/platformConfig/framework.ts +29 -0
- package/src/platformConfig/jd.ts +11 -0
- package/src/platformConfig/ks.ts +11 -0
- package/src/platformConfig/my.ts +15 -0
- package/src/platformConfig/qq.ts +13 -0
- package/src/platformConfig/quickapp.ts +48 -0
- package/src/platformConfig/swan.ts +11 -0
- package/src/platformConfig/tb.ts +29 -0
- package/src/platformConfig/tt.ts +11 -0
- package/src/platformConfig/wx.ts +20 -0
- package/src/pluginsOut/README.md +50 -0
- package/src/pluginsOut/frameworks/gioTaobaoAdapter.ts +195 -0
- package/src/pluginsOut/frameworks/gioTaroAdapter.ts +358 -0
- package/src/pluginsOut/frameworks/gioUniAppAdapter.ts +220 -0
- package/src/pluginsOut/ordinary/gioABTest.ts +254 -0
- package/src/pluginsOut/ordinary/gioCompress.ts +251 -0
- package/src/pluginsOut/ordinary/gioCustomTracking.ts +71 -0
- package/src/pluginsOut/ordinary/gioEventAutoTracking.ts +164 -0
- package/src/pluginsOut/ordinary/gioImpressionTracking.ts +298 -0
- package/src/types/base.ts +150 -0
- package/src/types/dataStore.ts +43 -0
- package/src/types/eventHooks.ts +141 -0
- package/src/types/gioCustomTracking.ts +9 -0
- package/src/types/gioEventTracking.ts +10 -0
- package/src/types/gioPerformance.ts +8 -0
- package/src/types/growingIO.ts +102 -0
- package/src/types/minipInstance.ts +126 -0
- package/src/types/platforms.ts +65 -0
- package/src/types/plugins.ts +24 -0
- package/src/types/uploader.ts +26 -0
- package/src/types/userStore.ts +22 -0
- package/src/utils/glodash.ts +423 -0
- package/src/utils/tools.ts +384 -0
- package/src/utils/verifiers.ts +76 -0
- package/tsconfig.json +32 -0
- package/typings/global.d.ts +92 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 名称:Taro代理插件
|
|
3
|
+
* 用途:用于重写Taro框架下的创建App/Page/Component方法实现代理Hook;并获取Taro框架下的自定义方法真实方法名获取。
|
|
4
|
+
* 注意:测试taro3react时,微信要用react18测试,支付宝要用react17测试。
|
|
5
|
+
*/
|
|
6
|
+
import { GrowingIOType } from '@@/types/growingIO';
|
|
7
|
+
|
|
8
|
+
let ut;
|
|
9
|
+
class GioTaroAdapter {
|
|
10
|
+
private taro: any;
|
|
11
|
+
private vue: any;
|
|
12
|
+
private exposedNames: any;
|
|
13
|
+
private taro3Listeners: any;
|
|
14
|
+
constructor(public growingIO: GrowingIOType) {
|
|
15
|
+
const { utils, vdsConfig, emitter, minipInstance, gioPlatform } =
|
|
16
|
+
this.growingIO;
|
|
17
|
+
ut = utils;
|
|
18
|
+
this.taro = vdsConfig?.taro;
|
|
19
|
+
this.vue = vdsConfig?.taroVue;
|
|
20
|
+
this.exposedNames = {};
|
|
21
|
+
this.taro3Listeners = {};
|
|
22
|
+
emitter.on('minipLifecycle', ({ event }) => {
|
|
23
|
+
if (event === 'Page onReadyEnd') {
|
|
24
|
+
if (this.taro && ut.isTaro3(this.taro)) {
|
|
25
|
+
// 提前存储页面节点信息用来获取dataset
|
|
26
|
+
this.growingIO.plugins?.gioTaroAdapter?.saveFullPage(document.body);
|
|
27
|
+
// taro3开发时要等onready才能取到节点,所以补充做一次曝光监听
|
|
28
|
+
if (!['swan', 'tt'].includes(gioPlatform)) {
|
|
29
|
+
setTimeout(
|
|
30
|
+
() =>
|
|
31
|
+
minipInstance.initImpression(minipInstance.getCurrentPage()),
|
|
32
|
+
0
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 主入口,版本判断
|
|
41
|
+
main = () => {
|
|
42
|
+
// 插件挂载时可能还没取到
|
|
43
|
+
if (!this.taro) {
|
|
44
|
+
this.taro = this.growingIO?.vdsConfig?.taro;
|
|
45
|
+
}
|
|
46
|
+
if (!this.vue) {
|
|
47
|
+
this.vue = this.growingIO?.vdsConfig?.taroVue;
|
|
48
|
+
}
|
|
49
|
+
const { Current, createComponent } = this.taro;
|
|
50
|
+
if (!Current && createComponent) {
|
|
51
|
+
this.proxyTaro2(this.taro);
|
|
52
|
+
} else if (Current && !createComponent) {
|
|
53
|
+
this.proxyTaro3(this.taro, this.vue);
|
|
54
|
+
} else {
|
|
55
|
+
ut.consoleText('未获取到Taro实例或不支持的Taro版本,请检查!', 'error');
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// 代理taro2
|
|
60
|
+
proxyTaro2 = (taro: any) => {
|
|
61
|
+
const self = this;
|
|
62
|
+
const originApp = taro.createApp;
|
|
63
|
+
const originComponent = taro.createComponent;
|
|
64
|
+
if (originApp) {
|
|
65
|
+
taro.createApp = function (AppClass) {
|
|
66
|
+
const AppRef = originApp(AppClass);
|
|
67
|
+
self.growingIO.dataStore.eventHooks.appOverriding(AppRef);
|
|
68
|
+
return AppRef;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (originComponent) {
|
|
72
|
+
taro.createComponent = function (ComponentClass, isPage) {
|
|
73
|
+
const ComponentRef = originComponent(ComponentClass, isPage);
|
|
74
|
+
const targetType = isPage ? 'page' : 'component';
|
|
75
|
+
// wx/qq的生命周期函数和自定义函数都在ComponentRef.methods中
|
|
76
|
+
// my/tt/jd的生命周期函数和自定义函数都在ComponentRef中
|
|
77
|
+
// swan的生命周期函数在ComponentRef.methods中,自定义函数在ComponentRef中
|
|
78
|
+
switch (self.growingIO.gioPlatform) {
|
|
79
|
+
case 'wx':
|
|
80
|
+
case 'qq':
|
|
81
|
+
self.growingIO.dataStore.eventHooks[`${targetType}Overriding`](
|
|
82
|
+
ComponentRef.methods
|
|
83
|
+
);
|
|
84
|
+
break;
|
|
85
|
+
case 'my':
|
|
86
|
+
case 'tt':
|
|
87
|
+
case 'jd':
|
|
88
|
+
self.growingIO.dataStore.eventHooks[`${targetType}Overriding`](
|
|
89
|
+
ComponentRef
|
|
90
|
+
);
|
|
91
|
+
break;
|
|
92
|
+
case 'swan':
|
|
93
|
+
self.growingIO.dataStore.eventHooks[`${targetType}Overriding`](
|
|
94
|
+
ComponentRef
|
|
95
|
+
);
|
|
96
|
+
self.growingIO.dataStore.eventHooks[`${targetType}Overriding`](
|
|
97
|
+
ComponentRef.methods
|
|
98
|
+
);
|
|
99
|
+
break;
|
|
100
|
+
default:
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
return ComponentRef;
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// 代理taro3
|
|
109
|
+
proxyTaro3 = (taro: any, vue?: any) => {
|
|
110
|
+
const { eventHooks } = this.growingIO.dataStore;
|
|
111
|
+
if (vue) {
|
|
112
|
+
console.log('Vue version: ', vue.version);
|
|
113
|
+
const mainVersion = Number.parseInt(
|
|
114
|
+
ut.head(ut.split(vue.version, '.')),
|
|
115
|
+
10
|
|
116
|
+
);
|
|
117
|
+
eventHooks.nativeGrowing();
|
|
118
|
+
if (mainVersion === 2) {
|
|
119
|
+
this.taro3Vue2Proxy(vue);
|
|
120
|
+
} else if (mainVersion === 3) {
|
|
121
|
+
this.taro3Vue3Proxy(vue);
|
|
122
|
+
} else {
|
|
123
|
+
ut.consoleText('不支持的Vue版本,请使用 Vue2 或 Vue3!', 'error');
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
eventHooks.nativeGrowing(['App']);
|
|
127
|
+
this.taro3ReactProxy(taro);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// taro3使用react的代理
|
|
132
|
+
taro3ReactProxy = (taroRuntime: any) => {
|
|
133
|
+
// @ts-ignore
|
|
134
|
+
const taroNode = document.__proto__.__proto__;
|
|
135
|
+
const self = this;
|
|
136
|
+
const { eventHooks } = this.growingIO.dataStore;
|
|
137
|
+
// 重写页面生命周期
|
|
138
|
+
const originCall = taroRuntime?.hooks?.call;
|
|
139
|
+
if (originCall) {
|
|
140
|
+
self.defineProperty(taroRuntime.hooks, 'call', function (...args) {
|
|
141
|
+
const argsArray = Array.from(args);
|
|
142
|
+
let result = originCall.apply(this, argsArray);
|
|
143
|
+
if (argsArray[0] === 'modifyPageObject') {
|
|
144
|
+
const pageObject = argsArray[1];
|
|
145
|
+
ut.keys(pageObject).forEach((k) => {
|
|
146
|
+
if (eventHooks.pageHandlers.includes(k)) {
|
|
147
|
+
pageObject[k] = eventHooks.lifeFcEffects(
|
|
148
|
+
k,
|
|
149
|
+
ut.isFunction(pageObject[k]) ? pageObject[k] : () => {},
|
|
150
|
+
'Page'
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
});
|
|
157
|
+
} else {
|
|
158
|
+
ut.consoleText('Taro3实例获取失败,请检查初始化逻辑!', 'warn');
|
|
159
|
+
eventHooks.nativeGrowing(['Page']);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 重写页面自定义方法
|
|
163
|
+
const proxyAddEventListener = (addEventListener) => {
|
|
164
|
+
return function (...args) {
|
|
165
|
+
const originHandler = args[1];
|
|
166
|
+
const handler = self.growingIO.dataStore.eventHooks.customFcEffects(
|
|
167
|
+
originHandler.name,
|
|
168
|
+
originHandler
|
|
169
|
+
);
|
|
170
|
+
self.taro3Listeners[originHandler.name] = handler;
|
|
171
|
+
return addEventListener.apply(this, [args[0], handler, args[2]]);
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
const proxyRemoveEventListener = (removeEventListener) => {
|
|
175
|
+
return function (...args) {
|
|
176
|
+
return removeEventListener.apply(this, [
|
|
177
|
+
args[0],
|
|
178
|
+
self.taro3Listeners[args[1].name]
|
|
179
|
+
]);
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
self.defineProperty(
|
|
183
|
+
taroNode,
|
|
184
|
+
'addEventListener',
|
|
185
|
+
proxyAddEventListener(taroNode.addEventListener)
|
|
186
|
+
);
|
|
187
|
+
self.defineProperty(
|
|
188
|
+
taroNode,
|
|
189
|
+
'removeEventListener',
|
|
190
|
+
proxyRemoveEventListener(taroNode.removeEventListener)
|
|
191
|
+
);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// taro3使用vue2的代理
|
|
195
|
+
taro3Vue2Proxy = (vue: any) => {
|
|
196
|
+
const self = this;
|
|
197
|
+
vue.mixin({
|
|
198
|
+
beforeMount: function () {
|
|
199
|
+
// 对传统methods写法的普通事件hook
|
|
200
|
+
const methods = this.$options.methods;
|
|
201
|
+
ut.keys(methods).forEach((k: string) => {
|
|
202
|
+
self.defineProperty(this, k, self.proxyMethods(k, this[k]));
|
|
203
|
+
});
|
|
204
|
+
// 对setup中定义的事件hook
|
|
205
|
+
const setupState = this._setupState;
|
|
206
|
+
if (!ut.isEmpty(setupState)) {
|
|
207
|
+
ut.keys(setupState).forEach((k: string) => {
|
|
208
|
+
self.defineProperty(
|
|
209
|
+
setupState,
|
|
210
|
+
k,
|
|
211
|
+
self.proxyMethods(k, setupState[k])
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// taro3使用vue3的代理
|
|
220
|
+
taro3Vue3Proxy = (vue: any) => {
|
|
221
|
+
const { eventHooks } = this.growingIO.dataStore;
|
|
222
|
+
const self = this;
|
|
223
|
+
vue.mixin({
|
|
224
|
+
beforeMount: function () {
|
|
225
|
+
// hook逻辑
|
|
226
|
+
const transferMethods = (o) =>
|
|
227
|
+
ut.keys(o).forEach((k: string) => {
|
|
228
|
+
self.defineProperty(this, k, self.proxyMethods(k, this[k]));
|
|
229
|
+
});
|
|
230
|
+
// 对传统methods写法的普通事件hook
|
|
231
|
+
const methods = this.$options.methods;
|
|
232
|
+
if (methods) {
|
|
233
|
+
transferMethods(methods);
|
|
234
|
+
}
|
|
235
|
+
// 对setup中定义的事件hook
|
|
236
|
+
const setupState = this?._?.setupState;
|
|
237
|
+
if (setupState) {
|
|
238
|
+
transferMethods(setupState);
|
|
239
|
+
}
|
|
240
|
+
// 使用emit监听组件点击事件的处理
|
|
241
|
+
const tEmit = this._.emit;
|
|
242
|
+
this._.emit = function (...args) {
|
|
243
|
+
(() =>
|
|
244
|
+
eventHooks.customFcEffects(args[0], () => {}).apply(this, args))();
|
|
245
|
+
return tEmit.apply(this, args);
|
|
246
|
+
};
|
|
247
|
+
},
|
|
248
|
+
mounted() {
|
|
249
|
+
// this._.vnode.el要在mounted中才能获取到
|
|
250
|
+
// 对<script setup>写法的事件hook,编译时会匿名,需要配合expose
|
|
251
|
+
const exposed = this?._?.exposed ?? {};
|
|
252
|
+
self.exposedNames = {};
|
|
253
|
+
if (!ut.isEmpty(exposed)) {
|
|
254
|
+
const nExposed = {};
|
|
255
|
+
// 保存exposed供<script setup>写法的事件查找真实的事件名
|
|
256
|
+
ut.keys(exposed).forEach((k) => {
|
|
257
|
+
if (ut.isFunction(exposed[k])) {
|
|
258
|
+
self.exposedNames[exposed[k].name] = k;
|
|
259
|
+
nExposed[exposed[k].name] = exposed[k];
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
// 只能取到编译后的名称,需要从exposedNames中映射
|
|
263
|
+
self.traverseHook(this._.vnode.el, 0);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// 重写方法
|
|
270
|
+
proxyMethods = (name: string, method: any) => {
|
|
271
|
+
const { eventHooks } = this.growingIO.dataStore;
|
|
272
|
+
if (ut.isFunction(method) && !eventHooks.pageHandlers.includes(name)) {
|
|
273
|
+
return function (...args) {
|
|
274
|
+
return eventHooks.customFcEffects(name, method).apply(this, args);
|
|
275
|
+
};
|
|
276
|
+
} else {
|
|
277
|
+
return method;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// 重新定义属性
|
|
282
|
+
defineProperty = (target, key, value) => {
|
|
283
|
+
Object.defineProperty(target, key, {
|
|
284
|
+
writable: true,
|
|
285
|
+
enumerable: true,
|
|
286
|
+
configurable: true,
|
|
287
|
+
value
|
|
288
|
+
});
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
// 遍历hook节点方法
|
|
292
|
+
traverseHook = (node: any, i: number) => {
|
|
293
|
+
this.veiAutoEventHook(node, i);
|
|
294
|
+
if (node.childNodes && ut.isArray(node.childNodes)) {
|
|
295
|
+
node.childNodes.forEach((n, i) => {
|
|
296
|
+
this.traverseHook(n, i);
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
// vue3中查找并重写节点vei对象中的自定义事件
|
|
302
|
+
veiAutoEventHook = (node: any, i: number) => {
|
|
303
|
+
const { eventHooks } = this.growingIO.dataStore;
|
|
304
|
+
if (node._vei) {
|
|
305
|
+
ut.keys(node._vei).forEach((k) => {
|
|
306
|
+
if (eventHooks.actionEventTypes.includes(k)) {
|
|
307
|
+
const func = node._vei[k].value;
|
|
308
|
+
let funcName;
|
|
309
|
+
if (
|
|
310
|
+
(ut.startsWith(`${func}`, 'function ($event)') ||
|
|
311
|
+
ut.startsWith(`${func}`, 'function($event)')) &&
|
|
312
|
+
/return \$setup.[\w]+\(/.test(`${func}`)
|
|
313
|
+
) {
|
|
314
|
+
// 进入该判断说明客户对方法进行了传参,导致框架编译为匿名函数,此时需要我们处理一下拿到真实的方法名
|
|
315
|
+
const s = `${func}`.match(/return \$setup.[\w]+\(/)[0];
|
|
316
|
+
if (s) {
|
|
317
|
+
funcName = s.replace('return $setup.', '').replace('(', '');
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// 没有获取到方法的情况下,通过exposedNames中存的值尝试匹配,不一定准,因为对象是无序的
|
|
321
|
+
if (!funcName) {
|
|
322
|
+
funcName = `${ut.keys(this.exposedNames)[i]}#${
|
|
323
|
+
Object.values(this.exposedNames)[i]
|
|
324
|
+
}`;
|
|
325
|
+
}
|
|
326
|
+
this.defineProperty(
|
|
327
|
+
node._vei[k],
|
|
328
|
+
'value',
|
|
329
|
+
this.proxyMethods(
|
|
330
|
+
this.exposedNames[func.name] || func.name || funcName,
|
|
331
|
+
func
|
|
332
|
+
)
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// taro3中存储当前页面完整的数据以获取曝光事件中的dataset
|
|
340
|
+
// https://taro-docs.jd.com/taro/docs/react-overall#dataset
|
|
341
|
+
saveFullPage = (body: any) => {
|
|
342
|
+
const pageTraverse = (tEle: any) => {
|
|
343
|
+
tEle.forEach((cEle: any) => {
|
|
344
|
+
if (cEle?.ctx?.route) {
|
|
345
|
+
if (!this.growingIO.taro3VMs) {
|
|
346
|
+
this.growingIO.taro3VMs = {};
|
|
347
|
+
}
|
|
348
|
+
this.growingIO.taro3VMs[cEle.ctx.route] = cEle;
|
|
349
|
+
} else if (ut.isArray(cEle.childNodes)) {
|
|
350
|
+
pageTraverse(cEle.childNodes);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
pageTraverse(body.childNodes);
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export default { name: 'gioTaroAdapter', method: GioTaroAdapter };
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 名称:uniapp代理插件
|
|
3
|
+
* 用途:用于重写uniapp框架下的创建App/Page/Component方法实现代理Hook;并获取uniapp框架下的自定义方法真实方法名获取。
|
|
4
|
+
*/
|
|
5
|
+
import { GrowingIOType } from '@@/types/growingIO';
|
|
6
|
+
|
|
7
|
+
const ONCE = '~';
|
|
8
|
+
const CUSTOM = '^';
|
|
9
|
+
let ut;
|
|
10
|
+
class GioUniAppAdapter {
|
|
11
|
+
private uniVue: any;
|
|
12
|
+
private exposedNames: any;
|
|
13
|
+
constructor(public growingIO: GrowingIOType) {
|
|
14
|
+
ut = this.growingIO.utils;
|
|
15
|
+
this.uniVue = this.growingIO?.vdsConfig?.uniVue;
|
|
16
|
+
this.exposedNames = {};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// 主入口,版本判断
|
|
20
|
+
main = () => {
|
|
21
|
+
// 插件挂载时可能还没取到
|
|
22
|
+
if (!this.uniVue) {
|
|
23
|
+
this.uniVue = this.growingIO?.vdsConfig?.uniVue;
|
|
24
|
+
}
|
|
25
|
+
console.log('Vue version: ', this.uniVue.version);
|
|
26
|
+
if (this.growingIO?.vdsConfig?.subpackage) return false;
|
|
27
|
+
const mainVersion = Number.parseInt(
|
|
28
|
+
ut.head(ut.split(this.uniVue.version, '.')),
|
|
29
|
+
10
|
|
30
|
+
);
|
|
31
|
+
switch (mainVersion) {
|
|
32
|
+
case 2:
|
|
33
|
+
this.uniVue2Proxy(this.uniVue);
|
|
34
|
+
break;
|
|
35
|
+
case 3:
|
|
36
|
+
this.uniVue3Proxy(this.uniVue);
|
|
37
|
+
break;
|
|
38
|
+
default:
|
|
39
|
+
ut.consoleText('不支持的Vue版本,请使用 Vue2 或 Vue3!', 'error');
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// 获取自定义真实的方法名
|
|
45
|
+
getHandlerName = (eventName: string, e: any) => {
|
|
46
|
+
if (eventName === '__e') {
|
|
47
|
+
let methodName = '__e';
|
|
48
|
+
const { type, currentTarget, target } = e;
|
|
49
|
+
const dataSet = currentTarget?.dataset || target?.dataset;
|
|
50
|
+
if (dataSet) {
|
|
51
|
+
const eventOpts = dataSet.eventOpts || dataSet['event-opts'];
|
|
52
|
+
const matchedEventType = (eventType: string, optType: string) =>
|
|
53
|
+
eventType === optType ||
|
|
54
|
+
(optType === 'regionchange' &&
|
|
55
|
+
(eventType === 'begin' || eventType === 'end'));
|
|
56
|
+
if (eventOpts) {
|
|
57
|
+
eventOpts.forEach((o: any[]) => {
|
|
58
|
+
let eType = ut.head(o);
|
|
59
|
+
const isCustom = eType.charAt(0) === CUSTOM;
|
|
60
|
+
eType = isCustom ? eType.slice(1) : type;
|
|
61
|
+
const isOnce = eType.charAt(0) === ONCE;
|
|
62
|
+
eType = isOnce ? eType.slice(1) : type;
|
|
63
|
+
const eArray = ut.last(o);
|
|
64
|
+
if (matchedEventType(type, eType)) {
|
|
65
|
+
methodName = ut.head(ut.head(eArray));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return methodName;
|
|
71
|
+
} else if (!ut.isEmpty(this.exposedNames)) {
|
|
72
|
+
return this.exposedNames[eventName] ?? eventName;
|
|
73
|
+
} else {
|
|
74
|
+
return eventName;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// vue2代理
|
|
79
|
+
uniVue2Proxy = (vue: any) => {
|
|
80
|
+
const {
|
|
81
|
+
dataStore: { eventHooks },
|
|
82
|
+
minipInstance
|
|
83
|
+
} = this.growingIO;
|
|
84
|
+
eventHooks.nativeGrowing(['App', 'Component', 'Behavior']);
|
|
85
|
+
/**
|
|
86
|
+
* 注意:在字节小程序中,uniapp的表现会不一致,beforeMount会晚于小程序onShow执行;
|
|
87
|
+
* 页面中如果没有onShow方法的定义,会无法触发SDK的onShow进而没有Page事件,让客户在页面中补一个空的onShow即可。
|
|
88
|
+
*/
|
|
89
|
+
vue.mixin({
|
|
90
|
+
beforeMount() {
|
|
91
|
+
if (this.mpType === 'page') {
|
|
92
|
+
eventHooks.pageOverriding(this.$scope);
|
|
93
|
+
// 需要单独处理getPageTitle,防止在vue中onShow里拿的是错误的
|
|
94
|
+
const self = this;
|
|
95
|
+
const originGetter = minipInstance.getPageTitle;
|
|
96
|
+
minipInstance.getPageTitle = function (...args) {
|
|
97
|
+
const cPath = self.$scope.route;
|
|
98
|
+
const vmTitle =
|
|
99
|
+
cPath === eventHooks.currentPage.path
|
|
100
|
+
? self.$scope?.$vm?._data?.gioPageTitle
|
|
101
|
+
: undefined;
|
|
102
|
+
return vmTitle ? vmTitle : originGetter.apply(this, args);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// vue3代理
|
|
110
|
+
uniVue3Proxy = (vue: any) => {
|
|
111
|
+
const {
|
|
112
|
+
dataStore: { eventHooks },
|
|
113
|
+
minipInstance
|
|
114
|
+
} = this.growingIO;
|
|
115
|
+
eventHooks.nativeGrowing();
|
|
116
|
+
// 需要单独处理getPageTitle,防止在vue中onShow里拿的是错误的
|
|
117
|
+
const originGetter = minipInstance.getPageTitle;
|
|
118
|
+
minipInstance.getPageTitle = function (...args) {
|
|
119
|
+
let pageInstance = !ut.isEmpty(args[0])
|
|
120
|
+
? args[0]
|
|
121
|
+
: minipInstance.getCurrentPage();
|
|
122
|
+
if (
|
|
123
|
+
pageInstance?.$vm?.mpType === 'page' &&
|
|
124
|
+
pageInstance?.$vm?.__route__
|
|
125
|
+
) {
|
|
126
|
+
const vmTitle = pageInstance?.$vm?.gioPageTitle;
|
|
127
|
+
return vmTitle ? vmTitle : originGetter.apply(this, args);
|
|
128
|
+
} else {
|
|
129
|
+
return originGetter.apply(this, args);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
const self = this;
|
|
133
|
+
vue.mixin({
|
|
134
|
+
beforeMount() {
|
|
135
|
+
const proxyMethods = (name: string, method: any) => {
|
|
136
|
+
if (
|
|
137
|
+
ut.isFunction(method) &&
|
|
138
|
+
!eventHooks.pageHandlers.includes(name)
|
|
139
|
+
) {
|
|
140
|
+
return function (...args) {
|
|
141
|
+
return eventHooks.customFcEffects(name, method).apply(this, args);
|
|
142
|
+
};
|
|
143
|
+
} else {
|
|
144
|
+
return method;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
// hook逻辑
|
|
148
|
+
const transferMethods = (origin, target) =>
|
|
149
|
+
ut.keys(origin).forEach((k: string) => {
|
|
150
|
+
if (ut.isFunction(target[k])) {
|
|
151
|
+
Object.defineProperty(target, k, {
|
|
152
|
+
value: proxyMethods(k, target[k]),
|
|
153
|
+
configurable: true,
|
|
154
|
+
writable: true
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
// 对传统普通事件hook,如果methods存在,说明有传统写法定义自定义的方法
|
|
159
|
+
const methods = this.$options.methods;
|
|
160
|
+
if (methods) {
|
|
161
|
+
transferMethods(methods, this);
|
|
162
|
+
}
|
|
163
|
+
// 对setup中定义的事件hook
|
|
164
|
+
const setupState = this?._?.setupState;
|
|
165
|
+
if (setupState) {
|
|
166
|
+
transferMethods(setupState, setupState);
|
|
167
|
+
}
|
|
168
|
+
// 对<script setup>写法的事件hook,编译时会匿名,需要配合expose
|
|
169
|
+
const exposed = this?._?.exposed;
|
|
170
|
+
self.exposedNames = {};
|
|
171
|
+
if (exposed) {
|
|
172
|
+
// 保存exposed供<script setup>写法的事件查找真实的事件名
|
|
173
|
+
const exposedKeys = ut.keys(exposed);
|
|
174
|
+
exposedKeys.forEach((k) => {
|
|
175
|
+
if (ut.isFunction(exposed[k])) {
|
|
176
|
+
self.exposedNames[exposed[k].name] = k;
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
// 只能取到编译后的名称,需要从exposedNames中映射
|
|
180
|
+
const methodKeys = ut
|
|
181
|
+
.keys(this.$scope)
|
|
182
|
+
.filter(
|
|
183
|
+
(k) => /^e[0-9]+$/.test(k) && ut.isFunction(this.$scope[k])
|
|
184
|
+
);
|
|
185
|
+
methodKeys.forEach((k, i) => {
|
|
186
|
+
const originFunc = this.$scope[k].value;
|
|
187
|
+
const customName = self.splicingMethodName(
|
|
188
|
+
originFunc.name,
|
|
189
|
+
k,
|
|
190
|
+
exposedKeys[i]
|
|
191
|
+
);
|
|
192
|
+
this.$scope[k].value = eventHooks.customFcEffects(
|
|
193
|
+
customName,
|
|
194
|
+
originFunc
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
// 使用emit监听组件事件hook
|
|
199
|
+
const tEmit = this._.emit;
|
|
200
|
+
this._.emit = function (...args) {
|
|
201
|
+
(() =>
|
|
202
|
+
eventHooks.customFcEffects(args[0], () => {}).apply(this, args))();
|
|
203
|
+
return tEmit.apply(this, args);
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
splicingMethodName = (originName, traversalName, exposedName) => {
|
|
210
|
+
if (originName) {
|
|
211
|
+
return originName;
|
|
212
|
+
} else if (exposedName) {
|
|
213
|
+
return `${traversalName}#${exposedName}`;
|
|
214
|
+
} else {
|
|
215
|
+
return traversalName;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export default { name: 'gioUniAppAdapter', method: GioUniAppAdapter };
|