MEAN全栈开发[第二期-前端实战手记]

AirLoft的原型。 第二期, 基于Bootstrap的扁平化设计, 把所有涉及的页面的clickable prototype做出来, 再迁移到express application或者Angular SPA上, 虽然只是前端的prototype, 但是做好一些npm scripts的配置会让前端开发特别的高效和舒服, 也算是在入门了各种炫技的前端展示后, 逐步到一个full-fledged website的过渡。

project screenshot

Difference about MEAN stack

Traditional web server like Apache is multi-thread, the key idea about multi-thread is visitors use seperate resources.Like a bank system, it works perfectly fine when the population size is small, while when the size grows more than the resources, you have to queue for resources. (然而当你访问一个网站的时候, 你是不会等的, 你看那个网站响应很慢, 你就退出了). In nodejs, it is single-threaded. 所以你知道现在很多大型的企业架构, 尤其是国内的, 在前几年还特别严重, 一到过年过节流量特别大的时候, 你的网站就会宕机, 因为太多人访问了, 你必须等。所以解决办法是什么, 通常企业就回去买更多的服务器, 或者更新过大的内存, 来扛过流量最大的时候, 可是有一个问题就是, 在流量没有那么大的绝大多数时候, 你的资源被浪费了!

在单线程的nodejs里面, 有一个非常重要的特性就是必须是非阻塞, 因为默认的情况就是大家都会去同一个center office, 同一个线程, 然而我们不希望前一个的request, 比如request static files or database, 影响到下一个用户, 所以nodejs里面的函数都是异步的。

Some useful helper libraries like Underscorejs, Mochajs, and Colors(which can add color to the nodejs console log).

Check out the articles in Evernore, saying that why we don’t want to use the default way of using directory to organize our view static files in Expressjs, but instead use a function-focused way to group the files.

Mongodbd stores documents as BSON(binary format of JSON). Mongoose like the shell for mongodb, enable us to add data validation on database(schema)

Traditional way of doing web, is let the server do all the data processing and application logic, then passes HTML out to the browser. While angularjs allows us to pull some data and logic to the browser.(so server only pass the data from database). Since we have V8 in Chrome.

The difference of Jquery and AngularJS is that Jquery added to the page to provide interactivity, it assume that you have a complete DOM, While angularjs comes earilier to help you form the HTML.

关于SPA(single page application)的缺点, 尤其在SEO上, search engine only fetches the page layout and it won’t run any javascript, thus those data and contents generated by javascript will be ignored by search engine. _the key feature of SPA is that it takes a longer load for first time login, and it can reduce the pressure on server side, since it handle the logic mostly on browser. _ For example, for a blog website, the blog post entry is for people to read and is expected to show up quitely quickly, is not suitable for SPA, while the admin page can be done in SPA. Thus, for the blog entry, we want the original and traditional method(passing the HTML pages), utilizing Express!

Remeber, Angularjs is not only for SPA, it can also provide rich data interactivity to an static pages.

API-rich means decouple the reliance!!! You don’t contact directly to the database, but instead use API and if API is satisfied, components can be substituted by any other components.

基础的架构是: database\rest API\application在同一个server上, 但之后我们通常会把database分开到另外一个server上(avoid fighting for the same resources).

Express

When using express, we can configure it to have some options, like which html engine to use, which css preprocessor, and whether to add the support for session;

We can see in app.js that there are a lot of app.use(), they are middlewares, and the request coming from the application will go through each of them. For example, use app.use(cookieParser()); will parse out the cookie information from the request and prepare to be more friendly in controller code. Server side js(app.js) if modified, should let the server stop and restart, but the client-side js or html or css, can just use browser refresh to update on-the-fly. To fix that, we use nodemon, a module that allows auto-reloading when server-side javascript is changed. I have install it globally, so we just need to type nodemon inside the project folder.

block content in jade, is the inheritance machanism of HTML layout, we can define some block + in the parent template, then in each child layout that extends it, the child template will redefine such block, just like in c++\java, an abstract class.

We decided to implement MVC inside the Express, like what we did in rails, we route the corresponding URL to controllers(which we created in a new folder).

Bootstrap is not being processed by node engine, thus we put it under public folder. Inside the layout.jade file, we will include the bootstrap theme and js files we want to use in our project:

1
2
3
4
5
6
7
8
9
10
11
doctype html
html
head
# set viewport metadata for better display in mobile device.
meta(name='viewport', content='width=device-width, initial-scale=1.0')
title= title
# before the style.css file, so that we can write style to override or polish the style in bootstrap theme.
link(rel='stylesheet', href='/bootstrap/css/cerulean.bootstrap.css')
link(rel='stylesheet', href='/stylesheets/style.css')
body
block content

After deciding on the urls we want to use, we will add corresponding controller into the index.js inside the routes folder. since we mainly have two function groups, we use ctrlLocations and ctrlOthers to represent these two main controller files.

Jade in Express

Let’s start to move the content in view into controller. When we look at the clickable prototype, especially its Jade view pages, we define Javascript object in hash in controller to store the data. We design data structure to hold data for views. Then in view folder, we can do: [1] use = to use that object’s value, or use #{} to insert the value into a string.

1
2
h1= title 
p   #{title}

Pay attention to the format when using the programming syntax or inline coding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--注意在用synax的时候要多空4个space-->
.row
.col-xs-12.col-sm-8
.row.list-group
each mission in missions
.col-xs-12.list-group-item
h4
a(href='/mission')= mission.name
small &nbsp;
- for (var i=0; i<mission.rating; i++)
span.glyphicon.glyphicon-star
- for (var i=mission.rating; i<5; i++)
span.glyphicon.glyphicon-star-empty
span.badge.pull-right.badge-default 100m
p.author= mission.author
p
each tag in mission.tags
span.label.label-warning= tag
| &nbsp;

