This is a super simple JavaScript framework built for traditional websites. It works, but it's basic and needs a lot of attention.
I needed a Framework that worked for traditional websites, the only modern tools I see released these days are that of Single Page Application Frameworks, libraries such as Angular, Ember and React. These Frameworks are amazing, but they don't help us very much if we want to build a Drupal site with server rendered templates.
- OOP
- Modern ES2015 JavaScript, especially modules
- Automatic separation of above and below fold execution, optimising for the fastest page load possible
- Simplified asynchronous loading of scripts only when a component exists on the page
- Effortless DOM bindings provided by a simple data attribute which contain a direct path to the JavaScript module itself! No more confusion about where JavaScript is being executed from and on which element.
- Fully encapsulated component logic, save for hooks that allow them to communicate in a clean and controlled manner
- config - a place for all of your configurations files
- core - in charge of module loading and base classes for modules
- modules - this is where the majority of your code will exist
- priority - immediate execution for above fold content such as top navigation menus or hero banners
- common - concatinated group of below fold, common components such as footers, articles and sidebars. This file is asynchronously downloaded and executed immediately after priority
- demand - chunk separated bundles which will split when the bundle's file size reaches a limit. These less common components are asynchronously downloaded only on existance
- services - a collection of utility modules
- Need to add a function to recollect elements that have been added dynamically after page load
- I have left styling out of this project (for now) but I could do with some suggestions with regards to automating above and below fold css compilation. Like styles compiled from priority might be used as critical render blocking css but then everything from common and demand gets concatinated and asynchronously loaded in later.
- Better naming conventions, better architecture, better error handling, better documentation etc
You'll need to download and treat this project like scaffolding.
yarn install
yarn run dev
yarn run build
<!-- Application will look inside modules/priority for Header.js -->
<header data-module-priority="header"></header>
<!-- Application will look inside modules/demand for vue/App.js -->
<div data-module="vue/app"></div>
<!-- Application will look inside modules/common for Footer.js -->
<footer data-module-common="footer"></footer>
All logic that needs to execute before any module is loaded should go here.
Logic that needs to execute after all the modules is loaded should go here.
Any third party script that might have a global effect can be broken down here.
This is the bare minimum needed for your new module.
import Context from "Context";
export default class extends Context {
// This function is the entry point for each module
init() {
}
}
A list of available properties.
This is the raw DOM node that you attached the data-module(-*) attribute to.
this.width = this.node.style.width;
This is the jQuery selected DOM node
this.height = this.element.height();
This contains common DOM nodes such as window, document, body etc... Cached into a single object
// jQuery selected window element
this.window_height = this.dom.window.height();
// 768px
this.media_query = this.break_point.small;
A list of available actions.
Decorated console.log message. Disabled outside of local development
error(error) {
this.debug('error logging', error);
}
// Result
[APOCALYPSE LOG - module_name] error logging [Object]
Allows us to effectively communicate with other components
// example component
speakToFriend() {
this.broadcast('notify', 'Is there anybody out there!');
}
This method allows us to receive broadcasts from other components.
// another component
constructor(params) {
super(params);
// specify broadcast
this.messages = ['notify'];
}
...
// listen for broadcasts
onmessage(name, data) {
switch(name) {
case 'notify':
// data = 'Is there anybody out there!'
this.notified(data);
break;
}
}
This combination enables users with accessability needs to engage with our components va the keyboard.
returns true IF the action was a mouse click OR the SPACE/ENTER key was pressed.
bindEvents() {
this.element.on('click keydown', this.eventFired);
}
eventFired(event) {
if (!this.accessible(event))
return;
...
}
A list of available methods
Where it all begins
init() {
this.bindEvents();
...
}
Where it all (should) end
destroy() {
super.destroy();
this.thirdparty.kill();
...
}