Skip to content

Commit

Permalink
Deferred loading (#7)
Browse files Browse the repository at this point in the history
* Ability to async loading the component chunk
* Introduce Component interface
* Updated documentation
  • Loading branch information
bububo authored and anton-github committed Nov 26, 2019
1 parent 06966d0 commit 01d5970
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 32 deletions.
2 changes: 1 addition & 1 deletion demo/dist/dc.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion demo/dist/dc.min.js.map

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,33 @@ <h2>Lazy initialization</h2>
</script>

</div>

<div class="mb-5">
<h2>Deferred Loading</h2>
<p>
You can defer your components loading by changing the way of registering components. This technique uses Webpack dynamic import under the hood. So Webpack creates a separate chunk for the component on the build step.
</p>
<script data-live-highlight="deferred-1">
const importFunction = () => import(/* webpackChunkName: "some-component" */ './some-component');
dcFactory.register(dcDeferredLoading(importFunction, 'some-component'));


</script>
<div data-live-highlight-target="deferred-1"></div>
<p>
The second argument in <i>dcDeferredLoading</i> should be the component's namespace.
</p>
<script data-live-highlight="deferred-2">
class SomeComponent extends DcBaseComponent {
static getNamespace() {
return 'some-component'
}
}
</script>
<div data-live-highlight-target="deferred-2"></div>
<p>The deferred loading is more powerfull with lazy initialization. In this case, the component's chunk will be loaded after dcFactory tries to initialize it.</p>
</div>

</div>
</div>
</body>
Expand Down
71 changes: 61 additions & 10 deletions src/dc-base-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,49 @@ import utils from './utils';
* @typedef {Object.<string, {(HTMLElement|HTMLElement[]|Object.<string, HTMLElement>)}>} ReferencesCollection
*/

/**
* Interface for classes that can be initialized by factory.
* @interface Component
*/
/**
* Init Component
* @method
* @name Component#init
*
*/
/**
* Destroy Component
* @method
* @name Component#destroy
*
*/
/**
* @method
* @name Component#getElement
* @return {HTMLElement} element
*/
/**
* @method
* @name Component#getNamespace
* @static
*/

/**
* Base component.
* @class
* @implements {Component}
*/
export default class DcBaseComponent {
/**
* @param {HTMLElement} element
*/
constructor(element) {
utils.checkForbiddenOverrides(DcBaseComponent, this, ['getElement', 'getNamespace', 'init', 'addListener']);
utils.checkForbiddenOverrides(DcBaseComponent, this, [
'getElement',
'getNamespace',
'init',
'addListener'
]);

/**
* Root element of the component
Expand Down Expand Up @@ -49,7 +86,11 @@ export default class DcBaseComponent {
/**
* @type {ReferencesCollection}
*/
this.refs = dcDom.getElementRefs(element, this.getNamespace(), this._id);
this.refs = dcDom.getElementRefs(
element,
this.getNamespace(),
this._id
);

this._checkRequiredRefs(this.refs);
}
Expand Down Expand Up @@ -94,9 +135,11 @@ export default class DcBaseComponent {
* @private
*/
_checkRequiredRefs(refs) {
this.constructor.getRequiredRefs().forEach((name) => {
this.constructor.getRequiredRefs().forEach(name => {
if (!refs[name]) {
throw new Error(`the value of required ref ${name} is ${refs[name]}`);
throw new Error(
`the value of required ref ${name} is ${refs[name]}`
);
}
});
}
Expand All @@ -122,7 +165,7 @@ export default class DcBaseComponent {
}

_removeAllListeners() {
this._listenersList.forEach((listener) => {
this._listenersList.forEach(listener => {
const { elem, eventName, eventCallback } = listener;
elem.removeEventListener(eventName, eventCallback);
});
Expand All @@ -139,19 +182,27 @@ export default class DcBaseComponent {
this.onDestroy();
}

onDestroy() {
}
onDestroy() {}

getChildAttribute(childNode, attributeName) {
const childId = dcDom.getParentId(childNode, this.getNamespace());
if (this._id !== childId) {
throw new Error('id attributes of child and parent don\'t match');
throw new Error("id attributes of child and parent don't match");
}

return dcDom.getNamespacedAttributeValue(childNode, attributeName, this.getNamespace());
return dcDom.getNamespacedAttributeValue(
childNode,
attributeName,
this.getNamespace()
);
}

findChildrenWithAttribute(attributeName) {
return dcDom.findChildrenWithAttribute(this.element, attributeName, this.getNamespace(), this._id);
return dcDom.findChildrenWithAttribute(
this.element,
attributeName,
this.getNamespace(),
this._id
);
}
}
50 changes: 50 additions & 0 deletions src/dc-deferred-loading.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import DcBaseComponent from './dc-base-component';
import dcFactory from './dc-factory';

/**
* This function returns wrapper componentClass
*
* @param {function} importFunction
* @param {string} nameSpace
* @return {typeof Component} wrapper for component class
*/
export default function dcDeferredLoading(importFunction, nameSpace) {
/**
* A wrapper class for deferred loading
* @class
* @implements {Component}
*/
class DeferredWrapper {
constructor(element) {
this.element = element;
}

getElement() {
return this.element;
}

init() {
importFunction()
.then(({ default: componentClass }) => {
this.actualInstance = new componentClass(this.element);
this.actualInstance.init();
})
.catch(error => {
console.error(
`An error occurred while loading the component ${nameSpace}`
);
console.error(error);
});
}

destroy() {
this.actualInstance.destroy();
}

static getNamespace() {
return nameSpace;
}
}

return DeferredWrapper;
}
38 changes: 19 additions & 19 deletions src/dc-factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,29 @@ class DcFactory {
constructor() {
/**
*
* @type {{componentClass: typeof DcBaseComponent, selector: string}[]}
* @type {{componentClass: typeof Component, selector: string}[]}
* @private
*/
this._registredComponents = [];

/**
*
* @type {WeakMap<HTMLElement, Map<typeof DcBaseComponent, ComponentState>>}
* @type {WeakMap<HTMLElement, Map<typeof Component, ComponentState>>}
* @private
*/
this._elementsComponents = new WeakMap();

/**
*
* @type {DcBaseComponent[]}
* @type {Component[]}
* @private
*/
this._instances = [];
}

/**
*
* @param {typeof DcBaseComponent} componentClass
* @param {typeof Component} componentClass
* @param {Function|string} selector that indicates how we should search that component elements
*/
register(componentClass, selector = null) {
Expand All @@ -68,7 +68,7 @@ class DcFactory {
/**
*
* @param {HTMLElement} element
* @param {typeof DcBaseComponent} componentClass
* @param {typeof Component} componentClass
* @return ComponentState
* @private
*/
Expand All @@ -80,7 +80,7 @@ class DcFactory {
/**
*
* @param {HTMLElement} element
* @param {typeof DcBaseComponent} componentClass
* @param {typeof Component} componentClass
* @param {ComponentState} state
* @private
*/
Expand Down Expand Up @@ -114,7 +114,7 @@ class DcFactory {
/**
*
* @param {HTMLElement} root
* @param {typeof DcBaseComponent} componentClass
* @param {typeof Component} componentClass
* @param {Function|string} selector
* @param {boolean} withLazy
* @private
Expand All @@ -140,7 +140,7 @@ class DcFactory {
/**
* Init component class on elements
* @param {HTMLElement} element
* @param {typeof DcBaseComponent} componentClass
* @param {typeof Component} componentClass
* @param {boolean} withLazy
* @private
*/
Expand Down Expand Up @@ -179,8 +179,8 @@ class DcFactory {
}

/**
* @param {DcBaseComponent} instance
* @param {typeof DcBaseComponent} componentClass
* @param {Component} instance
* @param {typeof Component} componentClass
* @private
*/
_onComponentCreated(instance, componentClass) {
Expand All @@ -193,7 +193,7 @@ class DcFactory {
/**
* @param {Error} error
* @param {HTMLElement} element
* @param {typeof DcBaseComponent} componentClass
* @param {typeof Component} componentClass
* @private
*/
_onComponentCreationError(error, element, componentClass) {
Expand All @@ -204,9 +204,9 @@ class DcFactory {

/**
*
* @param {typeof DcBaseComponent} componentClass
* @param {typeof Component} componentClass
* @param {HTMLElement} element
* @return {DcBaseComponent}
* @return {Component}
* @private
*/
_createComponentOnElement(componentClass, element) {
Expand Down Expand Up @@ -237,7 +237,7 @@ class DcFactory {

/**
*
* @param {DcBaseComponent} component
* @param {Component} component
* @private
*/
_destroyComponent(component) {
Expand All @@ -248,8 +248,8 @@ class DcFactory {
/**
* Returns all components of componentClass which are contained within the passed element
* @param {HTMLElement} element
* @param {typeof DcBaseComponent} componentClass
* @return DcBaseComponent[]
* @param {typeof Component} componentClass
* @return Component[]
*/
getChildComponents(element, componentClass) {
return this._instances.filter((instance) => element.contains(instance.getElement()) && instance instanceof componentClass);
Expand All @@ -258,8 +258,8 @@ class DcFactory {
/**
* Returns first found component of componentClass which is contained within the passed element
* @param {HTMLElement} element
* @param {typeof DcBaseComponent} componentClass
* @return DcBaseComponent
* @param {typeof Component} componentClass
* @return Component
*/
getChildComponent(element, componentClass) {
return this.getChildComponents(element, componentClass)[0];
Expand All @@ -268,7 +268,7 @@ class DcFactory {
/**
* Returns all existing components on the passed element. Just for debugging purpose!
* @param {HTMLElement} element
* @return DcBaseComponent[]
* @return Component[]
*/
debug(element) {
return this._instances.filter((instance) => instance.getElement() === element);
Expand Down
4 changes: 3 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import DcBaseComponent from './dc-base-component';
import dcFactory from './dc-factory';
import dcDom from './dc-dom';
import dcDeferredLoading from './dc-deferred-loading';

export {
DcBaseComponent,
dcFactory,
dcDom
dcDom,
dcDeferredLoading
}

0 comments on commit 01d5970

Please sign in to comment.