Skip to content

Commit 2163f8c

Browse files
authored
Merge pull request #1241 from Meituan-Dianping/develop
mpvue 数据增量同步更新
2 parents 63700b2 + af88676 commit 2163f8c

File tree

9 files changed

+433
-18
lines changed

9 files changed

+433
-18
lines changed

packages/mpvue-template-compiler/build.js

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2810,10 +2810,13 @@ var observerState = {
28102810
* object's property keys into getter/setters that
28112811
* collect dependencies and dispatches updates.
28122812
*/
2813-
var Observer = function Observer (value) {
2813+
var Observer = function Observer (value, key) {
28142814
this.value = value;
28152815
this.dep = new Dep();
28162816
this.vmCount = 0;
2817+
if (key) {
2818+
this.key = key;
2819+
}
28172820
def(value, '__ob__', this);
28182821
if (Array.isArray(value)) {
28192822
var augment = hasProto
@@ -2876,7 +2879,7 @@ function copyAugment (target, src, keys) {
28762879
* returns the new observer if successfully observed,
28772880
* or the existing observer if the value already has one.
28782881
*/
2879-
function observe (value, asRootData) {
2882+
function observe (value, asRootData, key) {
28802883
if (!isObject(value)) {
28812884
return
28822885
}
@@ -2890,7 +2893,9 @@ function observe (value, asRootData) {
28902893
Object.isExtensible(value) &&
28912894
!value._isVue
28922895
) {
2893-
ob = new Observer(value);
2896+
ob = new Observer(value, key);
2897+
ob.__keyPath = ob.__keyPath ? ob.__keyPath : {};
2898+
ob.__keyPath[key] = true;
28942899
}
28952900
if (asRootData && ob) {
28962901
ob.vmCount++;
@@ -2915,11 +2920,13 @@ function defineReactive$$1 (
29152920
return
29162921
}
29172922

2923+
// TODO: 先试验标记一下 keyPath
2924+
29182925
// cater for pre-defined getter/setters
29192926
var getter = property && property.get;
29202927
var setter = property && property.set;
29212928

2922-
var childOb = !shallow && observe(val);
2929+
var childOb = !shallow && observe(val, undefined, key);
29232930
Object.defineProperty(obj, key, {
29242931
enumerable: true,
29252932
configurable: true,
@@ -2942,6 +2949,7 @@ function defineReactive$$1 (
29422949
if (newVal === value || (newVal !== newVal && value !== value)) {
29432950
return
29442951
}
2952+
29452953
/* eslint-enable no-self-compare */
29462954
if (process.env.NODE_ENV !== 'production' && customSetter) {
29472955
customSetter();
@@ -2951,8 +2959,10 @@ function defineReactive$$1 (
29512959
} else {
29522960
val = newVal;
29532961
}
2954-
childOb = !shallow && observe(newVal);
2962+
childOb = !shallow && observe(newVal, undefined, key);
29552963
dep.notify();
2964+
obj.__keyPath = obj.__keyPath ? obj.__keyPath : {};
2965+
obj.__keyPath[key] = true;
29562966
}
29572967
});
29582968
}
@@ -2985,6 +2995,9 @@ function set (target, key, val) {
29852995
return val
29862996
}
29872997
defineReactive$$1(ob.value, key, val);
2998+
// Vue.set 添加对象属性,渲染时候把val传给小程序渲染
2999+
target.__keyPath = target.__keyPath ? target.__keyPath : {};
3000+
target.__keyPath[key] = true;
29883001
ob.dep.notify();
29893002
return val
29903003
}

packages/mpvue/index.js

Lines changed: 202 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -763,10 +763,13 @@ var observerState = {
763763
* object's property keys into getter/setters that
764764
* collect dependencies and dispatches updates.
765765
*/
766-
var Observer = function Observer (value) {
766+
var Observer = function Observer (value, key) {
767767
this.value = value;
768768
this.dep = new Dep();
769769
this.vmCount = 0;
770+
if (key) {
771+
this.key = key;
772+
}
770773
def(value, '__ob__', this);
771774
if (Array.isArray(value)) {
772775
var augment = hasProto
@@ -829,7 +832,7 @@ function copyAugment (target, src, keys) {
829832
* returns the new observer if successfully observed,
830833
* or the existing observer if the value already has one.
831834
*/
832-
function observe (value, asRootData) {
835+
function observe (value, asRootData, key) {
833836
if (!isObject(value)) {
834837
return
835838
}
@@ -843,7 +846,9 @@ function observe (value, asRootData) {
843846
Object.isExtensible(value) &&
844847
!value._isVue
845848
) {
846-
ob = new Observer(value);
849+
ob = new Observer(value, key);
850+
ob.__keyPath = ob.__keyPath ? ob.__keyPath : {};
851+
ob.__keyPath[key] = true;
847852
}
848853
if (asRootData && ob) {
849854
ob.vmCount++;
@@ -868,11 +873,13 @@ function defineReactive$$1 (
868873
return
869874
}
870875

876+
// TODO: 先试验标记一下 keyPath
877+
871878
// cater for pre-defined getter/setters
872879
var getter = property && property.get;
873880
var setter = property && property.set;
874881

875-
var childOb = !shallow && observe(val);
882+
var childOb = !shallow && observe(val, undefined, key);
876883
Object.defineProperty(obj, key, {
877884
enumerable: true,
878885
configurable: true,
@@ -895,6 +902,7 @@ function defineReactive$$1 (
895902
if (newVal === value || (newVal !== newVal && value !== value)) {
896903
return
897904
}
905+
898906
/* eslint-enable no-self-compare */
899907
if ("production" !== 'production' && customSetter) {
900908
customSetter();
@@ -904,8 +912,10 @@ function defineReactive$$1 (
904912
} else {
905913
val = newVal;
906914
}
907-
childOb = !shallow && observe(newVal);
915+
childOb = !shallow && observe(newVal, undefined, key);
908916
dep.notify();
917+
obj.__keyPath = obj.__keyPath ? obj.__keyPath : {};
918+
obj.__keyPath[key] = true;
909919
}
910920
});
911921
}
@@ -938,6 +948,9 @@ function set (target, key, val) {
938948
return val
939949
}
940950
defineReactive$$1(ob.value, key, val);
951+
// Vue.set 添加对象属性,渲染时候把val传给小程序渲染
952+
target.__keyPath = target.__keyPath ? target.__keyPath : {};
953+
target.__keyPath[key] = true;
941954
ob.dep.notify();
942955
return val
943956
}
@@ -965,6 +978,9 @@ function del (target, key) {
965978
if (!ob) {
966979
return
967980
}
981+
target.__keyPath = target.__keyPath ? target.__keyPath : {};
982+
// Vue.del 删除对象属性,渲染时候把这个属性设置为undefined
983+
target.__keyPath[key] = 'del';
968984
ob.dep.notify();
969985
}
970986

@@ -5293,6 +5309,182 @@ function initMP (mpType, next) {
52935309
}
52945310
}
52955311

5312+
var updateDataTotal = 0; // 总共更新的数据量
5313+
function diffLog (updateData) {
5314+
updateData = JSON.stringify(updateData);
5315+
if (!Vue$3._mpvueTraceTimer) {
5316+
Vue$3._mpvueTraceTimer = setTimeout(function () {
5317+
clearTimeout(Vue$3._mpvueTraceTimer);
5318+
updateDataTotal = (updateDataTotal / 1024).toFixed(1);
5319+
console.log('这次操作引发500ms内数据更新量:' + updateDataTotal + 'kb');
5320+
Vue$3._mpvueTraceTimer = 0;
5321+
updateDataTotal = 0;
5322+
}, 500);
5323+
} else if (Vue$3._mpvueTraceTimer) {
5324+
updateData = updateData.replace(/[^\u0000-\u00ff]/g, 'aa'); // 中文占2字节,中文替换成两个字母计算占用空间
5325+
updateDataTotal += updateData.length;
5326+
}
5327+
}
5328+
5329+
function getDeepData (keyList, viewData) {
5330+
if (keyList.length > 1) {
5331+
var _key = keyList.splice(0, 1);
5332+
var _viewData = viewData[_key];
5333+
if (_viewData) {
5334+
return getDeepData(keyList, _viewData)
5335+
} else {
5336+
return null
5337+
}
5338+
} else {
5339+
if (viewData[keyList[0]]) {
5340+
return viewData[keyList[0]]
5341+
} else {
5342+
return null
5343+
}
5344+
}
5345+
}
5346+
function compareAndSetDeepData (key, newData, vm, data) {
5347+
// 比较引用类型数据
5348+
try {
5349+
var keyList = key.split('.');
5350+
//page.__viewData__老版小程序不存在,使用mpvue里绑的data比对
5351+
var oldData = getDeepData(keyList, vm.$root.$mp.page.data);
5352+
if (oldData === null || JSON.stringify(oldData) !== JSON.stringify(newData)) {
5353+
data[key] = newData;
5354+
}
5355+
} catch (e) {
5356+
console.log(e, key, newData, vm);
5357+
}
5358+
}
5359+
5360+
function cleanKeyPath (vm) {
5361+
if (vm.__mpKeyPath) {
5362+
Object.keys(vm.__mpKeyPath).forEach(function (_key) {
5363+
delete vm.__mpKeyPath[_key]['__keyPath'];
5364+
});
5365+
}
5366+
}
5367+
5368+
function minifyDeepData (rootKey, originKey, vmData, data, _mpValueSet, vm) {
5369+
try {
5370+
if (vmData instanceof Array) {
5371+
// 数组
5372+
compareAndSetDeepData(rootKey + '.' + originKey, vmData, vm, data);
5373+
} else {
5374+
// Object
5375+
var _keyPathOnThis = {}; // 存储这层对象的keyPath
5376+
if (vmData.__keyPath) {
5377+
// 有更新列表 ,按照更新列表更新
5378+
_keyPathOnThis = vmData.__keyPath;
5379+
Object.keys(vmData).forEach(function (_key) {
5380+
if (vmData[_key] instanceof Object) {
5381+
// 引用类型 递归
5382+
if (_key === '__keyPath') {
5383+
return
5384+
}
5385+
minifyDeepData(rootKey + '.' + originKey, _key, vmData[_key], data, null, vm);
5386+
} else {
5387+
// 更新列表中的 加入data
5388+
if (_keyPathOnThis[_key] === true) {
5389+
if (originKey) {
5390+
data[rootKey + '.' + originKey + '.' + _key] = vmData[_key];
5391+
} else {
5392+
data[rootKey + '.' + _key] = vmData[_key];
5393+
}
5394+
}
5395+
}
5396+
});
5397+
// 根节点可能有父子引用同一个引用类型数据,依赖树都遍历完后清理
5398+
vm['__mpKeyPath'] = vm['__mpKeyPath'] || {};
5399+
vm['__mpKeyPath'][vmData.__ob__.dep.id] = vmData;
5400+
} else {
5401+
// 没有更新列表
5402+
compareAndSetDeepData(rootKey + '.' + originKey, vmData, vm, data);
5403+
}
5404+
}
5405+
} catch (e) {
5406+
console.log(e, rootKey, originKey, vmData, data);
5407+
}
5408+
}
5409+
5410+
function getRootKey (vm, rootKey) {
5411+
if (!vm.$parent.$attrs) {
5412+
rootKey = '$root.0' + ',' + rootKey;
5413+
return rootKey
5414+
} else {
5415+
rootKey = vm.$parent.$attrs.mpcomid + ',' + rootKey;
5416+
return getRootKey(vm.$parent, rootKey)
5417+
}
5418+
}
5419+
5420+
function diffData (vm, data) {
5421+
var vmData = vm._data || {};
5422+
var vmProps = vm._props || {};
5423+
var rootKey = '';
5424+
if (!vm.$attrs) {
5425+
rootKey = '$root.0';
5426+
} else {
5427+
rootKey = getRootKey(vm, vm.$attrs.mpcomid);
5428+
}
5429+
Vue$3.nextTick(function () {
5430+
cleanKeyPath(vm);
5431+
});
5432+
// console.log(rootKey)
5433+
5434+
// 值类型变量不考虑优化,还是直接更新
5435+
var __keyPathOnThis = vmData.__keyPath || vm.__keyPath || {};
5436+
delete vm.__keyPath;
5437+
delete vmData.__keyPath;
5438+
delete vmProps.__keyPath;
5439+
if (vm._mpValueSet === 'done') {
5440+
// 第二次赋值才进行缩减操作
5441+
Object.keys(vmData).forEach(function (vmDataItemKey) {
5442+
if (vmData[vmDataItemKey] instanceof Object) {
5443+
// 引用类型
5444+
if (vmDataItemKey === '__keyPath') { return }
5445+
minifyDeepData(rootKey, vmDataItemKey, vmData[vmDataItemKey], data, vm._mpValueSet, vm);
5446+
} else if(vmData[vmDataItemKey] !== undefined){
5447+
// _data上的值属性只有要更新的时候才赋值
5448+
if (__keyPathOnThis[vmDataItemKey] === true) {
5449+
data[rootKey + '.' + vmDataItemKey] = vmData[vmDataItemKey];
5450+
}
5451+
}
5452+
});
5453+
5454+
Object.keys(vmProps).forEach(function (vmPropsItemKey) {
5455+
if (vmProps[vmPropsItemKey] instanceof Object) {
5456+
// 引用类型
5457+
if (vmPropsItemKey === '__keyPath') { return }
5458+
minifyDeepData(rootKey, vmPropsItemKey, vmProps[vmPropsItemKey], data, vm._mpValueSet, vm);
5459+
} else if(vmProps[vmPropsItemKey] !== undefined){
5460+
data[rootKey + '.' + vmPropsItemKey] = vmProps[vmPropsItemKey];
5461+
}
5462+
// _props上的值属性只有要更新的时候才赋值
5463+
});
5464+
5465+
// 检查完data和props,最后补上_mpProps & _computedWatchers
5466+
var vmMpProps = vm._mpProps || {};
5467+
var vmComputedWatchers = vm._computedWatchers || {};
5468+
Object.keys(vmMpProps).forEach(function (mpItemKey) {
5469+
data[rootKey + '.' + mpItemKey] = vm[mpItemKey];
5470+
});
5471+
Object.keys(vmComputedWatchers).forEach(function (computedItemKey) {
5472+
data[rootKey + '.' + computedItemKey] = vm[computedItemKey];
5473+
});
5474+
// 更新的时候要删除$root.0:{},否则会覆盖原正确数据
5475+
delete data[rootKey];
5476+
}
5477+
if (vm._mpValueSet === undefined) {
5478+
// 第一次设置数据成功后,标记位置true,再更新到这个节点如果没有keyPath数组认为不需要更新
5479+
vm._mpValueSet = 'done';
5480+
}
5481+
if (Vue$3.config.devtools) {
5482+
// console.log('更新VM节点', vm)
5483+
// console.log('实际传到Page.setData数据', data)
5484+
diffLog(data);
5485+
}
5486+
}
5487+
52965488
// 节流方法,性能优化
52975489
// 全局的命名约定,为了节省编译的包大小一律采取形象的缩写,说明如下。
52985490
// $c === $child
@@ -5429,6 +5621,8 @@ function getPage (vm) {
54295621
return page
54305622
}
54315623

5624+
// 优化js变量动态变化时候引起全量更新
5625+
54325626
// 优化每次 setData 都传递大量新数据
54335627
function updateDataToMP () {
54345628
var page = getPage(this);
@@ -5437,6 +5631,9 @@ function updateDataToMP () {
54375631
}
54385632

54395633
var data = formatVmData(this);
5634+
5635+
diffData(this, data);
5636+
54405637
throttleSetData(page.setData.bind(page), data);
54415638
}
54425639

0 commit comments

Comments
 (0)