前言
作為一名前端開發者,刀耕火種的年代隨著 NodeJS 等工具的出現,已經一去不復返了。如果你還停留在寫著冗長的HTML代碼,不斷重復著復制粘貼,那么你應該繼續學習了。
之所以寫這篇文章,是源于前段時間我的一個Github個人主頁項目。因為是要放在Github上的,所以只能是靜態站點,那么所有靜態頁面如果一個一個手寫的話,是很痛苦的。沒有了后臺程序,如何去定義模板文件?總不至于每個頁面中都寫上相同的 header 和 footer 吧。可以使用 Sass 去寫 CSS 嗎?可以使用 CoffeeScript 去寫 JavaScript 嗎?可以使用 Markdown 去寫HTML內容嗎?可以使用 Jade 去寫HTML結構嗎?
帶著這些疑問,我將一步一步實現一個自動化的前端靜態站點項目。
環境準備
Mac 或 Linux,不推薦Windows下開發,因為你會遇到很多蛋疼的問題,從本質上來說,你應該開始在Linux下做開發了。
如果你像我一樣還在使用 Windows,可以安裝一個Ubuntu虛擬機,然后通過 Samba 共享文件,用 SSH 和 Windows做連接,最終就能實現在你熟悉的 Windows下做開發(編輯文件),而運行環境卻是 Linux。
1、創建項目
不管做什么開發,我們都應該遵循一定的規范,創建項目同樣要注意文件夾的名字和結構。首先創建一個如下的基礎項目結構,暫且就將項目命名為 auto-web:

簡單解釋一下:
/src 源文件所在地。開發過程中的各種CSS和JS源文件都往這里面放。
/dist 目標文件所在地。上線前通過工具生成的CSS和JS目標文件都輸出到這里。
/images 圖片文件。
/vendor 第三方的 CSS、JS 和字體文件都存放到這里。
/views HTML 視圖文件。
當然目前我們是手動創建的,可以設想一下,利用NodeJS,我們是完全可以做到一條命令創建出這樣的文件夾結構。
2、從 index.html 開始
首先我們習慣性地創建一個 index.html 頁面
<!DOCTYPE html><html>
<head>
<meta charset="utf-8" />
<title>Hello world</title>
</head>
<body>
<h2>這是我的個人主頁</h2>
</body></html>
3、我想用 Jade
什么是 Jade ? - Jade 是 Node 的一個模板引擎,一句話就是讓我們能夠更快更簡便地寫HTML,大家可以去 Jade 的 官網,由于其主要是靠縮進來定義結構,所以一般都能夠很快地學會這種寫法。
類似的模板引擎在 Rails 里面也有一個叫 Slim 。當然有很多開發者是不喜歡用類似的方式去開發,不過我們這里僅作為一個示例,具體用還是不用取決于你自己。
OK,咱們來將 index.html 換成 Jade。
首先,咱們在 /views 文件夾下創建一個 index.jade 文件,然后輸入下面的內容:
doctype htmlhtml
head
meta(charset="utf-8")
title Hello world
body
h2 這是我的個人主頁
這和上面的 index.html 是完全一樣的。那么我們可以對比一起兩種寫法:

