From 6bc6630b87e6e56ece8bbd9721d7c42d3a0e6e86 Mon Sep 17 00:00:00 2001 From: Jukka Kurkela Date: Sat, 19 Oct 2019 16:19:12 +0300 Subject: [PATCH] [perf] cache resolved data element options (#6579) * [perf] cache resolved data element options * Address review comments * Move uninitialized variables, update comments --- src/controllers/controller.bubble.js | 5 +++++ src/core/core.datasetController.js | 23 +++++++++++++++++++---- src/helpers/helpers.options.js | 10 +++++++++- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/controllers/controller.bubble.js b/src/controllers/controller.bubble.js index 8882969d48a..508e9bb3436 100644 --- a/src/controllers/controller.bubble.js +++ b/src/controllers/controller.bubble.js @@ -154,6 +154,11 @@ module.exports = DatasetController.extend({ datasetIndex: me.index }; + // In case values were cached (and thus frozen), we need to clone the values + if (me._cachedDataOpts === values) { + values = helpers.extend({}, values); + } + // Custom radius resolution values.radius = resolve([ custom.radius, diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 03a249a3fde..0df3ed60206 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -290,6 +290,7 @@ helpers.extend(DatasetController.prototype, { _update: function(reset) { var me = this; me._configure(); + me._cachedDataOpts = null; me.update(reset); }, @@ -389,13 +390,16 @@ helpers.extend(DatasetController.prototype, { */ _resolveDataElementOptions: function(element, index) { var me = this; + var custom = element && element.custom; + var cached = me._cachedDataOpts; + if (cached && !custom) { + return cached; + } var chart = me.chart; var datasetOpts = me._config; - var custom = element.custom || {}; var options = chart.options.elements[me.dataElementType.prototype._type] || {}; var elementOptions = me._dataElementOptions; var values = {}; - var keys, i, ilen, key; // Scriptable options var context = { @@ -405,6 +409,13 @@ helpers.extend(DatasetController.prototype, { datasetIndex: me.index }; + // `resolve` sets cacheable to `false` if any option is indexed or scripted + var info = {cacheable: !custom}; + + var keys, i, ilen, key; + + custom = custom || {}; + if (helpers.isArray(elementOptions)) { for (i = 0, ilen = elementOptions.length; i < ilen; ++i) { key = elementOptions[i]; @@ -412,7 +423,7 @@ helpers.extend(DatasetController.prototype, { custom[key], datasetOpts[key], options[key] - ], context, index); + ], context, index, info); } } else { keys = Object.keys(elementOptions); @@ -423,10 +434,14 @@ helpers.extend(DatasetController.prototype, { datasetOpts[elementOptions[key]], datasetOpts[key], options[key] - ], context, index); + ], context, index, info); } } + if (info.cacheable) { + me._cachedDataOpts = Object.freeze(values); + } + return values; }, diff --git a/src/helpers/helpers.options.js b/src/helpers/helpers.options.js index 0e96a71079c..0bca84dba51 100644 --- a/src/helpers/helpers.options.js +++ b/src/helpers/helpers.options.js @@ -115,9 +115,12 @@ module.exports = { * is called with `context` as first argument and the result becomes the new input. * @param {number} [index] - If defined and the current value is an array, the value * at `index` become the new input. + * @param {object} [info] - object to return information about resolution in + * @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable. * @since 2.7.0 */ - resolve: function(inputs, context, index) { + resolve: function(inputs, context, index, info) { + var cacheable = true; var i, ilen, value; for (i = 0, ilen = inputs.length; i < ilen; ++i) { @@ -127,11 +130,16 @@ module.exports = { } if (context !== undefined && typeof value === 'function') { value = value(context); + cacheable = false; } if (index !== undefined && helpers.isArray(value)) { value = value[index]; + cacheable = false; } if (value !== undefined) { + if (info && !cacheable) { + info.cacheable = false; + } return value; } }