In Jade, we want to use mixin as functions inside a jada file and use include to make it reuseable for other file. Like this, we usually put mixin at between extends layout and block content.

1
2
3
4
5
mixin outputRating(rating)
- for (var i=0; i<mission.rating; i++)
span.glyphicon.glyphicon-star
- for (var i=mission.rating; i<5; i++)
span.glyphicon.glyphicon-star-empty
1
2
<!--same place, but using `include`-->
include _includes/sharedHTMLfunctions

Here is an example of showing how to render sting to HTML when the textarea has newline character:

1
2
3
4
5
6
7
8
9
10
extends layout

block content
#banner.page-header
.row
.col-sm-12
h1= title
.row
.col-sm-12
p !{(text).replace(/\n/g, '<br/>')}

For now, the data is stored in controller, while in practice, we need database and data model to store the data. But here we are in the right track of fast development, namely, from developing a clickable prototype, to move hard-cored data into controller as variables, and finally put them into database.

Heroku

Add the engines part at project’s package.json, add the node and npm version on it.

Add a Procfile, used to declare the process type used by application. we put web: npm start inside the Procfile so that it tells Heroku to tun such web process.

Use forego to test before push it into production environment. brew install forego to install forego, then do forego start and go to localhost:5000 to see the result.

Then if everything goes fine, from a) engines part b) Profile c) tested under forego, then we do heroku create and git push heroku master to push the local git repo to the remote heroku container. Then heroku open to open that url.

Terminology: Dynos are isolated, virtualized Unix containers, that provide the environment required to run an application. Generally, if you deploy an application for the first time, Heroku will run 1 web dyno automatically. In other words, it will boot a dyno, load it with your slug, and execute the command you’ve associated with the web process type in your Procfile. So, it mainly is about scaling, if we have more dynos, we will have more resources. And we got that 1 dyno for free. we can type in heroku ps to check which dyno is running which process and for this command, heroku ps:scale web=3 queue=2, it means that we can start 5 dynos, 3 for the web and 2 for the queue process types.

Bootstrap

in Bootstrap, there is always 12 columns, and you can define how many columns you want to use in device, col-sm-6 means that element will take up 6 columns on device of size sm and larger.

  • xs: phone
  • sm: tablets
  • md: laptop
  • lg: external monitors

<div class="col-xs-12 col-sm-6"></div> means it will take the full width in phone screen while take only half in tablets or larger.

In Jade, in order to put plain text, we need to use pipe at front.

1
2
3
4
5
6
7
8
9
ul.nav.navbar-nav.navbar-right
li
a(href='#')
span.glyphicon.glyphicon-user
| Sign Up
li
a(href='#')
span.glyphicon.glyphicon-log-in
| Login

When dealing with block content, we should note that if some elements are not contrained by css, then they will be strethed to have full width of screen. what we did now is do wrap the content with a div.container so that we can apply css on the container easily.

1
2
div.container
block content

现在的一个主要问题是, 我知道布局是什么样子的, 但是我不知道应该怎么写?when to use .row or .page-header.

  • row. Rows must be placed within a .container (fixed-width) or .container-fluid (full-width) for proper alignment and padding.
  • Use rows to create horizontal groups of columns.
  • Content should be placed within columns, and only columns may be immediate children of rows.col- is the direct child element for row
  • Actually .page-header just applies some of its default styling options to the element with that class. In practice, we find that the element with that class will be margined from normal element.

We can download the cutomized bootstrap theme from the website, say it I only want to use the glyphicon icon from Bootstrap, then I can just check that box and download it, then after unzipping the file, we include the file under the css part and inlcude the css tag inside the html, then it works fine! Hooray! In this project, I mainly use glyphicon-user, glyphicon-fire and glyphicon-chevron-left for navigation.

Using superslides to achieve a full-screen images scrolling animation. pretty amazing effects.

Use .panel for inner component positioning.

1
2
3
4
5
6
   .panel.panel-primary
.panel-heading
h2.panel-title Training Time
.panel-body
p March 15 - March 17: 7:00 am - 7:00 pm
p March 21 - March 23: 7:00 am - 7:00 pm

Additional reference

How to undo the git add <filename> before git commit. Use git reset <filename>. Include node_modules into .gitignore so that it won’t be commited to github. Use

1
2
3
#You can create your own .gitignore which ignores itself. Create a .gitignore with this two records:
.gitignore
node_modules

free bootstrap themes下载bootstrap theme到public folder.

Like Airbnb, how to load video at the background of a website, using progressive loading or ?

使用nodemon来监听server-side js文件的改动来自动更新server, 使用browser-sync来监听各种css file的改动。有两种模式, 一种是通过直接创建一个server来host本地的static file, 另一种模式是通过代理另一个服务器, 比如php server, rails webbrick server, python simplehttpserver, nodejs server都可以的!!!!

1
2
3
browser-sync start --proxy='localhost:3000' --files 'app_server/views/*.jade, app_server/controllers/*.js, public/stylesheets/*.css'

browser-sync start --server --files 'app_server/views/*.jade, app_server/controllers/*.js, public/stylesheets/*.css'

由于我在nodejs里面设置了服务器的端口是3000, 那么通过让browser-sync监听这个端口并创建在3001的代理, 我可以在localhost:3001上面得到自动更新的网页!!

一起加油!