tina-weapp
Version:
143 lines (129 loc) • 5.12 kB
JavaScript
import { getObjByKey, isNotEmptyObject, noop } from './utils'
const ARRAYTYPE = '[object Array]'
const OBJECTTYPE = '[object Object]'
const FUNCTIONTYPE = '[object Function]'
let resultMap = [], ctxMap = []
export default function diffData (ctx, data, cb) {
data = JSON.parse(JSON.stringify(data))
const diffResult = diff(data, ctx._rawData)
const result = getObjByKey(diffResult)
let isNotEmptyObj = !!isNotEmptyObject(result)
cb = cb || noop
let { _setDataBackup: { setData } } = ctx
if (isNotEmptyObj) {
setData.call(ctx, result, cb)
resultMap.push(result)
ctxMap.push(ctx)
wx.nextTick(() => {
if (resultMap.length) {
resultMap.map((obj, index) => {
let context = ctxMap[index]
Object.keys(obj).forEach(key => {
updateRawData(context, key, obj[key])
})
})
resultMap = [], ctxMap = []
}
})
}
}
function updateRawData (ctx, key, value) {
const path = key.replace(/\[(\d+?)\]/g, '.$1').split('.')
path.reduce((acc, cur, index) => {
if (index == path.length - 1) {
acc[cur] = value
}
return acc[cur]
}, ctx._rawData)
}
function diff (current, pre) {
const result = {}
syncKeys(current, pre)
_diff(current, pre, '', result)
return result
}
function syncKeys (current, pre) {
if (current === pre) return
const rootCurrentType = type(current)
const rootPreType = type(pre)
if (rootCurrentType == OBJECTTYPE && rootPreType == OBJECTTYPE) {
for (let key in pre) {
const currentValue = current[key]
if (currentValue === undefined) {
current[key] = null
} else {
syncKeys(currentValue, pre[key])
}
}
} else if (rootCurrentType == ARRAYTYPE && rootPreType == ARRAYTYPE) {
if (current.length >= pre.length) {
pre.forEach((item, index) => {
syncKeys(current[index], item)
})
}
}
}
function _diff (current, pre, path, result) {
if (current === pre) return
const rootCurrentType = type(current)
const rootPreType = type(pre)
if (rootCurrentType == OBJECTTYPE) {
if (rootPreType != OBJECTTYPE || Object.keys(current).length < Object.keys(pre).length && path != '') {
setResult(result, path, current)
} else {
for (let key in current) {
const currentValue = current[key]
const preValue = pre[key]
const currentType = type(currentValue)
const preType = type(preValue)
if (currentType != ARRAYTYPE && currentType != OBJECTTYPE) {
if (currentValue != pre[key]) {
setResult(result, (path == '' ? '' : path + ".") + key, currentValue)
}
} else if (currentType == ARRAYTYPE) {
if (preType != ARRAYTYPE) {
setResult(result, (path == '' ? '' : path + ".") + key, currentValue)
} else {
if (currentValue.length < preValue.length) {
setResult(result, (path == '' ? '' : path + ".") + key, currentValue)
} else {
currentValue.forEach((item, index) => {
_diff(item, preValue[index], (path == '' ? '' : path + ".") + key + '[' + index + ']', result)
})
}
}
} else if (currentType == OBJECTTYPE) {
if (preType != OBJECTTYPE || Object.keys(currentValue).length < Object.keys(preValue).length) {
setResult(result, (path == '' ? '' : path + ".") + key, currentValue)
} else {
for (let subKey in currentValue) {
_diff(currentValue[subKey], preValue[subKey], (path == '' ? '' : path + ".") + key + '.' + subKey, result)
}
}
}
}
}
} else if (rootCurrentType == ARRAYTYPE) {
if (rootPreType != ARRAYTYPE) {
setResult(result, path, current)
} else {
if (current.length < pre.length) {
setResult(result, path, current)
} else {
current.forEach((item, index) => {
_diff(item, pre[index], path + '[' + index + ']', result)
})
}
}
} else {
setResult(result, path, current)
}
}
function setResult (result, k, v) {
if (type(v) != FUNCTIONTYPE) {
result[k] = v
}
}
function type (obj) {
return Object.prototype.toString.call(obj)
}