This is the story about a project of mine. A big one. A mixture between PHP and Node.js.
It's a single page application from one point of view and an SEO
optimized website from another. Tons of JavaScript, CSS and HTML was
written. In a single word, a spaghetti nightmare for any developer.
There were falls and rises. Producing and solving bugs. Fighting with
the latest technologies and ending up with a wonderfully simple library,
which is the topic of this article.
The Beginning
As it normally happens, the project was considered not so big. The
brief came in, we discussed how the development would be handled, what
technologies would be used and how we will use them. We made a plan and
got to work. In the beginning we had a few pages, which were controlled
by a CMS. There wasn't so much JavaScript code at first because our
system delivered most of the content.
Here is a rough structure of the project:
We put our client side code into different directories. The server side code was only PHP at the moment, so it went in to the php directory. We wrap everything into around 30 files and everything was OK.
The Journey
During the period of a few months, we were trying different concepts
and changed the project's code several times. From the current point of
view, I could spot four big issues which we met.
Problem #1 - Too Many Badly Structured Files
It looks like the client was happy with the result and decided to
invest more in his Internet appearance. We were asked to build a few new
features. Some of them were just new content places, others were
additions to already existing pages. We started adding more and more
files in all of the folders above. It started to get a little bit messy,
so we decided to create subdirectories for the different pages and save
the necessary code there.
For example, the CSS styles for the about page were in css/about/styles.css. The JavaScript in js/about/scripts.js
and so on. We used a PHP script which concatenates the files. There
were, of course, parts of the site that were on several pages and we put
them in common directories. This was good for a while, but
it did not work well for long because when the directories became full,
it was slow to modify something. You had to search for three different
directories to find what you needed. The site was still mainly written
in PHP.
Problem #2 - The Big Turning Point or How We Messed Things Up
At that time, mobile applications became popular. The client wanted
to have his site available for mobile devices and this is the big
turning point in the project. We had to convert the site to a single
page application. And not only that, it had to have tons of real-time
features. Of course, not all the content of the site had to be loaded
dynamically. The SEO was still an important part of the client's vision.
We chose the MEAN stack
for the upcoming parts. The problem was with the old pages. Their
content had to be served by PHP, but the pages' logic changed and it was
completely made with JavaScript. For several weeks, we felt like the
passengers of Titanic. We were in a hurry to release something, but
there was hole after hole and very soon, our ship was full of water
(bugs).
Problem #3 - A Tough Working Process
We used GruntJS for a while, but migrated to Gulp.
It helped a lot because we increased our development speed. However, it
was still too annoying to add or edit existing components. The solid
architecture that we had in the beginning was transformed to a complex
mixture of files. Yes, there was strict conventions for naming and
placing these files, but it was still too messy. We then banged our
heads together and came up with the following format:
We split the site in to different components, that were like black
boxes. They live in their own folder. Everything related to the
component was saved inside its directory. We designed carefully, the
APIs of the classes. They were testable and communicative. We found that
a structure such as this worked better for us because we had tons of
independent modules. Yes, we are mixing the JavaScript files with CSS
styles and HTML templates, but it was just easier to work on a unit
basis, instead of digging deeply into several directories.
Problem #4 - Angular vs. Custom Code
Those pages which were old and which we had to deliver via PHP, were
also full of JavaScript logic. However in some cases, Angular did not
work very well. We had to make small hacks to make the things run
smoothly. We ended up with a mixture between Angular controllers and
custom code. The good news was that the budget of the project was
expanded and we decided to use our own framework. At that time, I was
developing my own CSS preprocessor.
The project goes really, really fast. Very soon I ported my library for
client side usage. Line by line, it was transformed to a small
framework, which we started integrating into the project.
Why Create a New Framework?
This is probably what you're asking. Well, there is a dozen of others
that provide a wide range of capabilities. Yes, that's true, but ... we
did not need a wide range of capabilities. We needed specific things
and nothing more. We were ready to accept the fact that by using a
popular framework, we may add a few kilobytes to the overall page load.
That wasn't a big problem.
The status of our code-base was the
issue. We were focused on building good architecture and we all agree
that sometimes the custom solution fits better. The usage of Angular,
Ember, Knockout or Backbone comes with its benefits, but the truth is
that there is no universal framework.
I like the words of Jeremy Keith, in his talk The power of Simplicity,
he said that the most important thing while choosing your tool is the
philosophy of the person who made the tool and if that philosophy aligns
with yours. If the ideas of the framework do not align with yours, very
soon, you will go against them. The same thing happened to us. We tried
using Angular and there were too many difficulties. Problems that we
were able to solve, but we used hacks and complex workarounds.
We
also tried Ember, but it did not work, because it is heavily based on
its routing mechanisms. Backbone was a nice choice and it was the
closest thing to our vision. However, when I introduced AbsurdJS we decided to use it.
What AbsurdJS Did For Us
AbsurdJS was originally started as a CSS preprocessor, expanded to an HTML preprocessor
and it was successfully ported for client side usage. So, in the
beginning we use it for compiling JavaScript to HTML or CSS. Yes, you
heard me right; we started writing our styles and markup in JavaScript
(probably sounds strange, but please keep reading). I pushed the library
forward and a dozen of functionalities were added.
Divide and Rule
When you have a complex system, with many pages, you really don't
want to solve big problems. It is much better to split everything into
smaller tasks and solve them one by one. We did the same thing. We
decided that our application will be built of smaller components, like
so:
1
2
3
4
5
6
7
varabsurd = Absurd();
varMyComp = absurd.component('MyComp', {
constructor: function() {
// ...
}
});
varinstance = MyComp();
absurd.component defines a class. Calling the MyComp() method creates a new instance.
Let's Talk to Each Other
Having all these small components, we needed a channel for
communication. The observer pattern was perfect for this case. So, every
component is an event dispatcher.
01
02
03
04
05
06
07
08
09
10
varMyComp = absurd.component('MyComp', {
doSomething: function() {
this.dispatch('something-happen');
}
});
varinstance = MyComp();
instance.on('something-happen', function() {
console.log('Hello!');
});
instance.doSomething();
We are also able to pass data along with the message. The definition
of the components and their "listen-dispatch" nature is pretty trivial. I
adopted this concept from the other popular frameworks, because it
looks natural. It was also much easier for my colleagues to start using
AbsurdJS.
Controlling the DOM
Along with the PHP served markup, we had dynamically created DOM
elements. This means that we needed access to the existing DOM elements
or new ones, that will be later added to the page. For example, let's
say that we have the following HTML:
1
2
3
4
<divclass="content">
<h1>Page title</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
</div>
Here is a component which retrieves the heading:
1
2
3
4
5
6
7
absurd.component('MyComp', {
html: '.content h1',
constructor: function() {
this.populate();
console.log(this.el.innerHTML); // Page title
}
})();
The populate method is the only magic method in
the whole library. It does several things like compiling CSS or HTML,
it binds events and such things. In the example above, it sees that
there is an html property and initializes the el
variable which points to the DOM element. This works pretty good for us
because once we got that reference, we were able to work with the
elements and its children. For those components that needed dynamically
created elements, the html property accepts an object.
01
02
03
04
05
06
07
08
09
10
11
12
absurd.component('MyComp', {
html: {
'div.content': {
h1: 'Page title',
p: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
The JSON above is translated to the same HTML markup. I chose JSON
because from a JavaScript point of view, it is much more flexible. We
are able to merge objects, replace or delete only parts of it. In most
of the popular frameworks, the templates are just plain text that makes
them difficult for manipulating. AbsurdJS also has its own templating engine.
The this keyword in the expressions above, points to the component itself. The code between <% and %>
is valid JavaScript. So, features like computed properties could be
easily developed directly into the template's definition. Of course, we
are able to use the same template engine with already existing markup.
For example:
... could be controlled with the following component (the result is the same):
1
2
3
4
5
6
7
8
absurd.component('MyComp', {
html: '.content',
title: 'That\'s awesome',
availableFor: ['all browsers', 'Node.js'],
constructor: function() {
this.populate();
}
})();
Anyway, the point is that we were able to define templates or create
such from scratch. We are also able to control the data that is injected
in an easy and natural way. Everything is just properties of the good
old JavaScript object.
What About the Styling?
We successfully split the whole system in to small modules. The parts
that were before Angular controllers, became AbsurdJS components. We
realized that their HTML was tightly attached to their definition, that
completely changed the management of the markup in the application. We
stopped thinking about the concatenation, conventions or anything like
that. We did not have to create HTML files at all. When I look back, I
could see this exact moment in our commit history. It is easily visible
because many files were removed from the code-base.
Then I thought, what will happen if we do the same thing with the
CSS. It was of course possible because AbsurdJS was a CSS preprocessor
and could produce CSS. We just got the compiled string, create a new style tag in the head of the current page and inject it there.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
absurd.component('MyComp', {
css: {
'.content': {
h1: {
color: '#99FF00',
padding: 0,
margin: 0
},
p: {
fontSize: '20px'
}
}
},
html: '.content',
constructor: function() {
this.populate();
}
})();
Here is the style tag which is produced:
01
02
03
04
05
06
07
08
09
10
<styleid="MyComp-css"type="text/css">
.content h1 {
color: #99FF00;
padding: 0;
margin: 0;
}
.content p {
font-size: 20px;
}
</style>
And day by day we transferred the CSS styles from the SASS files
(because, at some point, we chose SASS as a CSS preprocessor) to the
AbsurdJS components. To be honest, it was pretty easy because all the
mixins and variables which we have, were defined as JavaScript functions
and variables. The sharing of the styles was even easier because
everything was JavasSript.
That Awkward Moment
... when everything works perfectly but you feel that something is wrong
We were looking at the code. It worked. AbsurdJS drove even the old
parts. The new stuff uses the same library. The HTML and the CSS were
nicely separated and placed directly into the components' definition.
However, I felt that there was something wrong. I stopped for a while
and asked myself: "What is the Web made from?".
And what we did, is a little bit different. It looks more like the picture below.
I've been building websites for more than ten years and I remember
the times when we all fought for the big separation of these three
building materials. And what I did in this project is exactly the
opposite. There was no CSS and HTML files (almost) at all. Everything
was JavaScript.
Many people will say that this is ridiculous and
we should give the client's money back. Yes, this could be true, but
this concept worked perfectly in our case. We did not write an
application. In fact, we wrote a bunch of independent components. I
believe that the Web will be a combination of ready-to-use components.
We,
as developers, will have to develop such components and probably
connect with and use such components written by others. Projects like AbsurdJS or Polymer are showing that this is possible and I encourage you to experiment in this direction.
Advertisement
Back to Reality
So in the end the client's business went well. It was so good that he
decided to launch a new service. And guess what. He wanted some parts
of the existing application transferred into the new project. I can't
tell you how happy we were to move components from one place to another.
We did not have to setup something, copy HTML markup or CSS files. We
just got the JavaScript file of the component, placed it somewhere and
created an instance of it. It just worked because there were no
dependencies. I'd not be surprised if some of these components are put
up for sale very soon. They are pretty light and provide nice
functionality connected with the client's product.
Yes, we broke some rules. Rules that I personally agree with. Rules
that I followed for many years. However, the reality is that we all want
quality and sometimes that quality is reachable by breaking the rules.
We want to produce good, well structured code which is easily
maintainable, flexible and extendable. We do not want to look back and
say, "Oh my gosh ... was that written by me!?". When I look back now, I
know why the code looks the way it does. It looks like that because it
was written for that project specifically.
Conclusion
If you found this tutorial interesting, check out the official page of AbsurdJS. There are guides, documentation, and articles. You can even try the library online.
Like every other tool, AbsurdJS is designed for specific usage. It fit
well for our project and may fit for yours. I don't even call it a
framework, because I don't like this definition. It's more like a
toolbox rather then a full stack framework. Feel free to experiment with
it, make pull requests or submit issues. It's completely open source
and available at GitHub.
Unknown
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation.
Medical Disclaimer
The information on this site is not intended or implied to be a substitute for professional medical advice, diagnosis or treatment. All content, including text, graphics, images and information, contained on or available through this web site is for general information purposes only. Krobknea makes no representation and assumes no responsibility for the accuracy of information contained on or available through this web site, and such information is subject to change without notice. You are encouraged to confirm any information obtained from or through this web site with other sources, and review all information regarding any medical condition or treatment with your physician. NEVER DISREGARD PROFESSIONAL MEDICAL ADVICE OR DELAY SEEKING MEDICAL TREATMENT BECAUSE OF SOMETHING YOU HAVE READ ON OR ACCESSED THROUGH THIS WEB SITE.