Skip to content

Commit c2923de

Browse files
committed
fix(performance): reduce object creation
Refs colinmeinke/wilderness#71
1 parent 198688c commit c2923de

1 file changed

Lines changed: 56 additions & 41 deletions

File tree

src/index.js

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ const attributeBlacklist = [
2727
]
2828

2929
/**
30-
* Wilderness' accepted node types.
30+
* Wilderness' accepted node types core props.
3131
*/
32-
const nodeTypes = [
32+
const nodeCoreProps = [
3333
{
3434
type: 'circle',
3535
coreProps: [ 'cx', 'cy', 'r' ]
@@ -64,6 +64,11 @@ const nodeTypes = [
6464
}
6565
]
6666

67+
/**
68+
* Wilderness' accepted node types.
69+
*/
70+
const nodeTypes = nodeCoreProps.map(({ type }) => type)
71+
6772
/**
6873
* Core props for the defined node type.
6974
*
@@ -74,7 +79,7 @@ const nodeTypes = [
7479
* @example
7580
* coreProps('rect')
7681
*/
77-
const coreProps = type => nodeTypes.filter(node => node.type === type)[ 0 ].coreProps
82+
const coreProps = type => nodeCoreProps.filter(node => node.type === type)[ 0 ].coreProps
7883

7984
/**
8085
* Diffs two objects and returns an object with remove and update props.
@@ -88,16 +93,21 @@ const coreProps = type => nodeTypes.filter(node => node.type === type)[ 0 ].core
8893
* diff(current, next)
8994
*/
9095
const diff = (current, next) => {
91-
const currentKeys = Object.keys(current)
92-
const nextKeys = Object.keys(next)
93-
const remove = currentKeys.filter(k => nextKeys.indexOf(k) === -1)
96+
const result = { remove: [], update: [] }
9497

95-
const update = nextKeys.filter(k => (
96-
currentKeys.indexOf(k) !== -1 ||
97-
current[ k ] !== next[ k ]
98-
))
98+
for (let currentKey in current) {
99+
if (typeof next[ currentKey ] === 'undefined') {
100+
result.remove.push(currentKey)
101+
}
102+
}
99103

100-
return { remove, update }
104+
for (let nextKey in next) {
105+
if (typeof current[ nextKey ] === 'undefined' || current[ nextKey ] !== next[ nextKey ]) {
106+
result.update.push(nextKey)
107+
}
108+
}
109+
110+
return result
101111
}
102112

103113
/**
@@ -162,9 +172,9 @@ const node = frameShp => {
162172
? groupNode(childFrameShapes)
163173
: pathNode(points)
164174

165-
Object.keys(attributes).map(attr => {
175+
for (let attr in attributes) {
166176
el.setAttribute(attr, attributes[ attr ])
167-
})
177+
}
168178

169179
return el
170180
}
@@ -257,13 +267,13 @@ const plainShapeObjectFromAttrs = (type, attributes) => {
257267
const props = coreProps(type)
258268
const result = { type }
259269

260-
Object.keys(attributes).map(k => {
270+
for (let k in attributes) {
261271
if (props.indexOf(k) !== -1) {
262272
const v = attributes[ k ]
263273
const n = Number(v)
264274
result[ k ] = Number.isNaN(n) ? v : n
265275
}
266-
})
276+
}
267277

268278
return result
269279
}
@@ -283,11 +293,11 @@ const removeCoreProps = (type, attributes) => {
283293
const props = coreProps(type)
284294
const result = {}
285295

286-
Object.keys(attributes).map(k => {
296+
for (let k in attributes) {
287297
if (props.indexOf(k) === -1) {
288298
result[ k ] = attributes[ k ]
289299
}
290-
})
300+
}
291301

292302
return result
293303
}
@@ -301,40 +311,45 @@ const removeCoreProps = (type, attributes) => {
301311
* @returns {Node}
302312
*
303313
* @example
304-
* updateNode()
314+
* updateNode(el, frameShape)
305315
*/
306316
const updateNode = (el, frameShp) => {
307-
if (validNode(el) && validFrameShape(frameShp)) {
308-
const { attributes: nextAttributes, childFrameShapes, points } = frameShp
317+
if (__DEV__) {
318+
if (!validNode(el)) {
319+
throw new TypeError(`The first argument of the updateNode function must be a valid DOM node`)
320+
}
309321

310-
if (childFrameShapes) {
311-
const childNodes = [ ...el.childNodes ].filter(validNodeType)
322+
if (!validFrameShape(frameShp)) {
323+
throw new TypeError(`The second argument of the updateNode function must be a valid frameShape`)
324+
}
325+
}
312326

313-
childFrameShapes.map((childFrameShape, i) => {
314-
updateNode(childNodes[ i ], childFrameShape)
315-
})
316-
} else {
317-
const nextPath = toPath(points)
327+
if (frameShp.childFrameShapes) {
328+
const childNodes = [ ...el.childNodes ].filter(validNodeType)
318329

319-
if (nextPath !== el.getAttribute('d')) {
320-
el.setAttribute('d', nextPath)
321-
}
330+
for (let i = 0, l = frameShp.childFrameShapes.length; i < l; i++) {
331+
updateNode(childNodes[ i ], frameShp.childFrameShapes[ i ])
322332
}
333+
} else {
334+
const nextPath = toPath(frameShp.points)
323335

324-
const { attributes: currentAttributes } = el
325-
326-
const { remove, update } = diff(currentAttributes, nextAttributes)
336+
if (nextPath !== el.getAttribute('d')) {
337+
el.setAttribute('d', nextPath)
338+
}
339+
}
327340

328-
remove.map(attr => {
329-
el.removeAttribute(attr)
330-
})
341+
const result = diff(el.attributes, frameShp.attributes)
331342

332-
update.map(attr => {
333-
el.setAttribute(attr, nextAttributes[ attr ])
334-
})
343+
for (let i = 0, l = result.remove.length; i < l; i++) {
344+
el.removeAttribute(result.remove[ i ])
345+
}
335346

336-
return el
347+
for (let i = 0, l = result.update.length; i < l; i++) {
348+
const attr = result.update[ i ]
349+
el.setAttribute(attr, frameShp.attributes[ attr ])
337350
}
351+
352+
return el
338353
}
339354

340355
/**
@@ -425,6 +440,6 @@ const validNode = el => {
425440
* @example
426441
* validNodeType(node)
427442
*/
428-
const validNodeType = ({ nodeName }) => nodeTypes.map(({ type }) => type).indexOf(nodeName) !== -1
443+
const validNodeType = ({ nodeName }) => nodeTypes.indexOf(nodeName) !== -1
429444

430445
export { frameShape, node, plainShapeObject, updateNode }

0 commit comments

Comments
 (0)