Skip to content

Instantly share code, notes, and snippets.

@kerlw
Created July 22, 2019 07:56
Show Gist options
  • Save kerlw/e8579089f6dc8fa48ad9089772b5255e to your computer and use it in GitHub Desktop.
Save kerlw/e8579089f6dc8fa48ad9089772b5255e to your computer and use it in GitHub Desktop.
fragment
import { freeze, unfreeze } from "./utils";
var seq = 0
const nanoid = () => {
seq++
return `fr${seq}`
}
export const Fragment = {
abstract: true,
name: 'Fragment',
props: {
name: {
type: String,
default: () => nanoid()
},
},
mounted() {
this.__container = this.$el
const container = this.$el
const parent = container.parentNode
container.__isFragment = true
container.__isMounted = false
const head = document.createComment(`fragment#${this.name}#head`)
const tail = document.createComment(`fragment#${this.name}#tail`)
container.__head = head
container.__tail = tail
let tpl = document.createDocumentFragment()
tpl.appendChild(head)
Array.from(container.childNodes)
.forEach(node => {
// container.appendChild(node, true)
let notFrChild = !node.hasOwnProperty('__isFragmentChild__')
tpl.appendChild(node)
if (notFrChild) {
freeze(node, 'parentNode', container)
freeze(node, '__isFragmentChild__', true)
}
})
tpl.appendChild(tail)
let next = container.nextSibling
parent.insertBefore(tpl, container, true)
parent.removeChild(container)
freeze(container, 'parentNode', parent)
freeze(container, 'nextSibling', next)
if (next)
freeze(next, 'previousSibling', container)
container.__isMounted = true
},
render(h) {
const children = this.$slots.default;
// add fragment attribute on the children
if (children && children.length) {
children.forEach(child => {
let data = {...child.data, attrs: {fragment: this.name, ...(child.data || {}).attrs}};
child.data = data;
}
)
} else {
console.log("fragment without children render invoking")
}
return h(
"div",
{
attrs: {fragment: this.name},
// key: getRandomStr(),
},
children
)
}
};
export const Plugin = {
install: function (Vue) {
const orgRemoveChild = window.Node.prototype.removeChild
window.Node.prototype.removeChild = function (node) {
// console.debug('==== Remove child called ===', node)
if (this.__isFragment) {
if (this.parentNode) {
// console.debug("=== parent === isFragment:", this.parentNode.__isFragment, this.parentNode.attributes.fragment)
let ret = this.parentNode.removeChild(node)
unfreeze(node, 'parentNode')
return ret
}
} else if (node.__isFragment && node.__isMounted) {
// console.error("=== removing fragment ===", node)
while (node.__head.nextSibling !== node.__tail)
orgRemoveChild.call(this, node.__head.nextSibling)
orgRemoveChild.call(this, node.__head)
orgRemoveChild.call(this, node.__tail)
let prev = node.__head.previousSibling,
next = node.__tail.nextSibling
if (prev)
freeze(prev, 'nextSibling', next)
if (next)
freeze(next, 'previousSibling', prev)
unfreeze(node, 'parentNode')
return node
} else {
// console.warn("== real remove ==", this, node, node.__isFragment, node.previousSibling, node.nextSibling)
let prev = node.previousSibling,
next = node.nextSibling
let ret = orgRemoveChild.call(this, node)
if (prev)
freeze(prev, 'nextSibling', next)
if (next)
freeze(next, 'previousSibling', prev)
return ret
}
}
const orgInsertBefore = window.Node.prototype.insertBefore
window.Node.prototype.insertBefore = function (node, ref, inFragment = false) {
// console.log('==== insert Before called ===', node, ref)
let realRef = !!ref && !!ref.__isFragment && !!ref.__isMounted ? ref.__head : ref
if (this.__isFragment) {
let notFrChild = !node.hasOwnProperty('__isFragmentChild__'),
freezeParent = (!inFragment || notFrChild)
notFrChild && freeze(node, '__isFragmentChild__', true)
let ret = this.parentNode ? this.parentNode.insertBefore(node, ref) : orgInsertBefore.call(this, node, realRef)
freezeParent && freeze(node, 'parentNode', this)
return ret
} else if (node.__isFragment && node.__isMounted) {
// console.log("===trying to insert " + node.attributes.fragment.value + " before " + ref)
if (node === ref) {
console.error("something must be wrong")
return
}
freeze(node, 'parentNode', this)
if (node.previousSibling)
freeze(node.previousSibling, 'nextSibling', node.nextSibling)
if (node.nextSibling)
freeze(node.nextSibling, 'previousSibling', node.previousSibling)
freeze(node, 'nextSibling', ref)
freeze(node, 'previousSibling', ref.previousSibling)
if (ref.previousSibling)
freeze(ref.previousSibling, 'nextSibling', node)
freeze(ref, 'previousSibling', node)
let tpl = document.createDocumentFragment(),
ele = node.__head
while (ele !== node.__tail) {
tpl.appendChild(ele)
ele = ele.nextSibling
}
tpl.appendChild(node.__tail)
orgInsertBefore.call(this, tpl, realRef)
return node
} else {
return orgInsertBefore.call(this, node, realRef)
}
}
const orgAppendChild = window.Node.prototype.appendChild
window.Node.prototype.appendChild = function (node, inFragment = false) {
// console.debug("==== append child called === is fragment: ", !!this.__isFragment, node)
if (this.__isFragment) {
// console.log("==== container append child called ===", inFragment, this.attributes.fragment.value, node)
if (this.parentNode) {
let notFrChild = !node.hasOwnProperty('__isFragmentChild__'),
freezeParent = (!inFragment || notFrChild)
notFrChild && freeze(node, '__isFragmentChild__', true)
let ret = this.parentNode.insertBefore(node, this.__tail, inFragment)
freezeParent && freeze(node, 'parentNode', this)
return ret
}
} else {
return orgAppendChild.call(this, node)
}
}
Vue.component('fragment', Fragment)
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment