Skip to content

Commit

Permalink
Merge pull request #1241 from Meituan-Dianping/develop
Browse files Browse the repository at this point in the history
mpvue 数据增量同步更新
  • Loading branch information
hucq authored Dec 5, 2018
2 parents 63700b2 + af88676 commit 2163f8c
Show file tree
Hide file tree
Showing 9 changed files with 433 additions and 18 deletions.
23 changes: 18 additions & 5 deletions packages/mpvue-template-compiler/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -2810,10 +2810,13 @@ var observerState = {
* object's property keys into getter/setters that
* collect dependencies and dispatches updates.
*/
var Observer = function Observer (value) {
var Observer = function Observer (value, key) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
if (key) {
this.key = key;
}
def(value, '__ob__', this);
if (Array.isArray(value)) {
var augment = hasProto
Expand Down Expand Up @@ -2876,7 +2879,7 @@ function copyAugment (target, src, keys) {
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
*/
function observe (value, asRootData) {
function observe (value, asRootData, key) {
if (!isObject(value)) {
return
}
Expand All @@ -2890,7 +2893,9 @@ function observe (value, asRootData) {
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value);
ob = new Observer(value, key);
ob.__keyPath = ob.__keyPath ? ob.__keyPath : {};
ob.__keyPath[key] = true;
}
if (asRootData && ob) {
ob.vmCount++;
Expand All @@ -2915,11 +2920,13 @@ function defineReactive$$1 (
return
}

// TODO: 先试验标记一下 keyPath

// cater for pre-defined getter/setters
var getter = property && property.get;
var setter = property && property.set;

var childOb = !shallow && observe(val);
var childOb = !shallow && observe(val, undefined, key);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
Expand All @@ -2942,6 +2949,7 @@ function defineReactive$$1 (
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}

/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter();
Expand All @@ -2951,8 +2959,10 @@ function defineReactive$$1 (
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
childOb = !shallow && observe(newVal, undefined, key);
dep.notify();
obj.__keyPath = obj.__keyPath ? obj.__keyPath : {};
obj.__keyPath[key] = true;
}
});
}
Expand Down Expand Up @@ -2985,6 +2995,9 @@ function set (target, key, val) {
return val
}
defineReactive$$1(ob.value, key, val);
// Vue.set 添加对象属性,渲染时候把val传给小程序渲染
target.__keyPath = target.__keyPath ? target.__keyPath : {};
target.__keyPath[key] = true;
ob.dep.notify();
return val
}
Expand Down
207 changes: 202 additions & 5 deletions packages/mpvue/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -763,10 +763,13 @@ var observerState = {
* object's property keys into getter/setters that
* collect dependencies and dispatches updates.
*/
var Observer = function Observer (value) {
var Observer = function Observer (value, key) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
if (key) {
this.key = key;
}
def(value, '__ob__', this);
if (Array.isArray(value)) {
var augment = hasProto
Expand Down Expand Up @@ -829,7 +832,7 @@ function copyAugment (target, src, keys) {
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
*/
function observe (value, asRootData) {
function observe (value, asRootData, key) {
if (!isObject(value)) {
return
}
Expand All @@ -843,7 +846,9 @@ function observe (value, asRootData) {
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value);
ob = new Observer(value, key);
ob.__keyPath = ob.__keyPath ? ob.__keyPath : {};
ob.__keyPath[key] = true;
}
if (asRootData && ob) {
ob.vmCount++;
Expand All @@ -868,11 +873,13 @@ function defineReactive$$1 (
return
}

// TODO: 先试验标记一下 keyPath

// cater for pre-defined getter/setters
var getter = property && property.get;
var setter = property && property.set;

var childOb = !shallow && observe(val);
var childOb = !shallow && observe(val, undefined, key);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
Expand All @@ -895,6 +902,7 @@ function defineReactive$$1 (
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}

/* eslint-enable no-self-compare */
if ("production" !== 'production' && customSetter) {
customSetter();
Expand All @@ -904,8 +912,10 @@ function defineReactive$$1 (
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
childOb = !shallow && observe(newVal, undefined, key);
dep.notify();
obj.__keyPath = obj.__keyPath ? obj.__keyPath : {};
obj.__keyPath[key] = true;
}
});
}
Expand Down Expand Up @@ -938,6 +948,9 @@ function set (target, key, val) {
return val
}
defineReactive$$1(ob.value, key, val);
// Vue.set 添加对象属性,渲染时候把val传给小程序渲染
target.__keyPath = target.__keyPath ? target.__keyPath : {};
target.__keyPath[key] = true;
ob.dep.notify();
return val
}
Expand Down Expand Up @@ -965,6 +978,9 @@ function del (target, key) {
if (!ob) {
return
}
target.__keyPath = target.__keyPath ? target.__keyPath : {};
// Vue.del 删除对象属性,渲染时候把这个属性设置为undefined
target.__keyPath[key] = 'del';
ob.dep.notify();
}

Expand Down Expand Up @@ -5293,6 +5309,182 @@ function initMP (mpType, next) {
}
}

var updateDataTotal = 0; // 总共更新的数据量
function diffLog (updateData) {
updateData = JSON.stringify(updateData);
if (!Vue$3._mpvueTraceTimer) {
Vue$3._mpvueTraceTimer = setTimeout(function () {
clearTimeout(Vue$3._mpvueTraceTimer);
updateDataTotal = (updateDataTotal / 1024).toFixed(1);
console.log('这次操作引发500ms内数据更新量:' + updateDataTotal + 'kb');
Vue$3._mpvueTraceTimer = 0;
updateDataTotal = 0;
}, 500);
} else if (Vue$3._mpvueTraceTimer) {
updateData = updateData.replace(/[^\u0000-\u00ff]/g, 'aa'); // 中文占2字节,中文替换成两个字母计算占用空间
updateDataTotal += updateData.length;
}
}

function getDeepData (keyList, viewData) {
if (keyList.length > 1) {
var _key = keyList.splice(0, 1);
var _viewData = viewData[_key];
if (_viewData) {
return getDeepData(keyList, _viewData)
} else {
return null
}
} else {
if (viewData[keyList[0]]) {
return viewData[keyList[0]]
} else {
return null
}
}
}
function compareAndSetDeepData (key, newData, vm, data) {
// 比较引用类型数据
try {
var keyList = key.split('.');
//page.__viewData__老版小程序不存在,使用mpvue里绑的data比对
var oldData = getDeepData(keyList, vm.$root.$mp.page.data);
if (oldData === null || JSON.stringify(oldData) !== JSON.stringify(newData)) {
data[key] = newData;
}
} catch (e) {
console.log(e, key, newData, vm);
}
}

function cleanKeyPath (vm) {
if (vm.__mpKeyPath) {
Object.keys(vm.__mpKeyPath).forEach(function (_key) {
delete vm.__mpKeyPath[_key]['__keyPath'];
});
}
}

function minifyDeepData (rootKey, originKey, vmData, data, _mpValueSet, vm) {
try {
if (vmData instanceof Array) {
// 数组
compareAndSetDeepData(rootKey + '.' + originKey, vmData, vm, data);
} else {
// Object
var _keyPathOnThis = {}; // 存储这层对象的keyPath
if (vmData.__keyPath) {
// 有更新列表 ,按照更新列表更新
_keyPathOnThis = vmData.__keyPath;
Object.keys(vmData).forEach(function (_key) {
if (vmData[_key] instanceof Object) {
// 引用类型 递归
if (_key === '__keyPath') {
return
}
minifyDeepData(rootKey + '.' + originKey, _key, vmData[_key], data, null, vm);
} else {
// 更新列表中的 加入data
if (_keyPathOnThis[_key] === true) {
if (originKey) {
data[rootKey + '.' + originKey + '.' + _key] = vmData[_key];
} else {
data[rootKey + '.' + _key] = vmData[_key];
}
}
}
});
// 根节点可能有父子引用同一个引用类型数据,依赖树都遍历完后清理
vm['__mpKeyPath'] = vm['__mpKeyPath'] || {};
vm['__mpKeyPath'][vmData.__ob__.dep.id] = vmData;
} else {
// 没有更新列表
compareAndSetDeepData(rootKey + '.' + originKey, vmData, vm, data);
}
}
} catch (e) {
console.log(e, rootKey, originKey, vmData, data);
}
}

function getRootKey (vm, rootKey) {
if (!vm.$parent.$attrs) {
rootKey = '$root.0' + ',' + rootKey;
return rootKey
} else {
rootKey = vm.$parent.$attrs.mpcomid + ',' + rootKey;
return getRootKey(vm.$parent, rootKey)
}
}

function diffData (vm, data) {
var vmData = vm._data || {};
var vmProps = vm._props || {};
var rootKey = '';
if (!vm.$attrs) {
rootKey = '$root.0';
} else {
rootKey = getRootKey(vm, vm.$attrs.mpcomid);
}
Vue$3.nextTick(function () {
cleanKeyPath(vm);
});
// console.log(rootKey)

// 值类型变量不考虑优化,还是直接更新
var __keyPathOnThis = vmData.__keyPath || vm.__keyPath || {};
delete vm.__keyPath;
delete vmData.__keyPath;
delete vmProps.__keyPath;
if (vm._mpValueSet === 'done') {
// 第二次赋值才进行缩减操作
Object.keys(vmData).forEach(function (vmDataItemKey) {
if (vmData[vmDataItemKey] instanceof Object) {
// 引用类型
if (vmDataItemKey === '__keyPath') { return }
minifyDeepData(rootKey, vmDataItemKey, vmData[vmDataItemKey], data, vm._mpValueSet, vm);
} else if(vmData[vmDataItemKey] !== undefined){
// _data上的值属性只有要更新的时候才赋值
if (__keyPathOnThis[vmDataItemKey] === true) {
data[rootKey + '.' + vmDataItemKey] = vmData[vmDataItemKey];
}
}
});

Object.keys(vmProps).forEach(function (vmPropsItemKey) {
if (vmProps[vmPropsItemKey] instanceof Object) {
// 引用类型
if (vmPropsItemKey === '__keyPath') { return }
minifyDeepData(rootKey, vmPropsItemKey, vmProps[vmPropsItemKey], data, vm._mpValueSet, vm);
} else if(vmProps[vmPropsItemKey] !== undefined){
data[rootKey + '.' + vmPropsItemKey] = vmProps[vmPropsItemKey];
}
// _props上的值属性只有要更新的时候才赋值
});

// 检查完data和props,最后补上_mpProps & _computedWatchers
var vmMpProps = vm._mpProps || {};
var vmComputedWatchers = vm._computedWatchers || {};
Object.keys(vmMpProps).forEach(function (mpItemKey) {
data[rootKey + '.' + mpItemKey] = vm[mpItemKey];
});
Object.keys(vmComputedWatchers).forEach(function (computedItemKey) {
data[rootKey + '.' + computedItemKey] = vm[computedItemKey];
});
// 更新的时候要删除$root.0:{},否则会覆盖原正确数据
delete data[rootKey];
}
if (vm._mpValueSet === undefined) {
// 第一次设置数据成功后,标记位置true,再更新到这个节点如果没有keyPath数组认为不需要更新
vm._mpValueSet = 'done';
}
if (Vue$3.config.devtools) {
// console.log('更新VM节点', vm)
// console.log('实际传到Page.setData数据', data)
diffLog(data);
}
}

// 节流方法,性能优化
// 全局的命名约定,为了节省编译的包大小一律采取形象的缩写,说明如下。
// $c === $child
Expand Down Expand Up @@ -5429,6 +5621,8 @@ function getPage (vm) {
return page
}

// 优化js变量动态变化时候引起全量更新

// 优化每次 setData 都传递大量新数据
function updateDataToMP () {
var page = getPage(this);
Expand All @@ -5437,6 +5631,9 @@ function updateDataToMP () {
}

var data = formatVmData(this);

diffData(this, data);

throttleSetData(page.setData.bind(page), data);
}

Expand Down
Loading

0 comments on commit 2163f8c

Please sign in to comment.