vuelidate
Version:
Simple, lightweight model-based validation for Vue.js
141 lines (127 loc) • 3.83 kB
JavaScript
// a minimial single-layer implementation
// of virtual-validation patching,
// based on Vue's snabbdom clone
function isUndef(v) {
return v === null || v === undefined
}
function isDef(v) {
return v !== null && v !== undefined
}
function sameVval(oldVval, vval) {
return vval.tag === oldVval.tag && vval.key === oldVval.key
}
function createVm(vval) {
const Vm = vval.tag
vval.vm = new Vm({ data: vval.args })
}
function updateVval(vval) {
const keys = Object.keys(vval.args)
for (let i = 0; i < keys.length; i++) {
keys.forEach((k) => {
vval.vm[k] = vval.args[k]
})
}
}
function createKeyToOldIdx(children, beginIdx, endIdx) {
let i, key
const map = {}
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key
if (isDef(key)) map[key] = i
}
return map
}
function updateChildren(oldCh, newCh) {
let oldStartIdx = 0
let newStartIdx = 0
let oldEndIdx = oldCh.length - 1
let oldStartVval = oldCh[0]
let oldEndVval = oldCh[oldEndIdx]
let newEndIdx = newCh.length - 1
let newStartVval = newCh[0]
let newEndVval = newCh[newEndIdx]
let oldKeyToIdx, idxInOld, elmToMove
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
if (isUndef(oldStartVval)) {
oldStartVval = oldCh[++oldStartIdx] // Vval has been moved left
} else if (isUndef(oldEndVval)) {
oldEndVval = oldCh[--oldEndIdx]
} else if (sameVval(oldStartVval, newStartVval)) {
patchVval(oldStartVval, newStartVval)
oldStartVval = oldCh[++oldStartIdx]
newStartVval = newCh[++newStartIdx]
} else if (sameVval(oldEndVval, newEndVval)) {
patchVval(oldEndVval, newEndVval)
oldEndVval = oldCh[--oldEndIdx]
newEndVval = newCh[--newEndIdx]
} else if (sameVval(oldStartVval, newEndVval)) {
// Vval moved right
patchVval(oldStartVval, newEndVval)
oldStartVval = oldCh[++oldStartIdx]
newEndVval = newCh[--newEndIdx]
} else if (sameVval(oldEndVval, newStartVval)) {
// Vval moved left
patchVval(oldEndVval, newStartVval)
oldEndVval = oldCh[--oldEndIdx]
newStartVval = newCh[++newStartIdx]
} else {
if (isUndef(oldKeyToIdx))
oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)
idxInOld = isDef(newStartVval.key) ? oldKeyToIdx[newStartVval.key] : null
if (isUndef(idxInOld)) {
// New element
createVm(newStartVval)
newStartVval = newCh[++newStartIdx]
} else {
elmToMove = oldCh[idxInOld]
if (sameVval(elmToMove, newStartVval)) {
patchVval(elmToMove, newStartVval)
oldCh[idxInOld] = undefined
newStartVval = newCh[++newStartIdx]
} else {
// same key but different element. treat as new element
createVm(newStartVval)
newStartVval = newCh[++newStartIdx]
}
}
}
}
if (oldStartIdx > oldEndIdx) {
addVvals(newCh, newStartIdx, newEndIdx)
} else if (newStartIdx > newEndIdx) {
removeVvals(oldCh, oldStartIdx, oldEndIdx)
}
}
function addVvals(vvals, startIdx, endIdx) {
for (; startIdx <= endIdx; ++startIdx) {
createVm(vvals[startIdx])
}
}
function removeVvals(vvals, startIdx, endIdx) {
for (; startIdx <= endIdx; ++startIdx) {
const ch = vvals[startIdx]
if (isDef(ch)) {
ch.vm.$destroy()
ch.vm = null
}
}
}
function patchVval(oldVval, vval) {
if (oldVval === vval) {
return
}
vval.vm = oldVval.vm
updateVval(vval)
}
export function patchChildren(oldCh, ch) {
if (isDef(oldCh) && isDef(ch)) {
if (oldCh !== ch) updateChildren(oldCh, ch)
} else if (isDef(ch)) {
addVvals(ch, 0, ch.length - 1)
} else if (isDef(oldCh)) {
removeVvals(oldCh, 0, oldCh.length - 1)
}
}
export function h(tag, key, args) {
return { tag, key, args }
}