在 HTML 代碼量越大的情況下,Jade 的寫法就越有優勢了。當然還是那句話,用與不用都取決于你自己的愛好。
OK,到此為止,我們已經寫好了一個 HTML的模板引擎。
直接點擊 index.jade 能運行嗎?肯定是不行的。在這里我得強調一下,我們所要做的額外的工作,不是為了運行項目,而是為了開發項目。就好比我們都要去北京,只不過你是走路去的,而我是坐飛機去的,目的一致,優化了開發流程而已。
明白了這一點,我們就知道,應該將 index.jade 轉換成 index.html 了。
該如何轉換呢?這必然得用上工具了,咱們需要一門后臺語言的命令行工具,Ruby?Python? 對,他們都能完成這份工作。但我們是前端,自然最佳選擇就是 NodeJS 了。
4、該 NodeJS 出場了
相信大家也都對 NodeJS 有過一定的了解,咱們先不要求精通,只要知道怎么使用即可。
首先咱們去 NPM 中查找要用到的 jade 包,找到 mmand Line 命令行,首先按照文檔說的安裝(必要時前面加上 sudo):
$ npm install jade -g
這里的 npm 是 Node 的一個包管理器,也就是通過類似上面的命令去安裝程序中需要用到的包。
然后通過 jade --help 可以看到如何將一個 Jade 文件轉換成 HTML:
jade views/index.jade -o ./
這樣就能在我們的項目根目錄下生成一個 index.html 了,而且它的內容是壓縮過的。
好像還不錯,咱們可以書寫 *.jade 文件,然后通過命令生成 *.html 。
5、母版頁
網站不可能只有一個頁面,但是我們肯定會有公共的頭部和底部,不可能每個頁面都去寫一遍。
有兩種方式達到這種目的,一種是母版頁,通過占位符和填坑的方式實現,也就是接下來我們要講的。還有一種是在每個頁面中去引入公共部分。當然得根據自己的需要來決定了。
首先我們在 views/ 下創建一個 layouts/layout.jade 母版頁:
doctype html
html
head
meta(charset="utf-8")
title Hello world
body
header
| 這是頭部
block con
footer
| 這是底部
注意這里的 block con 是關鍵,意思是創建一個標識為 con 的坑,等著其它具體頁面來填內容。
那么該怎么填內容呢?我們來改寫views/index.jade 文件
extends ./layouts/layout.jade
block con
div 這是主頁
首先是通過 extends 引入模板文件,然后再在 block con 下面去填充首頁的具體內容。
這樣,以后添加任何新頁面都可以通過上面的格式來了,不用去管公共部分。
6、該寫 CSS 了
頁面沒有樣式是不行的,那么我們接下來就來寫樣式文件。不過我們這里希望用 Sass 來寫。
Sass 是一個 CSS 預處理器,簡單點說就是用另外一種方式去書寫 CSS(增加了包括變量、嵌套等等新寫法),最終同樣通過工具轉換成原始的 CSS文件。不了解的同學可以大致看一下 官網,也都是很容易學會的。
好了,咱們在 /src/sass 中創建一個 main.scss 文件:
body{
background-color: #EEE;
h2{color: Red}}
作為演示,咱們這只用到了其中的嵌套規則。
上面說到,*.scss 也是不能直接引用的,需要先轉換成 *.css。怎么轉換呢?首先還是安裝 Sass:
gem install sass
然后執行下面的命令將 /src/main.scss 輸出到 /dist/css/main.css 中
sass src/sass/main.scss dist/css/main.css
大家可以看到,在 /dist/css/ 中實際上生成了 main.css 和 main.css.map 兩個文件,當然咱們可以不用管 main.css.map 這個文件,因為我也沒去看到底這個文件有什么作用。
OK,大功告成。接下來我們就在 views/layouts/layout.jade 中將 main.css 引入。
修改 views/layouts/layout.jade:
doctype html
html
head
meta(charset="utf-8")
title Hello world
link(href="dist/css/main.css" rel="stylesheet")
body
header
| 這是頭部
block con
footer
| 這是底部
然后重新執行
jade views/index.jade -o ./
打開新生成的 index.html 文件,是不是應用上了我們的樣式了?
7、該寫 JavaScript 了
同樣滴,JavaScript 也有自己的預處理,那就是 CoffeeScript 了。這樣你就可以寫 JavaScript 寫得很爽了。不了解的同學可以看看 教程 很簡單。
類似上面的 CSS,首先在 src/coffee 下創建一個 main.coffee 文件,然后寫上測試代碼:
hello = ()->
console.log("your coffee is work")
hello()
然后
npm install -g coffee-script
接著編譯 src/*.coffee 到 dist/js/*.js:
coffee -c -o src/coffee/main.coffee dist/js/
這里在測試中好像是不能輸出來的,不過文檔是這樣寫的,先不用管這個,暫時可以直接將其編譯到當前目錄下,然后手動復制到 dist/js/*.js 下面:
coffee -c src/coffee/
接下來我們就在 views/layouts/layout.jade 中將 main.js 引入。 修改views/layouts/layout.jade:
doctype html
html
head
meta(charset="utf-8")
title Hello world
link(href="dist/css/main.css" rel="stylesheet")
script(src="dist/js/main.js")
body
header
| 這是頭部
block con
footer
| 這是底部
然后重新執行
jade views/index.jade -o ./
打開新生成的 index.html 文件,F12看看是不是輸出了 your coffee is work。
8、該創作了
估計很多同學都想利用 Github 個人主頁(下面會講到)來寫博客,如果是有后臺程序,那么一般是將博客內容存到數據庫中,然后在一個指定的頁面中讀取顯示出來。
那么全靜態站點該如何做呢?我們沒有數據庫。的確,但是我們仍然可以把博客內容剝離出來,放到一個單獨的文件中,然后通過命令去生成單個的博客頁面。
這里我們使用 Markdown 格式來寫內容,我們新建一個文件夾 views/blog/ 文件夾用于存放所有博客文件。首先我們新建一個 views/blog/index.jade 的頁面用于顯示文章:
extends ../layouts/layout.jade
block con
article
include:marked hello.md
注意這里是通過 include:marked 指令來將 Markdown 文件引進來并解析成 HTML,這是 Jade 為我們提供的功能。
然后創建一個 hello.md 的文章:
### 這是標題
這是內容
[鏈接文字](鏈接地址)
最后就會生成一個 blog/index.html。
這里我們是把 include:marked hello.md 寫死了,事實上我們是要實現在 views/blog/ 下添加很多的 Markdown 格式的博客內容文件,然后執行 gulp 即可在 blog/下面生成對應的 HTML 文件。
大家可以思考一下這個問題,我這里就不做了,也許會在后面的文章中來進行補充。
到此為止,整個項目已經初具成效了。
那么我們每次修改內容的時候,都得去執行相應的命令,是不是有點繁瑣呢?程序員一定要懶,能程序做的就別自己手動去做。那么有這種工具可以幫我們完成這一系列動作嗎?
9、自動化構建
所謂的自動化構建就是將多個開發中的操作整理到一起,簡化我們的整個開發過程。簡單點說就是配置一系列的命令語句,然后一行命令就搞定所有的CSS、JavaScript等處理工作。
當然構建工具的選擇還是蠻多的(查看 完整列表)。這里我們選擇的是 gulp,因為使用和配置都比較簡單。
首先是安裝 gulp
npm install --global gulp
然后在你的項目根目錄下創建一個 gulpfile.js 文件,內容如下:
var gulp = require('gulp');gulp.task('default', function() {
// place code for your default task here});
然后執行
gulp
可以看到默認的任務執行了,只是沒有做任何工作:
[14:18:31] Starting 'default'...[14:18:31] Finished 'default' after 79 μs
下面我們就來配置 gulp 來完成我們的自動化任務,這里咱們先以編譯 Sass 文件作為示例。
首先得引入 Sass 的處理包,注意這里不再是 sass ,而是 gulp 的 Sass 插件,可以在 npm 上面找到(點擊這里)然后我們可以看看它的大致用法。
首先是引入:
var sass = require('gulp-sass');
然后是創建一個任務:
gulp.task('styles',function(){
gulp.src('./src/sass/*.scss')
.pipe(sass())
.pipe(gulp.dest('./dist/css'))})
這里我們通過 src 指定 *.scss 源文件的位置,然后傳入 pipe(自動化流程中的處理管道)最后通過 dest 指定輸出路徑。整個過程其實是很好理解的。
完了你的 gulpfile.js 文件就會是這個樣子:
var gulp = require('gulp'),
sass = require('gulp-sass');gulp.task('styles',function(){
gulp.src('./src/sass/*.scss')
.pipe(sass())
.pipe(gulp.dest('./dist/css'))})
接下來同樣需要安裝 gulp-sass
npm install -g gulp-sass
最后,我們執行
gulp
可以看到,Sass 文件已經編譯成功了。
對于 coffee 和 jade 文件,我們同樣創建各自的任務和處理管道,這里就不重復說了,可以直接看代碼。
這里還有一個 clean 任務,用于每次新編譯的時候把老文件清空掉
gulp.task('clean',function(cb){
del(['./dist'],cb)})
因為執行 gulp 命令默認是取的 default 任務,所以我們需要創建該任務,并把其它任務都給加進去
gulp.task('default',['clean'],function(){
gulp.start('styles','scripts','templates');})
可以看到這里是保證在 clean 任執行完成后再去執行我們的 'styles','scripts','templates'任務。
在實際的開發過程中,不管我們修改什么文件,只需要 gulp 一下即可,方便了很多。但是仍然每次修改都得執行一下。可不可以讓 gulp 來監聽我們的文件變化,然后自動重新執行編譯任務呢?
答案是可以的。只需要將這些任務加入監控 watch 就行了:
gulp.task('watch',function(){
gulp.watch('./src/sass/*.scss',['styles']);
gulp.watch('./src/coffee/*.coffee',['scripts']);
gulp.watch('./views/*.jade',['templates']);})
然后執行
gulp watch
這樣,gulp 就開始監聽文件了,不管我們做了什么修改,都不用再次執行 gulp 了。
源碼 中我用到了更多的包(壓縮、合并、重命名等等)大家可以了解一下,不懂的可以去 npm 上查一下用法文檔。
10、開發環境
如果要是在別人的環境中運行該項目,那么我們之前安裝的包都得重新安裝一遍,是不是很麻煩。別怕,我們有 package.json,用來管理我們這些包的依賴,換了環境只需要執行一下 npm install 即可將我們之前的包都給安裝上了,相當省事。
需要手動創建 package.json 嗎?NO,只需執行
npm init
接下來我們就可以按照提示創建自己的 package.json 了。 里面包含了該項目的一些信息,咱們找到 dependencies 這一行,這便是該項目要用到的包。正常情況下應該是這種格式:
"dependencies": {
"gulp": "^3.9.0",
"gulp-sass": "~2.0.4",
....}
但是很遺憾,里面什么都沒有,這意味著我們必須一個個手動往里面添加我們上面用到的包了。
心好累...
難道就不能自動給我們加上去嗎?不能,不過我們如果在開始安裝每個包的時候在命令結尾加上 -save 參數,那么后面這些依賴便可以自動加到 dependencies 下了,如:
npm install coffee-script -save
所以下次開發的時候,別忘了在安裝包的時候加上這個參數。
11、版本控制
現在項目已經完成了,我們應該將它納入版本控制了。什么叫版本控制呢?通俗點說就是充分管理你的代碼日志,你對代碼做的任何增刪改,只要你提交過,就會記錄下來,而且可以隨時回退到之前的任何狀態,從而保證整個開發過程不會斷層。
目前我們基本上都會使用Git來完成版本控制,Git只是一種協議,而具體則有Github、Bitbucket、Gitlab之類的實現。而 Github 則是開源項目的不二選擇。
1、首先我們在項目根目錄下執行
git init
這里是初始化,會創建一個 .git 文件夾,里面包含了所有跟版本控制相關的數據。所以,如果你想把該項目和Git斷開,直接刪除該文件夾即可。
2、添加一個 README.md (可選)用于描述項目
touch README.md
注意這里是 Markdown 格式的。
3、忽略某些文件
在提交代碼之前,我們需要將某些文件排除在外,比如由 npm 產生的 node_modules 文件夾,這是開發環境下用到的,可以通過 npm install 在新環境下安裝依賴。所以理應不納入版本管理,還有緩存之類的都應該排除掉。
那么我們就創建一個名為 .gitignore 的文件,內容就如下:
.DS_Store
node_modules
.sass-cache
上面只是一個大致的內容,實際開發的時候你得根據自己的需求去忽略某些文件。
4、提交代碼到本地
git add .git commit -m "init project"
注意,這里僅僅是將代碼提交到了本地。
5、提交到 Github
首先我們在Github上創建一個項目 auto-web

Create repository 到下一個頁面上,復制那句
git remote add origin git@github.com:awesomes-cn/auto-web.git
(這里是我的,你得復制你自己的) 然后回到項目根目錄執行該命令。
最后執行提交
git push -u origin master
去你的 Github 上,可以看到該項目的所有文件已經傳上去了,而那些忽略掉的文件則沒有上去。
感興趣的同學可以 cat .git/config 一下:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true[remote "origin"]
url = git@github.com:awesomes-cn/auto-web.git
fetch = +refs/heads/*:refs/remotes/origin/*[branch "master"]
remote = origin
merge = refs/heads/master
這就是本地與遠程連接的一個配置,由 remote 和 branch 組成,即通過【遠端源+分支】來精確定位到最終提交的地址。如果我們直接修改這個文件,也會和用命令一樣達到相同的配置效果。
起初是沒有這些內容的,當我們執行 git remote add origin ... 時則添加了
[remote "origin"]
url = git@github.com:awesomes-cn/auto-web.git
fetch = +refs/heads/*:refs/remotes/origin/*
而 git push -u origin master 則指定了
[branch "master"]
remote = origin
merge = refs/heads/master
所以如果只有 master 分支,后面的提交就直接可以 git push 了。
如果這是你在公司做的,回到家了想繼續開發,只需
git clone git@github.com:awesomes-cn/auto-web.git
然后
cd auto-web
npm installgulp watch
開發環境就緒,繼續吧....
免費個人主頁
如果你想把該項目作為一個個人主頁項目,也就是直接瀏覽靜態頁面。Github 給每個用戶提供了一個特殊的庫,那就是 <github用戶名>.github.io ,因為這個項目是可以直接在瀏覽器中瀏覽的。一般的前端庫都會將文檔頁面或者演示頁面放到該主頁上,這樣就沒必要自己搭服務器來做了。
那么很簡單,只需要把該項目的名字改成 <github用戶名>.github.io 比如我的是 awesomes-cn.github.io。
然后就可以在瀏覽器中通過 http://<github用戶名>.github.io 訪問了。
如此,一個個人主頁就誕生了,可以用來做個人簡歷和博客,完全不用服務器和數據庫參與,而且不需要花一分錢的費用。
如果網站中有大量的資源圖片,要是放在 Github 上勢必會影響訪問速度。所以可以將圖片放到云存儲上面,比如七牛云存儲,個人最多有15G的免費流量,完全夠用了。
總結
上面的每個部分,我講得都不是很深入,如果按照我所說的,整個流程都走通了,那么你現在可以對自己感興趣的部分去做深入了解和學習了。
當前的前端發展是很快的,但某些思想卻是不變的,我們必須學會以不變應萬變。打好基礎,將來不管什么新的框架或者工具都可以快速上手。
附 Github 源碼: https://github.com/awesomes-cn/auto-web
原文:http://www.awesomes.cn/source/9
- PHP技術文章
- PHP中session和cookie的區別
- php設計模式(一):簡介及創建型模式
- php設計模式結構型模式
- Php設計模式(三):行為型模式
- 十款最出色的 PHP 安全開發庫中文詳細介紹
- 12個提問頻率最高的PHP面試題
- PHP 語言需要避免的 10 大誤區
- PHP 死鎖問題分析
- 致PHP路上的“年輕人”
- PHP網站常見安全漏洞,及相應防范措施總結
- 各開源框架使用與設計總結(一)
- 數據庫的本質、概念及其應用實踐(二)
- PHP導出MySQL數據到Excel文件(fputcsv)
- PHP中14種排序算法評測
- 深入理解PHP原理之--echo的實現
- PHP性能分析相關的函數
- PHP 性能分析10則
- 10 位頂級 PHP 大師的開發原則
- 30條爆笑的程序員梗 PHP是最好的語言
- PHP底層的運行機制與原理
- PHP 性能分析與實驗——性能的宏觀分析
- PHP7 性能翻倍關鍵大揭露
- 鳥哥:寫在PHP7發布之際一些話
- PHP與MySQL通訊那點事
- Php session內部執行流程的再次剖析
- 關于 PHP 中的 Class 的幾點個人看法
- PHP Socket 編程過程詳解
- PHP過往及現在及變革
- PHP吉祥物大象的由來
- PHP生成靜態頁面的方法
- 吊炸天的 PHP 7 ,你值得擁有!
- PHP開發中文件操作疑難問答
- MongoDB PHP Driver的連接處理解析
- PHP 雜談《重構-改善既有代碼的設計》之二 對象
- 在php中判斷一個請求是ajax請求還是普通請求的方法
- 使用HAProxy、PHP、Redis和MySQL支撐10億請求每周架構細節
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重構-改善既有代碼的設計
- PHP場景中getshell防御思路分享
- 移動互聯時代,你看看除了PHP你還會些什么
- 安卓系統上搭建本地php服務器環境
- PHP中常見的緩存技術!
- PHP里10個鮮為人知但卻非常有用的函數
- 成為一名PHP專家其實并不難
- PHP 命令行?是的,您可以!
- PHP開發提高效率技巧
- PHP八大安全函數解析
- PHP實現四種基本排序算法
- PHP開發中的中文編碼問題
- php.get.post
- php發送get、post請求的6種方法簡明總結
- 中高級PHP開發者應該掌握哪些技術?
- 前端開發
- web前端知識體系大全
- 前端工程與性能優化(下)
- 前端工程與性能優化(上)
- 2016 年技術發展方向
- Web應用檢查清單
- 如何成為一名優秀的web前端工程師
- 前端組件化開發實踐
- 移動端H5頁面高清多屏適配方案
- 2015前端框架何去何從
- 從前端看“百度遷徙”的技術實現(一)
- 從前端看“百度遷徙”的技術實現(二)
- 前端路上的旅行
- 大公司里怎樣開發和部署前端代碼?
- 5個經典的前端面試問題
- 前端工程師新手必讀
- 手機淘寶前端的圖片相關工作流程梳理
- 一個自動化的前端項目實現(附源碼)
- 前端代碼異常日志收集與監控
- 15年雙11手淘前端技術總結 - H5性能最佳實踐
- 深入理解javascript原型和閉包系列
- 一切都是對象
- 函數和對象的關系
- prototype原型
- 隱式原型
- instanceof
- 繼承
- 原型的靈活性
- 簡述【執行上下文】上
- 簡述【執行上下文】下
- this
- 執行上下文棧
- 簡介【作用域】
- 【作用域】和【上下文環境】
- 從【自由變量】到【作用域鏈】
- 閉包
- 完結
- 補充:上下文環境和作用域的關系
- Linux私房菜