若要得到更好的閱讀體驗請移駕http://willkan.github.io/blog/html/Workflow/javascript
接觸前端一年了,瞎摸過來這麼久一直很但願看到一個完整的前端構建流程,遺憾沒找到相關文章,因此本身在作這個網頁的時候順便紀錄下來本身從架構到部署的整個過程。php
我一直但願有個網站可讓我用markdown寫文章,而且能夠在markdown中插入本身的demo(不是源碼),因而就作了這麼個項目讓我能夠輕鬆發佈本身的文章。(文中並不會提到如何解析markdown,解析markdown能夠了解下grunt-markdown
)css
網上前端資源很是豐富,作一個網頁徹底不必重複造輪子,下面是我作這個網頁用到的工具及其分析。html
響應式css框架:Foundation 4 不少人聽到響應式會第一時間想到Bootstrap,但在網上查詢後我發現不少人再說Bootstrap在移動端的性能並很差,甚至會拖慢應用,很大一部分緣由是由於Bootstrap只採用jQuery這麼個在移動端上並不太適合的庫。而Foundation4在移動端採用Zepto,而且採用mobile first概念(就是說首先爲小屏幕設計,隨着屏幕逐漸增大,層次越複雜,能夠和"漸進加強"類比)。 Bootstrap和Foundation的詳細對比你們能夠參見twitter-bootstrap-vs-foundation-4-which-one-is-right-for-you(注意,這裏比較的Bootstrap版本還比較老) 感受Foundation 4 在國內使用羣還比較少,因此教程仍是參見官網吧。 可是有一點比較奇怪的是Foundation 4的官方Docs在個人移動設備上仍會卡頓。是html文件太大?仍是Foundation 4的css太臃腫? 在以後真正進入開發時我會做出對比。前端
DOM操做庫:Zepto 爲何選擇Zepto?由於Zepto性能比其餘DOM操做庫在webkit上表現好太多了。而webkit又是IOS和Android上主力的瀏覽器核心。部分dom操做性能對比請查看http://jsperf.com/jqm3/103java
模板解釋引擎:HandleBars 其餘模版引擎也能夠,只是這裏我比較喜歡Handlebars而已。模版解釋的好處在於再也不須要再寫大量而不清晰的js插入dom結構語句。此外,我將回用Handlebars往markdown解析後的文件中插入demo。node
前端模塊化:seajs 要麼requirejs,要麼seajs,爲啥選seajs,請查看seajs處的解釋 架構工具:yeomanlinux
提升開發效率和規範 移動端動畫:採用css3動畫實現頁面切換而不是傳統的setTimeOut調整dom的樣式,css3
css預編譯:Sass 由於用到了foundation 4,而foundaton 4 官方版本又擁有Sass版本,因此用上了Sassgit
測試:CasperJS 感受上mocha,jasmine更偏向於本地純js調試,而CasperJS這基於PhantomJS的工具則更適合瀏覽器調試
工具選好了以後,咱們開始構建項目了。首先介紹一下yeoman,借用一下infoq上的介紹:
Yeoman是由Paul Irish、Addy Osmani、Sindre Sorhus、Mickael Daniel、Eric Bidelman和Yeoman社區共同開發的一個項目。它旨在爲開發者提供一系列健壯的工具、程序庫和工做流,幫助他們快速構建出漂亮、引人注目的Web應用。Yeoman擁有以下特性:
- 快速建立骨架應用程序——使用可自定義的模板(例如:HTML五、Boilerplate、Twitter Bootstrap等)、AMD(經過RequireJS)以及其餘工具輕鬆地建立新項目的骨架。
- 自動編譯CoffeeScrip和Compass——在作出變動的時候,Yeoman的LiveReload監視進程會自動編譯源文件,並刷新瀏覽器,而不須要你手動執行。
- 自動完善你的腳本——全部腳本都會自動針對jshint(軟件開發中的靜態代碼分析工具,用於檢查JavaScript源代碼是否符合編碼規範)運行,從而確保它們遵循語言的最佳實踐。
- 內建的預覽服務器——你不須要啓動本身的HTTP服務器。內建的服務器用一條命令就能夠啓動。
- 很是棒的圖像優化——Yeoman使用OptPNG和JPEGTran對全部圖像作了優化,從而你的用戶能夠花費更少時間下載資源,有更多時間來使用你的應用程序。
- 生成AppCache清單——Yeoman會爲你生成應用程序緩存的清單,你只須要構建項目就好。
- 「殺手級」的構建過程——你所作的工做不只被精簡到最少,讓你更加專一,並且Yeoman還會優化全部圖像文件和HTML文件、編譯你的CoffeeScript和Compass文件、生成應用程序的緩存清單,若是你使用AMD,那麼它還會經過r.js來傳遞這些模塊。這會爲你節省大量工做。
- 集成的包管理——Yeoman讓你能夠經過命令行(例如,yeoman搜索查詢)輕鬆地查找新的包,安裝並保持更新,而不須要你打開瀏覽器。
- 對ES6模塊語法的支持——你可使用最新的ECMAScript 6模塊語法來編寫模塊。這仍是一種實驗性的特性,它會被轉換成eS5,從而你能夠在全部流行的瀏覽器中使用編寫的代碼。
- PhantomJS單元測試——你能夠經過PhantomJS輕鬆地運行單元測試。當你建立新的應用程序的時候,它還會爲你自動建立測試內容的骨架。
yeoman是基於node.js,首先得安裝node.js,而後在命令行中安裝yeoman
npm install -g yo grunt-cli bower
mac或linux下npm安裝到全局須要root權限
yo用於構建項目的手足架。
剛安裝完的yeoman(1.0版本)中是不含模版的,我先去找了個模版generator-backbone
npm install -g generator-backbone
而後使用該模版開始構建
mkdir myapps cd myapps yo bakcbone myapps
命令行提示是否使用twitter和coffeejs,該項目中都用不到,因此都不選
此時myapps的目錄結構以下
但該架構並不徹底知足個人需求,因而我進行了手動修改,修改後結構以下
yo的使用基本結束。
以後本文提到的路徑都是myapps
的相對路徑
bower用於載入網頁須要的組件
因爲目錄變動,咱們須要修改bower的路徑,bower的路徑在.bowerrrc
文件中,將其內容改成
{ "directory": "app/public/bower_components" }
bower.json
文件是用來描述須要載入的組件
grunt用於自動化,例如編譯scss文件,編譯coffe文件,部署等。
由於myapps的目錄修改過,因此Gruntfile.js
中的相關路徑也得修改。
後面的開發中會大量用到grunt,此處先提一下grunt的一些我的經驗
yeoman預置的Gruntfile.js很聰明,已設置爲從package.json
讀取grunt插件並註冊,只需修改此文件並執行 npm install
,便可註冊grunt的插件。
在grunt.initConfig()
中咱們能夠配置各類子任務,例如
grunt.initConfig({ copy: { dist: { files: [{ expand: true, dot: true, cwd: '<%= yeoman.app %>', dest: '<%= yeoman.dist %>', src: [ '*.{ico,txt}', '.htaccess', 'images/{,*/}*.{webp,gif}' ] }] } } })
而後經過grunt.registerTask()
來註冊任務,例如
grunt.registerTask('default', [ 'copy:dist', //執行copy下的dist子任務, 'copy' //執行copy下全部任務,執行順序爲定義時的順序 ])
Foundation 4是一個CSS框架,有CSS版本和SCSSS版本,均可以在官網得到。
直接上個人html代碼先
<div class="row"> <div class="small-12 large-9 columns"> <h1> 前端工做流程 </h1> <hr/> <article></article> </div> <button class="hide-for-medium hide-for-large nav-btn"></button> <aside class="hide-for-small small-6 large-3 columns" data-magellan-expedition="fixed"></aside> </div>
這裏解釋一下Foundation的柵欄表格。
columns
表示行,row
表示列.columns
元素必須得是.row
的後繼元素
默認版本的Foundation中,把行分紅12份,使用以下:
small-1
表示該列寬度佔最近.row
祖先元素的1/12small-3
表示該列寬度佔最近.row
祖先元素的3/12small-3 large-4
表示該列寬度在小屏幕中佔3/12,在大屏幕中佔4/12small
,large
表示屏幕大小,可在_global.scss
中找到定義,能夠在該文件中搜索關鍵字$small-screen
Foundation的css使用就解釋到這,更多請查看API
而後來看看Foundation的js插件部分,foundation依賴於zepto或jQuery,引入其餘插件前須要先引入foundation.js
若使用zepto請注意zepto是可定製的,foundation使用的是他本身定製的zepto,因此請使用foundation/js/vendor/zepto.js
鑑於SCSS的方便,我採用了SCSS版本。這就帶來了個問題,每次都要編譯才能被瀏覽器解釋。grunt這時候就起做用了。
首先說一下我SCSS的配置
app/public/bower_components/foundation
app/resource/scss
中app/public/style/scss
中app/public/style/main.css
經過@import
引入,html中只導入該css而後就是grunt的配置了
gem install compass
package.json
中已含有grunt-contrib-compass
插件,該插件能夠對SCSS進行編譯而後能夠開始配置Gruntfile.js
:
... grunt.initConfig({ ... //實時監控 watch: { ... compass: { files: ['<%= yeoman.app %>/resource/scss/{,*/}*.{scss,sass}'],//監控的文件路徑 tasks: ['compass']//若監控文件改動,執行的任務 }, ... }, //compass配置 comapss: { //全局配置 options: { sassDir: '<%= yeoman.app %>/resource/scss', cssDir: 'app/public/styles/scss', imagesDir: '<%= yeoman.app %>/public/images', javascriptsDir: '<%= yeoman.app %>/public//scripts', fontsDir: '<%= yeoman.app %>/public/styles/fonts', //此處配置SCSS的引入源路徑,我添加了foundation的SCSS路徑和animate的SCSS路徑 importPath: [ '<%= yeoman.app %>/public/bower_components', '<%= yeoman.app %>/public/bower_components/foundation/scss', '<%= yeoman.app %>/public/bower_components/animate.scss/source' ], relativeAssets: true }, //局部配置,覆蓋同名項 dist: {}, server: { options: { debugInfo: true } } }, ... }); //往server任務中插入compass子任務,執行該任務命令爲grunt server grunt.registerTask('server', [ ...//其餘任務, 'compass:server', ...//其餘任務 ]) ...
在命令行中執行grunt server,便可實現SCSS的實施編譯
在HTML和CSS框架部分咱們已經完成了HTML框架的構建。
下面咱們開始使用HTML模版來編寫網頁須要模版。
HTML模版引擎的好處這裏就不說了,在很是多的模版引擎中我選擇了Handlebars,僅僅是由於我喜歡這個模版。
模版的編寫就不說了,這部分主要仍是描述如何模版文件的放置結構和實現模版的預編譯。
首先來看看目錄結構
app/resource/templates
中, 後綴名是.hbs
app/resource/templates/helpers
中.hbs
這些文件編譯生成的文件我放置在.tmp/templates
這臨時文件夾中.tmp/templates
中的文件和helpers鏈接在一塊兒,生成.tmp/template.js
下面來看看預編譯
首先,爲何要預編譯?
模版文件並不能直接被js執行,須要經Handlebars解釋成一個函數後才能被執行,也就是說瀏覽器得先執行模版解釋,這對性能並很差的移動設備來講無疑帶來了性能損耗。因此更好的作法是在服務器段就把這些模版文件預編譯成可執行的函數。
那該怎麼實現預編譯呢?
grunt這時候又來了。
預編譯Handlebars須要grunt-contrib-handlebars
這個插件,載入方式仍是在package.json
中添加該項,而後npm install
那接下來看看這插件的配置(grunt.initConfig()中的配置
)
handlebars: { compile: { options: { //函數所在命名空間 namespace: "Handlebars.templates", //函數名稱格式 processName: function (filename) { return filename .replace(/app\/resource\/templates\//, '') .replace(/\.hbs$/, ''); } }, files: { //'生成文件':[須要編譯的模版文件] '.tmp/templates/template.js': ['<%= yeoman.app %>/resource/templates/*.hbs'] } } }
上述代碼實現下列轉換(文件所在路徑都是app/resource/templates
)
把helpers也合併進來
這裏用到grunt-contrib-concat
,注意文件鏈接順序,應該是先鏈接helpers類文件,應爲模版中可能會用到自定義的helper,只有先定義了helper,後面的模版函數才能正常執行
HTML引人預編譯的文件
預編譯帶來的好處還有一個:咱們再也不須要引入完整的handlebars.js和.hbs
這類模版文件,只須要引入handlebars.runtime.js(和handlebars.js相比,沒有模版解釋函數)和預編譯生成的文件.tmp/template.js
實時編譯監控
咱們能夠添加監控的文件和任務以實現.hbs
這類文件的實時編譯,這樣咱們只要直接修改模版文件,並不會由於預編譯而給調試修改帶來麻煩。
接下來讓咱們看看該如何組織js文件。
之前咱們引入js文件的方式是:
<script src=""></script>
或者是用jQuery$.ajax()
但這些引入方式有一個致命缺陷就是文件依賴問題,咱們更但願看到的是像java同樣能夠import
,因而一部分前端的前輩們就致力於解決模塊化問題。
seajs(CMD規範)和requirejs(AMD規範)就是解決模塊化問題比較好的兩個庫。
兩者的詳細對比我就不說了,你們能夠看看SeaJS與RequireJS最大的區別。
雖然模塊化的確頗有用,但無能否認目前大多數的庫仍是傳統的寫法(外國也有一部分庫已支持AMD),也就是說須要這些s庫進行人工CMD化或AMD化。國際上的一些比較著名的庫我還沒找到原生支持CMD,都須要人工CMD化。
而我此次項目選擇的是seajs,緣由是兩者在構建上都沒有成熟的方案,seajs的生態圈更本地化(做者是玉伯大大),相比一堆英文的交流我更願意用中文交流,也算是爲國內前端生態圈盡一份力吧。
我在這講的主要是本身在使用seajs過程當中遇到的問題和解決方案,想查看API者請移駕https://github.com/seajs/seajs/issues/266,若是玉伯大大看到這篇文章,容小弟稍微抱怨下用github做參考文檔真心不太容易用。。。資料找起來仍是挺費勁的。
先規範一下scripts目錄結構
如下描述的相對路徑是app/public/scripts
sea-modules
放置的是經過spm(下面會提到)加載的庫example
example/logic
放置的是本身編寫的邏輯模塊example/utils
放置的是CMD化後的開源js庫example/templates
放置的是CMD化後的模版文件(例如前文提到的app目錄下的.tmp/templates.js
,.tmp下的文件還未CMD化)example/static
放置的是已經CMD化的靜態資源文件注意seajs的路徑解釋規則,請查看模塊標誌
這裏注意paths
中定義的路徑就是其直接路徑,並不會互相解釋,例如
seajs.config({ //base默認爲'app/public/scripts/sea-modules' paths:{ scripts: '/public/scripts', logic: 'scripts/logic' //此處並不會被解釋爲'/public/scripts/logic',而是'app/scripts/sea-modules/logic' } });
此外paths的配置還應注意不該以'/'結尾(僅限2.1.1版本),詳情請見https://github.com/seajs/seajs/issues/926
這個問題是由於使用seajs還應注意一個初學者不太能發現的ID 和路徑匹配原則
seajs2.0開始推薦將全部js封裝成CMD模塊.
對於非CMD的js咱們須要對其進行封裝,我說一下封裝foundation.js這個文件(依賴dom選擇器)
define(function(require, exports, module){ var $ = require('$');//寫入依賴,須要再seajs配置中配置alias的$ ...//源代碼 return Foundation; })
而對於相似於模板預編譯成的文件,我建議使用grunt對其進行封裝,grunt-contrib-concat
支持對文件進行包裝,這裏不做描述
通過上面的步驟,網站的組織已經比較清晰,再編輯完網頁全部功能後,進入下一步----部署.
yeoman配置的Gruntfile.js
並不能適應全部自動化,咱們須要對其進行適當修改.
先來看看個人grunt build
執行了哪些任務
grunt.registerTask('build', [ 'clean:dist',//清空臨時目錄 'compass:dist',//編譯SCSS 'markdown',//將markdown轉換爲handlebars模板格式 'handlebars:markdown',//預編譯handlebars 'handlebars:compile',//預編譯handlebars 'concat:template',//鏈接handlebars預編譯後的文件以及助手 'wrap:template',//對鏈接後的文件進行CMD封裝 'transport:seajs',//獲取CMD模塊ID 'concat:seajs',//鏈接CMD模塊 'uglify:seajs',//壓縮CMD模塊鏈接而成的文件 'useminPrepare', 'imagemin',//壓縮圖像 'htmlmin',//壓縮html文件 'cssmin',//壓縮css 'copy',//將沒處理過而網站又須要的文件拷貝過來 'rev',//爲css和js添加版本號 'usemin' ]);
usemin
的做用請看官方說明
Replaces references to non-optimized scripts or stylesheets into a set of HTML files (or any templates/views).
屌絲翻譯:替換HTML文件(或任意模板文件)的引用
舉個例子,main.css
經處理後變成main-rev.css
,usemin就是把html中引用到main.css
的地方都改成main-rev.css
這裏我只講述文檔比較少的transport和concat,這兩個任務分別用到了grunt-cmd-transport
和grunt-cmd-concat
對於爲分配ID的CMD模塊,咱們首先使用grunt-cmd-transport
抽取其ID,先上代碼
transport: { seajs: { options: { alias: { underscore: 'underscore', backbone: 'backbone', $: '$', modernizr: 'modernizr' }, paths: [ 'app/public/scripts/example/static', 'app/public/scripts/sea-modules', '.build' ] }, files: [ { cwd: 'app/public/scripts/example', src: [ 'static/{,*/,*/*/}*.js', 'templates/*.js', 'utils/*.js', 'logic/*.js' ], dest: '.build' } ] } }
解釋一下options
alias
能夠指向一個文件也能夠指向自定義的別名,alias中的鍵值對(假設爲key:value
)的表示須要處理的文件中若含有require('key')
則用require('value')
來代替
paths
是一個數組,表示須要用到的模塊路徑,例如處理的文件中須要require('logic')
,而logic模塊並不在默認路徑中,則須要在paths
中添加logic模塊所在的路徑(注意,此處應把file.dest
指向的路徑也添加進去)
idleading
咱們能夠用這屬性給全部抽取的ID添加一個前綴
而後是抽取
file.src
這數組的順序相同file.dest
指向的路徑下模塊ID的命名方式
ID名字 = options.idleading + 文件相對CWD路徑
例如
假設無idleading,cwd爲'app/build/scripts',dest爲'.build',文件相對路徑爲'logic/a'則ID名字是'logic/a',抽取出來的目錄結構是
.build |--logic |--a.js
模塊抽取完後就要開始進行鏈接.仍是先上代碼
concat: { seajs: { options: { relative: true, include: 'all', paths: [ 'app/public/scripts/sea-modules', 'app/public/scripts/example/static', '.build' ] }, files: { 'dist/public/scripts/main.js': ['.build/{,*/,*/*/}*.js'] } } }
options的paths
解釋和transport的options.paths
同樣
這是我本身應用的一個策略,也就是儘可能保證一步部署,再也不對部署後的文件上進行手工修改
usemin並不會對seajs的引用進行修改,爲了實現部署優先,咱們在index.html應採用下面方式
<!-- 引入seajs --> <script src="public/scripts/sea-modules/seajs/seajs/2.1.1/sea.js"></script> <!-- 引入seajs配置文件 --> <script src="public/scripts/config.js"></script> <!-- 引入seajs合併後的文件, 路徑爲合併後的文件相對路徑 --> <script src="public/scripts/main.js"></script> <!-- 調用啓動模塊 --> <script> seajs.use(['modernizr','logic/load-markdown']); </script>
解釋一下引入合併後的文件的緣由:
解釋一下部署後的調用啓動模塊
當引入合併後的文件時,會對一系列CMD模塊進行define,define過程應該是這樣的:
若define()的參數中含有ID,則先對ID進行路徑解釋,把解釋後的路徑添加到seajs.cache
中
seajs.cache
中是否含有解釋後的路徑,如有,則直接調用該路徑對應的模塊,若無則想服務器獲取該模塊seajs.use
和require
時得到相同的解釋路徑我選擇的測試工具是CasperJS.
基於PhantomJS, 也就是說CasperJS能夠執行一系列瀏覽器上的動做, 應該就是所謂的UI測試吧,而這正是web最須要保證質量的事.
建議使用CasperJS 1.1版本(目前最新版本)
使用請參看API
對於剛邁入大四的我來講,也就是一兩個月前接觸過測試這一律念,而ui測試通俗點說就是: 點擊了按鈕a, 頁面發生了響應. 而測試就是判斷這響應是否符合測試設定的標準, 不管符不符合, 都返回測試結果.
原本想描述本身的建網頁流程,感受寫着寫着有點變成流水帳了…還丟了一坨代碼上來…