日照项目的居民端小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

160 lines
5.1 KiB

import dataPath from './data-path';
import dataTracer from './data-tracer';
const getDataOnPath = function (data, path) {
let ret = data
path.forEach((s) => {
if (typeof ret !== 'object' || ret === null) ret = undefined
else ret = ret[s]
})
return ret
}
const setDataOnPath = function (data, path, value) {
let cur = data
let index = 0
while (index < path.length - 1) {
const s = path[index++]
if (typeof s === 'number') {
if (!(cur[s] instanceof Array)) {
cur[s] = []
}
} else if (typeof cur[s] !== 'object' || cur[s] === null) {
cur[s] = {}
}
cur = cur[s]
}
cur[path[index]] = value
}
export default Behavior({
lifetimes: {
created() {
this._initComputedWatchInfo()
}
},
definitionFilter(defFields) {
const computedDef = defFields.computed || {}
const watchDef = defFields.watch || {}
const observersItems = []
if (!defFields.methods) {
defFields.methods = {}
}
if (!defFields.data) {
defFields.data = {}
}
if (defFields.methods._initComputedWatchInfo) {
throw new Error('Please do not use this behavior more than once in a single component')
}
// initialize status, executed on created
const initFuncs = []
defFields.methods._initComputedWatchInfo = function () {
if (this._computedWatchInfo) return
this._computedWatchInfo = {
computedRelatedPathValues: {},
watchCurVal: {},
}
initFuncs.forEach((func) => func.call(this))
}
// handling computed
const computedUpdaters = []
Object.keys(computedDef).forEach((targetField) => {
const targetPath = dataPath.parseSingleDataPath(targetField)
const updateMethod = computedDef[targetField]
// update value and calculate related paths
const updateValueAndRelatedPaths = function () {
const oldPathValues = this._computedWatchInfo.computedRelatedPathValues[targetField]
let needUpdate = false
for (let i = 0; i < oldPathValues.length; i++) {
const {path, value: oldVal} = oldPathValues[i]
const curVal = getDataOnPath(this.data, path)
if (oldVal !== curVal) {
needUpdate = true
break
}
}
if (!needUpdate) return false
const relatedPathValues = []
const val = updateMethod(dataTracer.create(this.data, relatedPathValues))
this.setData({
[targetField]: val
})
this._computedWatchInfo.computedRelatedPathValues[targetField] = relatedPathValues
return true
}
// calculate value on registration
const relatedPathValuesOnDef = []
const val = updateMethod(dataTracer.create(defFields.data, relatedPathValuesOnDef))
setDataOnPath(defFields.data, targetPath, val)
initFuncs.push(function () {
const pathValues = relatedPathValuesOnDef.map(({path}) => ({
path,
value: getDataOnPath(this.data, path)
}))
this._computedWatchInfo.computedRelatedPathValues[targetField] = pathValues
})
// calculate value on setData
computedUpdaters.push(updateValueAndRelatedPaths)
})
if (computedUpdaters.length) {
// add a single observer for all computed fields
observersItems.push({
fields: '**',
observer() {
if (!this._computedWatchInfo) return
let changed
do {
changed = false
// eslint-disable-next-line no-loop-func
computedUpdaters.forEach((func) => {
if (func.call(this)) changed = true
})
} while (changed)
}
})
}
// handling watch
Object.keys(watchDef).forEach((watchPath) => {
const paths = dataPath.parseMultiDataPaths(watchPath)
// record the original value of watch targets
initFuncs.push(function () {
const curVal = paths.map((path) => getDataOnPath(this.data, path))
this._computedWatchInfo.watchCurVal[watchPath] = curVal
})
// add watch observer
observersItems.push({
fields: watchPath,
observer() {
if (!this._computedWatchInfo) return
const oldVal = this._computedWatchInfo.watchCurVal[watchPath]
const curVal = paths.map((path) => getDataOnPath(this.data, path))
this._computedWatchInfo.watchCurVal[watchPath] = curVal
let changed = false
for (let i = 0; i < curVal.length; i++) {
if (oldVal[i] !== curVal[i]) {
changed = true
break
}
}
if (changed) watchDef[watchPath].apply(this, curVal)
}
})
})
// register to observers
if (typeof defFields.observers !== 'object') {
defFields.observers = {}
}
if (defFields.observers instanceof Array) {
defFields.observers.push(...observersItems)
} else {
observersItems.forEach((item) => {
defFields.observers[item.fields] = item.observer
})
}
},
})