Open hereisfun opened 5 years ago
经排查,推测是mpvue源码的问题
下面的代码,表示patch的时候会调用一遍小程序的setData
function patch () {
corePatch.apply(this, arguments);
this.$updateDataToMP();
}
// install platform patch function
Vue$3.prototype.__patch__ = patch;
而vue在销毁组件vnode时,也是调用patch来销毁的:
// call the last hook...
vm._isDestroyed = true;
// invoke destroy hooks on current rendered tree
vm.__patch__(vm._vnode, null);
// fire destroyed hook
callHook(vm, 'destroyed');
也就是说,如果触发了组件的销毁,而原本该位置的组件节点仍然存在时,会错误的调用一次$updateDataToMP, 将旧数据塞回去。
页面中有A, B, 更新后变成只有C(A, B, C都是同一个组件,key值唯一)
A, B -> C
此时因为key不同,sameVnode的判定为false,在updateChildren时会首先生成C, 然后destroy A, B
也就是:
create C: this.setData({$root.0_0_0: C})
destroy A和B: this.setData({$root.0_0_0: A, $root.0_0_1: B})
因为0_0_0位置上的元素还在,所以旧数据就被重新更新上去了。
免改源码方法: 不使用唯一key。你可以不用key或者用索引作为key。这样updateChildren时就会走patchVnode分支,不用destroy组件节点。 尽管vue官方推荐使用唯一key,但不使用key时,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法来更新节点。另外,因为小程序没有DOM操作,节点的更新本质也是通过setData来做的,所以不用key应该也不会带来太多性能问题
改源码:
思路就是在destroy的时候不要调用updateDataToMP, 观察发现被销毁的vm会被标记为vm._isDestroyed = true
, 因此在updateDataToMP中加上条件判断即可:
function updateDataToMP () {
var page = getPage(this);
if (!page) {
return
}
// 对于被销毁的节点,不更新data
if (this._isDestroyed) {
return
}
var data = formatVmData(this);
diffData(this, data);
throttleSetData(page.setData.bind(page), data);
}
该修改已提PR: https://github.com/Meituan-Dianping/mpvue/pull/1643
[扼要问题描述]
mpvue 版本号:
[mpvue@2.0.5]
最小化复现代码:
[建议提供最小化可运行的代码:附件或文本代码]
index.vue
t-text.vue:
问题复现步骤:
观察到的表现: