修复方法如下:
修改uni统计源代码(修改后的uni-stat见附件)
打开HBuilderX安装目录,找到 HBuilderX安装目录\plugins\uniapp-cli\node_modules\@dcloudio\uni-stat\dist\index.js 这个文件。需注意package.json文件中version指定的版本,我的是uni-stat 2.0.0-32920211119001这个版本。
如果你的项目不是使用HBuilderX创建的,你可能需要修改 HBuilderX安装目录\plugins\uniapp-cli\node_modules\@dcloudio\vue-cli-plugin-uni\packages\uni-stat\dist 文件夹下的 uni-stat.cjs.js和uni-stat.es.js这两个文件,改造方法应该和下面的相同(因为我的项目没有用到这两个js代码,所以不清楚这两个代码在什么环境下起作用)。至于怎么查看用到的是哪个目录中的uni-stat,可以通过查看项目编译后的app-service.js文件,搜索关键字“@dcloudio/uni-stat@”, 可以定位到uni-stat的版本
下面开工改造:
1. 修改getRoute方法
const getRoute = () => {
var pages = getCurrentPages();
var page = pages[pages.length - 1];
if (!page) return ''
// preloadPage的nvue页面$vm为undefined
let _self = page.$vm;
// 为preloadPage的nvue页面尝试从$page获取route属性
let _page = page.$page;
if (getPlatformName() === 'bd' && _self) {
return _self.$mp && _self.$mp.page.is;
} else if (_self){
return (_self.$scope && _self.$scope.route) || (_self.$mp && _self.$mp.page.route);
} else if (_page){
return _page.route
} else {
console.error('未找到路径')
return ''
}
};
2. 修改getPageRoute方法
const getPageRoute = (self) => {
var pages = getCurrentPages();
var page = pages[pages.length - 1];
if (!page) return ''
let _self = page.$vm;
let _page = page.$page;
let query = self._query;
let str = query && JSON.stringify(query) !== '{}' ? '?' + JSON.stringify(query) : '';
// clear
self._query = '';
if (getPlatformName() === 'bd' && _self) {
return _self.$mp && _self.$mp.page.is + str;
} else if (_self) {
return (_self.$scope && _self.$scope.route + str) || (_self.$mp && _self.$mp.page.route + str);
} else if (_page){
return _page.route
} else {
console.error('未找到路径')
return ''
}
};
3. 修改class Stat的load方法
class Stat extends Util {
....此处省略
load(options, self) {
if (!self.$scope && !self.$mp) {
// 如果是preloadPage nvue页面, 则这里拿不到对应的页面(也就是nvue页面不在CurrentPages页面栈中),需要使用route判断一下正确性
// 否则会把错误的页面赋值给self.$scope,进而引发Cannot read property '__call_hook' of undefined的错误
const page = getCurrentPages();
const route = page[page.length - 1].route;
if (self.$options && self.$options.route === route) {
self.$scope = page[page.length - 1];
}
}
this.self = self;
this._query = options;
}
}
4. 在lifecycle前定义函数tryinterceptShare
const tryinterceptShare = function(ctx) {
if (ctx.$scope && ctx.$scope.onShareAppMessage) {
let oldShareAppMessage = ctx.$scope.onShareAppMessage;
ctx.$scope.onShareAppMessage = function(options) {
stat.interceptShare(false);
return oldShareAppMessage.call(ctx, options)
};
}
}
5. 修改lifecycle 的 onLoad 方法
onLoad(options) {
stat.load(options, this);
// 重写分享,获取分享上报事件
tryinterceptShare(this)
// preloadPage nvue页面,$scope可能为undefined
if (!this.$scope) {
var $scope = undefined;
var self = this;
Object.defineProperty(this, '$scope', {
get() {
return $scope
},
set(val) {
$scope = val;
setTimeout(()=>{
tryinterceptShare(self)
}, 0)
}
})
}
}
6. try catch lifecycle 中的所有方法,防止uni-stat统计出错引发应用异常
在 lifecycle定义后,添加如下代码
const ERROR_PREFIX = 'uni-stat@' + STAT_VERSION;
// 把lifecycle的方法全都try catch起来,防止出什么错导致应用页面打不开
const tryfunc = function(func, name) {
return function() {
try {
let args = [].slice.call(arguments);
func.apply(this, args)
} catch(e) {
console.error(ERROR_PREFIX + ' error in function '+ name);
console.error(JSON.stringify(e))
}
}
}
Object.keys(lifecycle).forEach(k=>{
lifecycle[k] = tryfunc(lifecycle[k], k)
})
至此改造完毕。因为我也不知道这么修改会不会在其它平台出现错误,所以大家还是在修改后调试一下再发布,不过目前我自己在安卓机子上测试没发现问题。