Aggressively optimise client library to prevent handlebars enrichment where possible

This commit is contained in:
Andrew Kingston 2021-06-25 15:04:27 +01:00
parent 83a337e20e
commit be2ec9b427
4 changed files with 69 additions and 26 deletions

View File

@ -23,6 +23,11 @@
// props with old ones, depending on how long enrichment takes.
let latestUpdateTime
// Keep track of stringified representations of context and instance
// to avoid enriching bindings as much as possible
let lastContextKey
let lastInstanceKey
// Get contexts
const context = getContext("context")
const insideScreenslot = !!getContext("screenslot")
@ -42,7 +47,9 @@
definition?.hasChildren &&
definition?.showEmptyState !== false &&
$builderStore.inBuilder
$: updateComponentProps(instance, $context)
$: rawProps = getRawProps(instance)
$: instanceKey = JSON.stringify(rawProps)
$: updateComponentProps(rawProps, instanceKey, $context)
$: selected =
$builderStore.inBuilder &&
$builderStore.selectedComponentId === instance._id
@ -59,6 +66,16 @@
name,
})
const getRawProps = instance => {
let validProps = {}
Object.entries(instance)
.filter(([name]) => !name.startsWith("_"))
.forEach(([key, value]) => {
validProps[key] = value
})
return validProps
}
// Gets the component constructor for the specified component
const getComponentConstructor = component => {
const split = component?.split("/")
@ -76,13 +93,23 @@
}
// Enriches any string component props using handlebars
const updateComponentProps = (instance, context) => {
const updateComponentProps = (rawProps, instanceKey, context) => {
const instanceSame = instanceKey === lastInstanceKey
const contextSame = context.key === lastContextKey
if (instanceSame && contextSame) {
return
} else {
lastInstanceKey = instanceKey
lastContextKey = context.key
}
// Record the timestamp so we can reference it after enrichment
latestUpdateTime = Date.now()
const enrichmentTime = latestUpdateTime
// Enrich props with context
const enrichedProps = enrichProps(instance, context)
const enrichedProps = enrichProps(rawProps, context)
// Abandon this update if a newer update has started
if (enrichmentTime !== latestUpdateTime) {

View File

@ -14,18 +14,32 @@
const newContext = createContextStore(context)
setContext("context", newContext)
$: providerKey = key || $component.id
const providerKey = key || $component.id
// Add data context
$: newContext.actions.provideData(providerKey, data)
// Generate a permanent unique ID for this component and use it to register
// any datasource actions
const instanceId = generate()
// Instance ID is unique to each instance of a provider
let instanceId
// Keep previous state around so we can avoid updating unless necessary
let lastDataKey
let lastActionsKey
// Add actions context
$: {
if (instanceId) {
actions?.forEach(({ type, callback, metadata }) => {
$: provideData(data)
$: provideActions(actions, instanceId)
const provideData = newData => {
const dataKey = JSON.stringify(newData)
if (dataKey !== lastDataKey) {
newContext.actions.provideData(providerKey, newData)
lastDataKey = dataKey
}
}
const provideActions = newActions => {
const actionsKey = JSON.stringify(newActions)
if (actionsKey !== lastActionsKey) {
lastActionsKey = actionsKey
newActions?.forEach(({ type, callback, metadata }) => {
newContext.actions.provideAction(providerKey, type, callback)
// Register any "refresh datasource" actions with a singleton store
@ -43,10 +57,6 @@
}
onMount(() => {
// Generate a permanent unique ID for this component and use it to register
// any datasource actions
instanceId = generate()
// Unregister all datasource instances when unmounting this provider
return () => dataSourceStore.actions.unregisterInstance(instanceId)
})

View File

@ -4,7 +4,21 @@ export const createContextStore = oldContext => {
const newContext = writable({})
const contexts = oldContext ? [oldContext, newContext] : [newContext]
const totalContext = derived(contexts, $contexts => {
return $contexts.reduce((total, context) => ({ ...total, ...context }), {})
// The key is the serialized representation of context
let key = ""
for (let i = 0; i < $contexts.length - 1; i++) {
key += $contexts[i].key
}
key += JSON.stringify($contexts[$contexts.length - 1])
// Reduce global state
const reducer = (total, context) => ({ ...total, ...context })
const context = $contexts.reduce(reducer, {})
return {
...context,
key,
}
})
// Adds a data context layer to the tree

View File

@ -22,14 +22,6 @@ export const propsAreSame = (a, b) => {
* Data bindings are enriched, and button actions are enriched.
*/
export const enrichProps = (props, context) => {
// Exclude all private props that start with an underscore
let validProps = {}
Object.entries(props)
.filter(([name]) => !name.startsWith("_"))
.forEach(([key, value]) => {
validProps[key] = value
})
// Create context of all bindings and data contexts
// Duplicate the closest context as "data" which the builder requires
const totalContext = {
@ -41,7 +33,7 @@ export const enrichProps = (props, context) => {
}
// Enrich all data bindings in top level props
let enrichedProps = enrichDataBindings(validProps, totalContext)
let enrichedProps = enrichDataBindings(props, totalContext)
// Enrich click actions if they exist
if (enrichedProps.onClick) {