What every developer should know about developing SPAs
Hang out in any front-end web development chatroom for even a short period and you’ll come across the same set of “help me” or “how do it?” questions over and over again. They are not difficult issues once you have learnt about them but until you do and while you are learning frontend development they are often a source of confusion and frustration.
So I thought I’d try and cover a few of the common issues that seem to come up repeatedly …
Seriously, the first thing to know isn’t anything technical at all, just a little common-sense. If you are asking for help, you are asking complete strangers to give up their time and energy to help solve your problem. No one is obligated to do that and people often have their own work to be getting on with so don’t be impatient or demanding.
No one is obligated to provide you with free 1:1 tuition or training so don’t demand help by inviting people to private chat or direct-messaging them unless they have offered this help. If no one is answering you, don’t just keep spamming the room with the same question - maybe it’s unanswerable or no one is available who knows about the subject.
There maybe no wrong questions but there are definitely wrong ways to ask questions. You will get better responses if you make clear you have put some effort in solving things yourself first and are not just looking for other people to do your work for you. So, explain what you are seeing, what you expect to see instead, what errors you are getting and what you have tried already to solve things and you will dramatically increase your chances of getting a positive, helpful response.
It’s not uncommon for the immediate issue we’re facing to be the result of a fundamentally wrong approach, especially when learning something new. So, don’t get angry if someone tries to explain that you are doing something wrong and would be better approaching it another way. You should be more concerned with learning how to do things right than just making an error message go away and if someone is willing to take the time to explain how and why, you should be grateful and take advantage of it instead of arguing.
It should go without saying that insulting people or the technology choices they have made isn’t going to make anyone enthusiastic to help you. Technology can be frustrating sometimes, but there’s no excuse for personal insults, denigrating someone else’s skills or bad language.
You are unlikely to get much help if your only description of an issue is “it’s not working”. Browsers like Chrome now come with incredibly rich and useful developer tools that will help you debug and analyze any issue you might be having and there is simply no excuse not to use them.
So, have you opened the developer tools and checked the console for any error messages as a first step?
If the console tells you that it “cannot read property ‘whatever’ of undefined” then you probably have a javascript scope issue. Don’t just rely on other people giving you an immediate fix to make the problem go away (usually either bind
or using a lambda expression / arrow function) without understanding what the issue is. Do a google search, read the results and learn how the platform and language you are trying to use work.
Learn to set breakpoints in code and inspect things. Posting an error message or stack trace to a chat room (or worse, a screenshot of an error message or stack trace) isn’t going to help - no one can debug your code remotely via chat unless it’s something really obvious (in which case, learn what those obvious things are to save you time).
Are all the files even loading? That means more than just something loaded, does is have the correct content? If your javascript script is complaining that a ’<’ character is unexpected then there is a good chance it’s not javascript that it’s loading, it’s really HTML (maybe a “not found” page, because of the next issue). The network tab lets you inspect things like this.
That “HTML being loaded instead of script” issue is just one symptom of the misunderstanding around how routing works with Single Page Apps. The other problem you can run into is that you build the site, it works when you start at the home page but if you are on any other page and press “refresh” you get a 404 error.
Here’s why …
With a traditional server-rendered web-site, the browser sends the URL to the server and the server responds with the content for that page. But when you are writing a SPA, that isn’t how things work. What needs to happen is that the server sends the “index.html” page that loads the app for pretty much any request it receives that shouldn’t be handled as something specific (typically images, scripts and other assets that make up the site).
This is often referred to as a 404-redirect but it’s not really a redirect, it’s a re-write. You need the URL to stay as it was in the browser and the server to serve-up the index.html
page content for all requests that match the page routes in your app. The routing logic within your client-side code will then look at the URL in the browser and decide which view to load and render.
If you are developing locally and your development web-server doesn’t handle this re-writing for you then you should learn to configure it so that it does (most tools support it, if not find a better tool). Some frameworks provide tools with it built in - with Polymer’s CLI tool for example you just run polymer serve
and it handles everything automatically.
Remember - any server that your code is going to run on will need to be configured to behave the same way and that includes production. Now there was another solution that avoided doing this which was to move the “route” information to use the hash part of the URL instead of the path.
This meant that any page request was always to the root index.html
page and the server only served out that single page from the same URL path. If you have a server that doesn’t provide any re-writing then the hash-routing might be your only option but you should try to use proper path based routing if you can for SEO and other technical reasons.
Don’t “fix” the issue locally by switching which one you are using unless you can run with the same system in production when you deploy your app and plan on using proper path-routing if you can because it’s better, even if it might seem harder to get started with than hash routing.
Understanding how URL paths work becomes important when your app page content could be loading from different paths. You should understand all the pieces of a URL, the protocol, host, path and so on but especially the path part.
You may be used to loading everything from a full absolute URL such as:
http://www.example.com/my-folder/my-page.html?filter=published#section-2
This works great, but at some point you will have code or a component that can’t and shouldn’t know about the entirety of the app and instead just needs to reference something such as a script or image relative to itself.
This is when you’ll use a relative reference and it’s especially common with web-components where they need to reference other components but can’t know anything about the app they are being used with other than that the component they need will be a sibling to whatever folder they are in.
So, they will have a reference such as "../polymer/polymer-element.html"
. This means that a component loaded from /bower_components/my-elements/my-widget.html
would be going “up” a level from the my-elements
folder to bower_components
and then down into the polymer
folder to load polymer-element.html
. It doesn’t matter if someone decides to call their folder “components” or “vendor” or anything else - the relative path works.
Remember, the path is relative to where things are going to be loaded from at runtime, not where they are on disk while you develop them. A common problem is mixing and matching things - it’s easy to “solve” a relative path issue by making some references root-relative (e.g. “/bower_components/polymer/polymer-element.html” but if a different folder is used or the app needs to load from a sub-folder instead of the root, this isn’t going to work. It will also lead to other issues where files that should be seen as the same and already loaded are not, because they are referenced from a different URL, so code that should only load and run once is duplicated and causes errors.
Remember to use the network tools tab to check what is being requested, that things are not being loaded from different URLs and that things are loading successfully.
Any browser version only understands the technology and language that existed when they were developed. That means for instance that IE11 or older Safari, FireFox and Chrome browsers will only knows about es5, the version of javascript that existed when they were developed.
It doesn’t matter that you read that es6 (or es2015 as it’s also known) is “the future” and it’s easier to use, if you want your site to run on a browser that only understands es5 you need to give it es5 to execute. It’s a bit like sticking a bluray disc into a machine that only understands DVDs, it just ain’t going to play because it just doesn’t understand it.
There are two ways to approach this. You can stick to writing everything in es5 directly until there are no more users that use browsers that don’t support the newer versions but this means you are letting the “can’t or won’t upgrade” users dictate the tools and technology you can use in your development which is very limiting (many “how to” guides provide examples in newer javascript syntax). Alternatively, you can develop using the newer javascript version and use build tools that “transpile” you code back into something that the older browsers will understand and can run.
Again, your framework tooling may do this for you out of the box - Polymer’s CLI can transpile to es5 for you if you want to target those browsers. Many use packages such as “babel” which can convert code between different versions of javascript so that you can write and develop using the latest language features. You can even chose to use “new” JS-related languages like Typescript (or Coffee-script if you are insane) and the transpiler will convert it into runnable JavaScript for you.
Incidentally, you may be thinking “I don’t care about users with old browsers, I’m just going to use es6 and serve that”. That’s definitely an option and it works (there are some technical reasons for wanting to serve es6 to a browser that supports it instead of es5). But I have bad news for you - one of the most important users that is ever going to visit your site still insists on using es5. Who? Google, that’s who. Yes, the google-bot currently understands es5 only so you still need to support it for now if you care about SEO even if you don’t care about any of the people with IE11.
As well as transpiling the source code to a different javascript version, you will also want to reduce it’s size if you can to improve runtime performance. This is called “minification” and is another type of transpilation. It might take your code that has understandable variables such as var hasCompanyBeenActivated = false;
and convert it to something smaller but equivalent, like var a=0;
(with any references also updated). Fewer bytes mean faster loading pages which is great.
But oh dear, you then have an issue and instead of the beautiful code you wrote you instead have a wall of hieroglyphic looking JS to try and debug.
No, you don’t.
JavaScript and CSS have a feature called “SourceMaps” which map the code that is executing back to the original sources. This will enable you to debug, step through and examine the code as though it was the original source even in a browser that doesn’t support that source language directly. Again, you need to enable it in your build process and make sure that you have “source maps” enabled in your dev tools. You don’t need to deploy the source-maps to production - you can keep them locally as long as you tell the browser where to find them.
There is definitely a lot of things to learn to work in frontend web development and it’s changing all the time. Learning the basics and setting some time to learn about the developer tools, javascript scope and so on will really pay-back the time investment in the long-run.